Files
JJBB/Assets/Project/Script/Player/Controller/ProcessInput.cs
2024-08-23 15:49:34 +08:00

477 lines
18 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
/// 当前主要输入设备
/// </summary>
public ControlInputType InputType { get; private set; }
/// <summary>
/// 当前帧输入状态
/// </summary>
public ControlFrameType FrameType { get; private set; }
/// <summary>
/// 希望主角移动的位置
/// </summary>
public Vector3 MovePoint { get; private set; }
/// <summary>
/// 点击地面移动是否有更新
/// </summary>
public bool UpdatePos { get; private set; }
private void Awake()
{
Instance = this;
inputModule = GetComponent<ProcessInputModule>();
_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<JoyStickLogic>();
}
}
}
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;
}
/// <summary>
/// 清空当前输入状态
/// </summary>
// 注如果持续使用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<ObjManager>.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<ObjManager>.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<CollectItem>.GetInstance().RemoveItem(hit.collider.gameObject);
result = true;
}
//判断是不是掉落
var dropItem = hit.collider.gameObject.GetComponentInParent<Obj_DropItem>();
if (dropItem != null)
{
dropItem.SendDropItem();
return true;
}
// Obj点击操作单击点选目标并移动过去
var npcScript = hit.collider.gameObject.GetComponentInParent<Obj_Character>();
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<CollectItem>.GetInstance().RemoveItem(hit.collider.gameObject);
}
}
}
}
/// <summary>
/// 获得当前记录的摇杆输入帧。如果小于ProcessInput更新时机会是上一帧状态。
/// </summary>
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);
}
}
/// <summary>
/// 检查一个RectTransform的中心位置是否可以接受点击事件这个检测不受同一Canvas以下的物体影响
/// </summary>
private static readonly Vector3[] _fourVector3Buffer = new Vector3[4];
// 注List实际没法优化到Buffer这里至少可以减少List构造
private static readonly List<RaycastResult> _raycastResultListBuffer = new List<RaycastResult>(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, // 摇杆或者轴向结束,需要主角停止当前移动
}