Files
2026-03-05 18:07:55 +08:00

303 lines
9.6 KiB
C#

using RootMotion.FinalIK;
using UnityEngine;
namespace NBF
{
/// <summary>
///
/// </summary>
public class PlayerLocalMover : PlayerDriveBase
{
public LayerMask interactableLayer;
public LookAtIK lookAtIK; // 挂在背部上的 LookAtIK 脚本
public float aimDistance = 1.5f; // 目标点离相机多远
private Transform lookTarget; // 实际目标点
[Header("限制角度(单位:度)")] public float maxUpAngle = 20f; // 相机抬头最多20°
public float maxDownAngle = 40f; // 相机低头最多40°
/// <summary>
/// 基础移动速度
/// </summary>
private const float MoveSpeed = 3f;
/// <summary>
/// 冲刺速度
/// </summary>
private const float SprintSpeed = 5f;
private float acceleration = 10f; // 加速度
/// <summary>
/// 重力加速度
/// </summary>
private float gravity = 9.81f;
private float groundedGravity = -0.5f; // 小的向下力确保角色保持在地面
private float airControl = 0.5f; // 空中控制系数
private float verticalVelocity = 0f;
private Vector3 currentVelocity = Vector3.zero;
private float currentSpeed = 0f;
[Tooltip("视角旋转敏感度")] public Vector2 sensitivity = new Vector2(0.015f, 0.015f);
public float minPitch = -40.0f;
public float maxPitch = 75.0f;
public float boatMaxPitch = 60;
private float _cameraPitch;
private Vector3 _rotationInput = Vector3.zero;
private bool _isRun;
[HideInInspector] public int nextShowSlotIndex = -1;
protected override void OnStart()
{
lookAtIK = Character.gameObject.GetComponent<LookAtIK>();
lookTarget = new GameObject("SpineLookTarget").transform;
lookTarget.SetParent(Character.transform);
interactableLayer = LayerMask.GetMask("Interactive");
InputManager.OnPlayerCanceled += OnPlayerCanceled;
InputManager.OnPlayerPerformed += OnPlayerPerformed;
}
protected override void OnUpdate()
{
UpdateGear();
InteractiveCheck();
}
protected override void OnLateUpdate()
{
UpdatePlayerHandView();
}
protected override void OnFixedUpdate()
{
UpdatePlayerPosition();
UpdatePlayerRotation();
}
public override void OnDestroy()
{
InputManager.OnPlayerCanceled -= OnPlayerCanceled;
InputManager.OnPlayerPerformed -= OnPlayerPerformed;
}
#region
private void UpdateGear()
{
if (Player.CanChangeGear())
{
if (nextShowSlotIndex > 0)
{
var data = Fishing.Inst.Datasource;
data.SetSelfTestGear(nextShowSlotIndex);
nextShowSlotIndex = -1;
}
}
}
#endregion
#region
private void OnPlayerPerformed(string action)
{
if (action == "Run")
{
_isRun = true;
}
}
private void OnPlayerCanceled(string action)
{
if (action == "Run")
{
_isRun = false;
}
else if (action.StartsWith("Quick"))
{
nextShowSlotIndex = int.Parse(action.Substring("Quick".Length));
}
else if (action == "UseTorch")
{
Player.Data.openLight = !Player.Data.openLight;
}
else if (action == "UseTelescope")
{
Player.Data.openTelescope = !Player.Data.openTelescope;
Player.ToggleTelescope();
}
}
#endregion
#region
private RaycastHit hitInfo;
private bool isHit;
private readonly float _interactionDistance = 8f;
private InteractiveObject _lastInteractiveObject;
private void InteractiveCheck()
{
if (Player.NowBoat)
{
SetInteractiveObject(null);
return;
}
if (Player && Player.Fsm.CurrentStateId == States.Player.Idle)
{
// 从屏幕中心发射射线
Ray ray = BaseCamera.Main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
isHit = Physics.Raycast(ray, out hitInfo, _interactionDistance, interactableLayer);
// 检测到可交互物体时
if (isHit)
{
var interactiveObject = hitInfo.transform.gameObject.GetComponent<InteractiveObject>();
if (interactiveObject != null)
{
SetInteractiveObject(interactiveObject);
return;
}
}
}
SetInteractiveObject(null);
}
private void SetInteractiveObject(InteractiveObject interactiveObject)
{
if (_lastInteractiveObject != interactiveObject)
{
_lastInteractiveObject = interactiveObject;
InputManager.Instance.OnInteractiveObject(_lastInteractiveObject);
}
}
#endregion
#region
private void UpdatePlayerHandView()
{
var cameraTransform = BaseCamera.Main.transform;
Vector3 cameraForward = cameraTransform.forward;
Vector3 flatForward = Vector3.ProjectOnPlane(cameraForward, Vector3.up).normalized;
// 获取相机 pitch 角度(负值是上看,正值是下看)
float pitchAngle = Vector3.SignedAngle(flatForward, cameraForward, cameraTransform.right);
// 限制 pitch 角度
pitchAngle = Mathf.Clamp(pitchAngle, -maxUpAngle, maxDownAngle);
// 重新构造限制后的目标方向
Quaternion limitedPitch = Quaternion.AngleAxis(pitchAngle, cameraTransform.right);
Vector3 limitedDirection = limitedPitch * flatForward;
// 设置目标点
lookTarget.position = cameraTransform.position + limitedDirection * aimDistance;
lookAtIK.solver.target = lookTarget;
}
#endregion
#region
private void UpdatePlayerPosition()
{
Vector2 movementInput = InputManager.GetMovementInput();
if (Player.NowBoat)
{
Player.NowBoat.BoatInput(movementInput);
return;
}
Vector3 movementDirection = Vector3.zero;
movementDirection += Vector3.forward * movementInput.y;
movementDirection += Vector3.right * movementInput.x;
movementDirection = movementDirection.relativeTo(BaseCamera.Main.transform, Character.transform.up);
// 计算目标速度
float targetSpeed = _isRun ? SprintSpeed : 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 (Character.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;
Character.CollisionFlags = Character.CharacterController.Move(totalMovement);
}
private void UpdatePlayerRotation()
{
// Look
Vector2 lookInput = InputManager.GetLookInput() * sensitivity;
if (lookInput.x != 0.0f)
{
_rotationInput.y += lookInput.x;
}
AddControlPitchInput(-lookInput.y, minPitch, Player.NowBoat ? boatMaxPitch : maxPitch);
Player.CameraRoot.transform.localRotation = Quaternion.Euler(_cameraPitch, 0.0f, 0.0f);
if (_rotationInput != Vector3.zero)
{
Character.CharacterController.transform.rotation *= Quaternion.Euler(_rotationInput);
_rotationInput = Vector3.zero;
}
}
/// <summary>
/// 添加输入(影响俯仰)。
/// 此操作应用于相机父级的本地旋转。
/// </summary>
private void AddControlPitchInput(float value, float minPitch = -40.0f, float maxPitch = 70.0f)
{
if (value != 0.0f)
_cameraPitch = VectorUtil.ClampAngle(_cameraPitch + value, minPitch, maxPitch);
}
#endregion
}
}