Files
Fishing2/Assets/Scripts/Fishing2/Unit/Move/CharacterControllerComponent.cs
2025-11-12 17:24:02 +08:00

426 lines
15 KiB
C#

using System.Collections.Generic;
using NBC;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
using UnityEngine;
using UnityEngine.InputSystem;
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 PlayerAsset PlayerAsset;
public readonly Queue<MoveState> MoveStateQueue = new Queue<MoveState>();
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; // 加速度
/// <summary>
/// 重力加速度
/// </summary>
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<CharacterControllerComponent>
{
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<InputComponent>();
if (inputComponent != null)
{
inputComponent.OnPlayerPerformed -= self.OnPlayerCanceled;
inputComponent.OnPlayerPerformed -= self.OnPlayerPerformed;
inputComponent.OnPlayerValueCanceled -= self.OnPlayerValueCanceled;
inputComponent.OnPlayerValuePerformed -= self.OnPlayerValuePerformed;
}
}
self.PlayerAsset = null;
}
}
public class MoveComponentAwakeSystem : AwakeSystem<CharacterControllerComponent>
{
protected override void Awake(CharacterControllerComponent self)
{
var unitUnityComponent = self.Parent.GetComponent<UnitUnityComponent>();
self.PlayerAsset = unitUnityComponent.Asset;
self.characterController = unitUnityComponent.GameObject.GetComponent<CharacterController>();
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>();
inputComponent.OnPlayerPerformed += self.OnPlayerCanceled;
inputComponent.OnPlayerPerformed += self.OnPlayerPerformed;
inputComponent.OnPlayerValueCanceled += self.OnPlayerValueCanceled;
inputComponent.OnPlayerValuePerformed += self.OnPlayerValuePerformed;
}
}
}
public class MoveComponentUpdateSystem : UpdateSystem<CharacterControllerComponent>
{
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<Vector2>();
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<Vector2>();
SendMoveMessage(v2, false);
}
else if (name == InputDef.Player.Look)
{
var v2 = context.ReadValue<Vector2>();
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
});
}
/// <summary>
/// 接收服务器移动开始通知
/// </summary>
/// <param name="startPosition"></param>
/// <param name="moveDirection"></param>
/// <param name="speed"></param>
/// <param name="timestamp"></param>
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}");
}
/// <summary>
/// 接收服务器移动停止通知
/// </summary>
/// <param name="finalPosition"></param>
/// <param name="timestamp"></param>
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
}
}