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, // 摇杆或者轴向结束,需要主角停止当前移动
}