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