using System.Collections.Generic; using Fantasy; using Fantasy.Entitas; using Fantasy.Entitas.Interface; using Fantasy.Helper; using UnityEngine; using UnityEngine.InputSystem; using Log = NBC.Log; namespace NBF.Fishing2 { [System.Serializable] public struct MoveState { public Vector3 startPosition; // 开始移动时的位置 public Vector3 moveDirection; // 移动方向(标准化向量) public float moveSpeed; // 移动速度 public bool isMoving; // 是否正在移动 public double serverTimestamp; // 服务器时间戳 } public class CharacterControllerComponent : Entity { public bool IsSelf; public bool Run; public CharacterController characterController; public PlayerModelAsset PlayerModelAsset; public readonly Queue MoveStateQueue = new Queue(); public Transform transform => characterController.transform; private MoveState currentMoveState; // private Vector3 networkFacingDirection; // 添加目标位置和旋转用于插值 private Vector3 targetPosition; private Quaternion targetRotation; // 插值平滑参数 public float positionLerpSpeed = 10f; public float rotationLerpSpeed = 10f; private float currentSpeed = 0f; private float acceleration = 10f; // 加速度 /// /// 重力加速度 /// private float gravity = 9.81f; private Vector3 currentVelocity = Vector3.zero; private float verticalVelocity = 0f; private float groundedGravity = -0.5f; // 小的向下力确保角色保持在地面 private float airControl = 0.5f; // 空中控制系数 #region System public class MoveComponentDestroySystem : DestroySystem { protected override void Destroy(CharacterControllerComponent self) { self.characterController = null; self.IsSelf = false; self.Run = false; var mapUnit = self.Parent as MapUnit; if (mapUnit.IsSelf()) { var inputComponent = self.Scene.GetComponent(); if (inputComponent != null) { inputComponent.OnPlayerPerformed -= self.OnPlayerCanceled; inputComponent.OnPlayerPerformed -= self.OnPlayerPerformed; inputComponent.OnPlayerValueCanceled -= self.OnPlayerValueCanceled; inputComponent.OnPlayerValuePerformed -= self.OnPlayerValuePerformed; } } self.PlayerModelAsset = null; } } public class MoveComponentAwakeSystem : AwakeSystem { protected override void Awake(CharacterControllerComponent self) { var unitUnityComponent = self.Parent.GetComponent(); self.PlayerModelAsset = unitUnityComponent.ModelAsset; self.characterController = unitUnityComponent.GameObject.GetComponent(); self.lastSyncedFacing = self.characterController.transform.forward; // 初始化目标位置和旋转 self.targetPosition = self.characterController.transform.position; self.targetRotation = self.characterController.transform.rotation; self.characterController.enabled = true; var mapUnit = self.Parent as MapUnit; if (mapUnit.IsSelf()) { self.IsSelf = true; var inputComponent = self.Scene.GetComponent(); inputComponent.OnPlayerPerformed += self.OnPlayerCanceled; inputComponent.OnPlayerPerformed += self.OnPlayerPerformed; inputComponent.OnPlayerValueCanceled += self.OnPlayerValueCanceled; inputComponent.OnPlayerValuePerformed += self.OnPlayerValuePerformed; } } } public class MoveComponentUpdateSystem : UpdateSystem { protected override void Update(CharacterControllerComponent self) { self.ProcessMoveStates(); self.CheckRotationSync(); // 插值更新位置和旋转 self.InterpolatePositionAndRotation(); var mapUnit = self.Parent as MapUnit; mapUnit.Position = self.characterController.transform.position; mapUnit.Rotation = self.characterController.transform.rotation; var f = mapUnit.Forward; } } #endregion #region Input private void OnPlayerPerformed(string action) { if (action == InputDef.Player.Run) { Run = true; } } private void OnPlayerCanceled(string action) { if (action == InputDef.Player.Run) { Run = false; } } private void OnPlayerValueCanceled(InputAction.CallbackContext context) { var name = context.action.name; if (name == InputDef.Player.Move) { var v2 = context.ReadValue(); SendMoveMessage(v2, true); } } private void OnPlayerValuePerformed(InputAction.CallbackContext context) { // var mapUnit = Parent as MapUnit; // Log.Info($"OnPlayerValuePerformed IsSelf={mapUnit.IsSelf()} id={mapUnit.Id}"); var name = context.action.name; if (name == InputDef.Player.Move) { var v2 = context.ReadValue(); SendMoveMessage(v2, false); } else if (name == InputDef.Player.Look) { var v2 = context.ReadValue(); UpdatePlayerRotation(v2); } } #endregion #region Message private void SendMoveMessage(Vector3 movementInput, bool isStop = false) { var mapUnit = Parent as MapUnit; Vector3 movementDirection = Vector3.zero; // 发送本地相对坐标而不是世界坐标 if (!isStop) { movementDirection = new Vector3(movementInput.x, 0, movementInput.y); } Net.Send(new C2Map_Move() { Direction = movementDirection.ToVector3Info(), IsStop = isStop, IsRun = Run, Timestamp = TimeHelper.Now, Position = mapUnit.Position.ToVector3Info(), Rotation = mapUnit.Forward.ToVector3Info(), }); } private void SendLookMessage() { Log.Info("发送朝向同步消息"); Net.Send(new C2Map_Look() { Rotation = characterController.transform.forward.ToVector3Info(), Timestamp = TimeHelper.Now }); } /// /// 接收服务器移动开始通知 /// /// /// /// /// public void OnServerStartMove(Vector3 startPosition, Vector3 moveDirection, float speed, double timestamp) { MoveState moveState = new MoveState { startPosition = startPosition, moveDirection = moveDirection.normalized, moveSpeed = speed, isMoving = true, serverTimestamp = timestamp }; MoveStateQueue.Enqueue(moveState); Log.Info($"MoveStateQueue Count={MoveStateQueue.Count}"); } /// /// 接收服务器移动停止通知 /// /// /// public void OnServerStopMove(Vector3 finalPosition, double timestamp) { MoveState moveState = new MoveState { startPosition = finalPosition, moveDirection = Vector3.zero, moveSpeed = 0f, isMoving = false, serverTimestamp = timestamp }; MoveStateQueue.Enqueue(moveState); Log.Info($"MoveStateQueue Count={MoveStateQueue.Count}"); } public void OnServerLook(Vector3 rotation, double timestamp) { // 设置目标旋转用于插值 targetRotation = Quaternion.LookRotation(rotation); } #endregion #region Move private void ProcessMoveStates() { // 只有在当前状态不是移动状态时,才处理下一个状态 if (MoveStateQueue.Count > 0) { currentMoveState = MoveStateQueue.Dequeue(); if (currentMoveState.isMoving) { StartMovement(currentMoveState); } else { StopMovement(currentMoveState); } } // 如果正在移动,持续更新 if (currentMoveState.isMoving) { UpdateMovement(); } } private void StartMovement(MoveState moveState) { targetPosition = moveState.startPosition; Debug.Log($"开始移动 - 位置: {moveState.startPosition}"); } private void StopMovement(MoveState moveState) { targetPosition = moveState.startPosition; Debug.Log($"停止移动 - 最终位置: {moveState.startPosition}"); } private void UpdateMovement() { // 将本地相对方向转换为世界方向 Vector3 localDirection = currentMoveState.moveDirection; Vector3 characterRight = Vector3.ProjectOnPlane(characterController.transform.right, Vector3.up).normalized; Vector3 characterForward = Vector3.ProjectOnPlane(characterController.transform.forward, Vector3.up).normalized; Vector3 movementDirection = characterForward * localDirection.z + characterRight * localDirection.x; float targetSpeed = currentMoveState.moveSpeed; // 平滑加速 if (movementDirection.magnitude > 0.1f) { currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, acceleration * Time.deltaTime); } else { currentSpeed = Mathf.Lerp(currentSpeed, 0f, acceleration * 2f * Time.deltaTime); } // 处理重力 if (characterController.isGrounded) { verticalVelocity = groundedGravity; // 地面移动 - 完全控制 currentVelocity = Vector3.Lerp(currentVelocity, movementDirection.normalized * currentSpeed, acceleration * Time.deltaTime); } else { verticalVelocity -= gravity * Time.deltaTime; // 空中移动 - 有限控制 Vector3 targetAirVelocity = movementDirection.normalized * currentSpeed * airControl; currentVelocity = Vector3.Lerp(currentVelocity, new Vector3(targetAirVelocity.x, currentVelocity.y, targetAirVelocity.z), acceleration * Time.deltaTime); } // 组合移动 Vector3 totalMovement = currentVelocity * Time.deltaTime; totalMovement.y = verticalVelocity * Time.deltaTime; Log.Info($"移动=== Id={Parent.Id} 位置: {characterController.transform.position} 移动量: {totalMovement}"); characterController.Move(totalMovement); } // 添加位置和旋转插值方法 private void InterpolatePositionAndRotation() { // 只对非本地玩家进行插值 if (!IsSelf) { // 插值位置 // characterController.transform.position = Vector3.Lerp( // characterController.transform.position, // targetPosition, // positionLerpSpeed * Time.deltaTime); // 插值旋转 - 使用更平滑的插值方法 characterController.transform.rotation = Quaternion.Slerp( characterController.transform.rotation, targetRotation, rotationLerpSpeed * Time.deltaTime); } } #endregion #region Look public float rotationSyncInterval = 0.15f; public float minRotationChange = 10f; private Vector3 _rotationInput = Vector3.zero; private float rotationSyncTimer; private Vector3 lastSyncedFacing; private bool isRotating = false; [Tooltip("视角旋转敏感度")] public Vector2 sensitivity = new Vector2(0.015f, 0.015f); private void UpdatePlayerRotation(Vector2 lookValue) { // Look Vector2 lookInput = lookValue * sensitivity; // 处理水平旋转 (Yaw) if (lookInput.x != 0.0f) { _rotationInput.y += lookInput.x; isRotating = true; } // 应用旋转到角色和相机 if (_rotationInput != Vector3.zero) { characterController.transform.rotation *= Quaternion.Euler(0, _rotationInput.y, 0); _rotationInput = Vector3.zero; } } // 检查朝向同步 private void CheckRotationSync() { if (!IsSelf) return; rotationSyncTimer += Time.deltaTime; if (rotationSyncTimer >= rotationSyncInterval) { float angleChange = Vector3.Angle(transform.forward, lastSyncedFacing); // 修改:即使在旋转中也定期同步朝向,避免累积误差 if (angleChange >= minRotationChange || isRotating) { SendLookMessage(); lastSyncedFacing = transform.forward; rotationSyncTimer = 0f; isRotating = false; } } } #endregion } }