using System.Collections; using System.Collections.Generic; using Games.Events; using Games.LogicObj; using Games.Scene; using GCGame.Table; using Module.Log; using UnityEngine; using UnityEngine.EventSystems; using EventSystem = UnityEngine.EventSystems.EventSystem; using Games.GlobeDefine; // 2018.03.02 - 这个类型功能改得很轻,仅仅记录玩家实际的输入状态,其他受限状态都应该由主角控制机自行判定 // 注:当前所有状态逻辑都是阻止主角运动,而不是玩家输入 // 键盘输入 > 摇杆输入 > 地面点击输入,允许同时输入,但是高优先输入覆盖低优先 public class ProcessInput : MonoBehaviour { // 摇杆预测移动距离 private const float _joyStickMoveDelta = 2f; private const float _processMoveInterval = 0.1f; private const float _maxSampleDistance = 5f; private const float _processMovePercision = 0.3f; public static ProcessInput Instance { get; private set; } public ProcessInputModule inputModule { get; private set; } private JoyStickLogic _joyStick; private bool _loadJoyStick; private RectTransform _rectTransform; // 特殊处理,一帧点击操作位置,可以临时覆盖_touchPointerPos private Vector2? _frameTouchPos; private Vector2? _touchPointerPos; private float _nextProcessTime; /// /// 当前主要输入设备 /// public ControlInputType InputType { get; private set; } /// /// 当前帧输入状态 /// public ControlFrameType FrameType { get; private set; } /// /// 希望主角移动的位置 /// public Vector3 MovePoint { get; private set; } /// /// 点击地面移动是否有更新 /// public bool UpdatePos { get; private set; } private void Awake() { Instance = this; inputModule = GetComponent(); _rectTransform = (RectTransform) transform; InputType = ControlInputType.None; } public void HideJoystic(bool state) { if (_joyStick == null) return; _joyStick.gameObject.SetActive(state); } public void CreateJoyStick() { _loadJoyStick = true; var uiData = UIInfo.JoyStickRoot; LoadAssetBundle.Instance.LoadUI(uiData.path, uiData.name, OnJoyStickLoaded, null); } public void DestroyJoyStick() { _loadJoyStick = false; if(_joyStick != null) Destroy(_joyStick.gameObject); } private void OnEnable() { GameManager.AddLateUpdate(ProcessMove, GameManager.joyStickUpdateOrder); } private void OnDisable() { GameManager.RemoveLateUpdate(ProcessMove, GameManager.joyStickUpdateOrder); } private void OnJoyStickLoaded(string modelName, GameObject resObj, Hashtable hashParam) { if (_loadJoyStick) { _loadJoyStick = false; if (resObj == null) LogModule.ErrorLog("摇杆无法加载!"); else { var joyStickObj = Instantiate(resObj); joyStickObj.transform.SetParent(transform, false); joyStickObj.transform.SetAsLastSibling(); _joyStick = joyStickObj.GetComponent(); } } } private void OnApplicationFocus(bool hasFocus) { // App失去焦点时,释放当前输入状态 if (!hasFocus) { ReleaseInput(); isBlocked = true; EventDispatcher.Instance.Dispatch(Games.Events.EventId.ProcessInputBlock, isBlocked); } } // 仅仅持续一帧的点击,不需要执行清空操作 public void StartOneFrameTouch(Vector2 touchPos) { if (!ClickObject(touchPos)) { if (PlayerPreferenceData.SystemUseScreenClick) _frameTouchPos = touchPos; } } // 仅仅按下,需要逻辑置空 public void StartTouchPos(Vector2 touchPos) { if (!ClickObject(touchPos)) { if (PlayerPreferenceData.SystemUseScreenClick && _touchPointerPos == null) _touchPointerPos = touchPos; } } public void MoveTouchPos(Vector2 touchPos) { // Touch如果已经取消,则必须重新启动才能赋值 if (_touchPointerPos != null) _touchPointerPos = touchPos; } public void EndTouchPos() { _touchPointerPos = null; _frameTouchPos = null; } /// /// 清空当前输入状态 /// // 注:如果持续使用AxisInput,会重新收到ControlStart的事件 public void ReleaseInput() { EndTouchPos(); if (_joyStick != null) _joyStick.ReleaseJoyStick(); // if (_joyStickPointerId != null) // { // _joyStickPointerId = null; // //JoyStickLogic.Instance().OnPointerUp(); // } } private void ProcessMove() { // 提前检测是否被遮挡,如果被遮挡,立刻停止操作 if ((_touchPointerPos != null || _joyStick != null && _joyStick.CurrentPointerId != null) && isBlocked) ReleaseInput(); if (_frameTouchPos == null) _frameTouchPos = _touchPointerPos; // 更新当前主要输入设备 var currentInput = ControlInputType.None; UpdatePos = false; // 主角不存在时,强制配置为无输入 var mainPlayer = Singleton.Instance.MainPlayer; if (mainPlayer != null && SceneLogic.CameraController != null && SceneLogic.CameraController.MainCamera != null) { var axisInput = GetAxisInput(); if (axisInput != Vector2.zero) currentInput = CommonUtility.Max(currentInput, ControlInputType.Axis); if (_joyStick != null && _joyStick.input != null) currentInput = CommonUtility.Max(currentInput, ControlInputType.JoyStick); if (_frameTouchPos != null) currentInput = CommonUtility.Max(currentInput, ControlInputType.Touch); if (currentInput != ControlInputType.None) { var targetPos = mainPlayer.Position; switch (currentInput) { case ControlInputType.Axis: UpdatePos = true; targetPos = targetPos + CovertInputToWorld(axisInput.normalized) * _joyStickMoveDelta; break; case ControlInputType.JoyStick: UpdatePos = true; // ReSharper disable once PossibleNullReferenceException System.Diagnostics.Debug.Assert(_joyStick.input != null, "_joyStick.input != null"); targetPos = targetPos + CovertInputToWorld(_joyStick.input.Value) * _joyStickMoveDelta; break; case ControlInputType.Touch: // 切换到Touch的第一帧 if (InputType != ControlInputType.Touch || Time.unscaledTime > _nextProcessTime) { _nextProcessTime = Time.unscaledTime + _processMoveInterval; UpdatePos = true; // ReSharper disable once PossibleInvalidOperationException var ray = SceneLogic.CameraController.MainCamera.ScreenPointToRay(_frameTouchPos.Value); targetPos = RayToColliderPoint(ray); } break; } if (UpdatePos) { UpdatePos = false; var movePoint = PointToNavPoint(targetPos); if (movePoint != null && (mainPlayer.Position - movePoint.Value).RemoveY().sqrMagnitude > _processMovePercision.ToSquare()) { UpdatePos = true; MovePoint = movePoint.Value; } } } // FrameTouchPos仅仅保留一帧 _frameTouchPos = null; } else ReleaseInput(); var isControlling = currentInput != ControlInputType.None; if (isControlling) { if (FrameType == ControlFrameType.ControlStart) FrameType = ControlFrameType.Controlling; else if (FrameType != ControlFrameType.Controlling) FrameType = ControlFrameType.ControlStart; } else { if (FrameType == ControlFrameType.JoyStickEnd || FrameType == ControlFrameType.TouchEnd) FrameType = ControlFrameType.None; else if (FrameType != ControlFrameType.None) FrameType = InputType == ControlInputType.Touch ? ControlFrameType.TouchEnd : ControlFrameType.JoyStickEnd; } InputType = currentInput; } // 注:点击逻辑暂时维持原状,不移交主角处理 private bool ClickObject(Vector2 point) { var result = false; if (GameManager.gameManager && GameManager.gameManager.PlayerDataPool.IsFollowTeam) GUIData.AddNotifyData(StrDictionary.GetClientDictionaryString("#{5128}")); else { // 注:允许移动中点击,但不允许移动中有其他拖拽 var mainPlayer = Singleton.GetInstance().MainPlayer; if (mainPlayer && !mainPlayer.IsDie()) { var mainCamera = SceneLogic.CameraController == null ? null : SceneLogic.CameraController.MainCamera; if (mainCamera != null) { var ray = mainCamera.ScreenPointToRay(point); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { // 客户端物品 点击拾取 if (hit.collider.gameObject.CompareTag("CollectItem")) { if (GameManager.gameManager.PlayerDataPool.m_objMountParam.AdvanceMountId > 0) //当前骑乘ID { var userPos = mainPlayer.Position; var TargetPos = hit.collider.gameObject.transform.position; TargetPos.y = userPos.y; var dis = Vector3.Distance(userPos, TargetPos); if (dis <= 3f) //距离足够才会下坐骑然后播放采集动作 { GameEvent gameEvent = new GameEvent(); gameEvent.Reset(); gameEvent.EventID = GameDefine_Globe.EVENT_DEFINE.EVENT_CLICKCOLLECTITEMUNMOUNTCALLBACK; gameEvent.AddFloatParam(point.x); gameEvent.AddFloatParam(point.y); mainPlayer.CurObjAnimState = GameDefine_Globe.OBJ_ANIMSTATE.STATE_NORMOR; mainPlayer.AskUnMount(); mainPlayer.UnMountEventCallBack = gameEvent; } return true; } Singleton.GetInstance().RemoveItem(hit.collider.gameObject); result = true; } //判断是不是掉落 var dropItem = hit.collider.gameObject.GetComponentInParent(); if (dropItem != null) { dropItem.SendDropItem(); return true; } // Obj点击操作,单击点选目标并移动过去 var npcScript = hit.collider.gameObject.GetComponentInParent(); if (npcScript != null) { var roleBaseTab = npcScript.BaseAttr.RoleData; if (roleBaseTab != null && roleBaseTab.DialogRadius >= 0) { mainPlayer.SetClientTargetByClick(npcScript); result = true; } } } } } } return result; } public void ClickCollectItemUnMountCallBack(float x, float y) { var mainCamera = SceneLogic.CameraController ? SceneLogic.CameraController.MainCamera : null; if (mainCamera) { var ray = mainCamera.ScreenPointToRay(new Vector2(x, y)); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { if (hit.collider.gameObject.CompareTag("CollectItem")) { Singleton.GetInstance().RemoveItem(hit.collider.gameObject); } } } } /// /// 获得当前记录的摇杆输入帧。如果小于ProcessInput更新时机,会是上一帧状态。 /// public Vector3 GetCurrentInputDirectionInWorld() { Vector2 input; if (InputType == ControlInputType.Axis) input = GetAxisInput(); //else if (InputType == ControlInputType.JoyStick) // input = JoyStickLogic.Instance().CurrentInput; else input = Vector2.zero; return CovertInputToWorld(input); } private Vector2 GetAxisInput() { var result = Vector2.zero; // 编辑器和PC端支持键盘和其他设备输入 #if UNITY_EDITOR || UNITY_STANDALONE result.x = Input.GetAxis("Horizontal"); result.y = Input.GetAxis("Vertical"); if (result.x > 0f) result.x = 1f; else if (result.x < 0f) result.x = -1f; if (result.y > 0f) result.y = 1f; else if (result.y < 0f) result.y = -1f; if (result != Vector2.zero) result = result.normalized; #endif return result; } public static Vector3? RayToNavPoint(Ray ray) { return PointToNavPoint(RayToColliderPoint(ray)); } private static Vector3? PointToNavPoint(Vector3 point) { Vector3? result = null; UnityEngine.AI.NavMeshHit navHit; if (UnityEngine.AI.NavMesh.SamplePosition(point, out navHit, _maxSampleDistance, UnityEngine.AI.NavMesh.AllAreas)) result = navHit.position; return result; } private static Vector3 RayToColliderPoint(Ray ray) { // 如果射线可以打到行走面上,则试图使用行走面作为目标位置 // 否则使用射线与主角高度水平面交点作为采样位置 RaycastHit hit; return Physics.Raycast(ray, out hit, float.PositiveInfinity, ActiveScene.terrainLayMask) ? hit.point : CommonUtility.RayToHorizontalPlane(ray, ObjManager.Instance.MainPlayer.Position.y); } public static Vector3 CovertInputToWorld(Vector2 inputDirection) { var result = Vector3.zero; var cameraTransform = SceneLogic.CameraController.MainCamera.transform; if (inputDirection != Vector2.zero) { var x = Vector3.Cross(Vector3.up, cameraTransform.forward); if (x != Vector3.zero) { var z = Vector3.Cross(x, Vector3.up); result = inputDirection.x * x.normalized + inputDirection.y * z.normalized; } } return result; } #region Raycast检测机制 public bool isBlocked { get; private set; } // LateUpdate之前进行检测,可能会比实际遮挡慢一帧,可以接受 private void Update() { var currentBlock = !CanRaycastToCenterPoint(_rectTransform); if (currentBlock != isBlocked) { isBlocked = currentBlock; EventDispatcher.Instance.Dispatch(Games.Events.EventId.ProcessInputBlock, isBlocked); } } /// /// 检查一个RectTransform的中心位置是否可以接受点击事件,这个检测不受同一Canvas以下的物体影响 /// private static readonly Vector3[] _fourVector3Buffer = new Vector3[4]; // 注:List实际没法优化到Buffer;这里至少可以减少List构造; private static readonly List _raycastResultListBuffer = new List(4); public bool CanRaycastToCenterPoint(RectTransform rectTrans) { if (!EventSystem.current || !EventSystem.current.gameObject.activeSelf) return false; rectTrans.GetWorldCorners(_fourVector3Buffer); var center = (_fourVector3Buffer[0] + _fourVector3Buffer[2]) * 0.5f; var pointerEvent = new PointerEventData(EventSystem.current) { position = RectTransformUtility.WorldToScreenPoint(UIManager.Instance().UICamera, center) }; EventSystem.current.RaycastAll(pointerEvent, _raycastResultListBuffer); var result = false; for (var i = 0; i < _raycastResultListBuffer.Count; i++) { if (_raycastResultListBuffer[i].gameObject == rectTrans.gameObject) { result = true; break; } else if (!_raycastResultListBuffer[i].gameObject.transform.IsChildOf(transform)) break; } return result; } #endregion } public enum ControlInputType { None, Touch, // 点击地面输入 JoyStick, // 摇杆输入 Axis, // 键盘或者其他轴向输入工具 } public enum ControlFrameType { None, // 无控制事件 ControlStart, // 玩家开始控制 Controlling, // 玩家控制中 TouchEnd, // 地面点击结束 JoyStickEnd, // 摇杆或者轴向结束,需要主角停止当前移动 }