Files
Fishing2/Assets/Scripts/Fishing/Player/PlayerCharacter/DataDrivenFPSController.cs
2025-05-27 22:29:10 +08:00

334 lines
9.7 KiB
C#

using System;
using RootMotion.FinalIK;
using UnityEngine;
namespace NBF
{
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(CharacterController))]
public class SmoothFPSController : MonoBehaviour
{
public enum ControlMode
{
LocalPlayer, // 本地玩家,接受输入控制
NetworkPlayer // 网络玩家,接受数据驱动
}
[Header("Control Settings")] public ControlMode controlMode = ControlMode.LocalPlayer;
[Range(0.1f, 10f)] public float lookSensitivity = 2f;
[Range(0f, 0.5f)] public float lookSmoothing = 0.1f;
[Range(0.1f, 10f)] public float moveSpeed = 5f;
public float jumpHeight = 2f;
public float gravity = -9.81f;
[Header("Components")] public CharacterController characterController;
public Transform cameraTransform;
// 物理状态
private Vector3 velocity;
private bool isGrounded;
// 视角控制变量
private Vector2 currentLookInput;
private Vector2 smoothedLookInput;
private Vector2 lookVelocity;
private float xRotation = 0f;
// 移动控制变量
private Vector2 currentMoveInput;
private bool currentJumpInput;
// 网络同步数据
// private Vector3 targetPosition;
// private Quaternion targetRotation;
private float positionLerpSpeed = 10f;
private float rotationLerpSpeed = 5f;
public bool IsSelf;
private FPlayer _player;
public LookAtIK lookAtIK; // 挂在背部上的 LookAtIK 脚本
private Transform lookTarget; // 实际目标点
[Header("限制角度(单位:度)")] public float maxUpAngle = 20f; // 相机抬头最多20°
public float maxDownAngle = 40f; // 相机低头最多40°
public float aimDistance = 1.5f; // 目标点离相机多远
[HideInInspector] public int nextShowSlotIndex = -1;
public LayerMask interactableLayer;
public Rigidbody physicsBody; // 用于物理同步的Rigidbody
private void Awake()
{
if (characterController == null)
characterController = GetComponent<CharacterController>();
interactableLayer = LayerMask.GetMask("Interactive");
physicsBody = GetComponent<Rigidbody>();
}
private void Start()
{
InputManager.OnPlayerCanceled += OnPlayerCanceled;
InputManager.OnPlayerPerformed += OnPlayerPerformed;
lookAtIK = GetComponent<LookAtIK>();
_player = GetComponent<FPlayer>();
if (IsSelf)
{
// camera = BaseCamera.Main;
}
lookTarget = new GameObject("SpineLookTarget").transform;
lookTarget.SetParent(_player.transform);
transform.position = _player.Data.position;
transform.rotation = _player.Data.rotation;
}
private void OnDestroy()
{
InputManager.OnPlayerCanceled -= OnPlayerCanceled;
InputManager.OnPlayerPerformed -= OnPlayerPerformed;
}
private void Update()
{
if (controlMode == ControlMode.LocalPlayer)
{
UpdateLocalPlayer();
}
else
{
UpdateNetworkPlayer();
}
UpdateGear();
UpdatePlayerHandView();
}
private void GetInputs()
{
currentMoveInput = InputManager.GetMovementInput();
currentLookInput = InputManager.GetLookInput();
currentJumpInput = Keyboard.current.spaceKey.wasPressedThisFrame;
}
private void UpdatePlayerHandView()
{
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;
}
#region
private void UpdateGear()
{
if (_player.CanChangeGear())
{
if (nextShowSlotIndex > 0)
{
Debug.LogError("切换钓组=========");
var data = Fishing.Inst.Datasource;
data.SetSelfTestGear(nextShowSlotIndex);
nextShowSlotIndex = -1;
}
}
}
#endregion
#region
private void OnPlayerPerformed(string action)
{
if (action == "Run")
{
Sprint();
}
}
private void OnPlayerCanceled(string action)
{
if (action == "Run")
{
StopSprinting();
}
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 bool _isSprinting;
private bool _sprintInputPressed;
[Space(10.0f)] public float maxSprintSpeed = 10.0f;
private void UpdateNetworkPlayer()
{
}
private void UpdateLocalPlayer()
{
// 获取输入
GetInputs();
// 处理地面检测
GroundCheck();
// 处理移动
HandleMovement();
// 处理视角旋转(现在更加平滑)
HandleLookRotation();
// 处理跳跃
// HandleJump();
// 应用重力
ApplyGravity();
}
private void GroundCheck()
{
isGrounded = characterController.isGrounded;
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f; // 轻微下压确保贴地
}
}
private void HandleMovement()
{
Vector3 move = (transform.right * currentMoveInput.x + transform.forward * currentMoveInput.y) *
GetMaxSpeed();
characterController.Move(move * Time.deltaTime);
// physicsBody.MovePosition(move * Time.deltaTime);
// physicsBody.Move(move * Time.deltaTime, transform.rotation);
// 手动同步CharacterController和物理位置
if (physicsBody != null)
{
physicsBody.position = transform.position;
physicsBody.rotation = transform.rotation;
}
}
private void HandleLookRotation()
{
// 应用输入平滑(使用平滑阻尼)
smoothedLookInput = Vector2.SmoothDamp(
smoothedLookInput,
currentLookInput,
ref lookVelocity,
lookSmoothing);
// 计算旋转量(应用灵敏度)
float lookX = smoothedLookInput.x * lookSensitivity * Time.deltaTime;
float lookY = smoothedLookInput.y * lookSensitivity * Time.deltaTime;
// 水平旋转(左右看)
// transform.Rotate(Vector3.up * lookX);
physicsBody.MoveRotation(physicsBody.rotation * Quaternion.Euler(0, lookX, 0));
// 垂直旋转(上下看) - 带有角度限制
xRotation -= lookY;
xRotation = Mathf.Clamp(xRotation, -45f, 80f); // 限制上下视角范围
// 应用摄像机旋转
cameraTransform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
}
private void HandleJump()
{
if (currentJumpInput && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
}
private void ApplyGravity()
{
velocity.y += gravity * Time.deltaTime;
characterController.Move(velocity * Time.deltaTime);
}
public void Sprint()
{
_sprintInputPressed = true;
}
/// <summary>
/// Request the character to stop sprinting.
/// </summary>
public void StopSprinting()
{
_sprintInputPressed = false;
}
private bool CanSprint()
{
return true;
// return IsWalking() && !IsCrouched();
}
private void CheckSprintInput()
{
if (!_isSprinting && _sprintInputPressed && CanSprint())
{
_isSprinting = true;
}
else if (_isSprinting && (!_sprintInputPressed || !CanSprint()))
{
_isSprinting = false;
}
}
public float GetMaxSpeed()
{
return _isSprinting ? maxSprintSpeed : moveSpeed;
}
#endregion
}
}