using UnityEngine; using UnityEngine.InputSystem; /// /// 플레이어 배의 움직임을 제어하는 컴포넌트 /// 속도, 회전, 틸트, 파도 효과 등을 관리합니다. /// public class VoyagePlayerShipMovement : MonoBehaviour { #region Inspector Fields [Header("기본 이동 설정")] [Tooltip("배의 최대 이동 속도")] [SerializeField] private float maxSpeed = 20f; [SerializeField] private float accelerationRate = 1f; [SerializeField] private float dragFactor = 0.98f; [SerializeField] private float minSpeedThreshold = 0.1f; [Header("회전 설정")] [SerializeField] private float rotationSpeed = 270f; [SerializeField] private float minRotationSpeed = 90f; [SerializeField] private float rotationAccelerationRate = 5f; [SerializeField] private float turnSpeedPenalty = 0.5f; [SerializeField] private float maxTurnAngle = 180f; #endregion #region Private Fields private Vector3 currentVelocity; private Vector2 currentInput; public Vector2 CurrentInput => currentInput; private float currentRotationSpeed; public float CurrentRotationSpeed => currentRotationSpeed; private float targetSpeed; private float currentSpeed; public float CurrentSpeed => currentSpeed; public float MaxSpeed => maxSpeed; // 회전 틸트 관련 private float currentRotationTilt; private float lastRotationY; private float currentAngularVelocity; // 가속 틸트 관련 private float currentAccelTilt; private float accelTiltVelocity; private float prevSpeed; // 파도 효과 관련 private float waveTime; private float waveRandomOffset; private float currentWaveHeight; #endregion #region Unity Messages private void FixedUpdate() { UpdateMovement(); } #endregion #region Movement Methods private void UpdateMovement() { if (IsMoving()) { HandleMovement(); HandleRotation(); } else { DecelerateMovement(); } ApplyDrag(); ApplyMovement(); } private bool IsMoving() { return currentInput.magnitude > minSpeedThreshold; } private void HandleMovement() { float baseTargetSpeed = CalculateBaseTargetSpeed(); float turnPenaltyFactor = CalculateTurnPenaltyFactor(); targetSpeed = baseTargetSpeed * turnPenaltyFactor; currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, accelerationRate * Time.fixedDeltaTime); if (ShouldStop()) { currentSpeed = 0f; } UpdateVelocityVector(); } private void HandleRotation() { if (IsMoving()) { Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized; Quaternion targetRotation = Quaternion.LookRotation(inputDirection, Vector3.up); // 회전 속도를 현재 속도에 비례하도록 설정 float desiredRotationSpeed = rotationSpeed * (currentSpeed / maxSpeed); desiredRotationSpeed = Mathf.Max(desiredRotationSpeed, minRotationSpeed); currentRotationSpeed = Mathf.Lerp(currentRotationSpeed, desiredRotationSpeed, rotationAccelerationRate * Time.fixedDeltaTime); // 기본 회전 적용 (오브젝트 전체) transform.rotation = Quaternion.RotateTowards( transform.rotation, targetRotation, currentRotationSpeed * Time.fixedDeltaTime ); } } private float CalculateBaseTargetSpeed() { return Mathf.Clamp01(currentInput.magnitude) * maxSpeed; } private float CalculateTurnPenaltyFactor() { Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized; float angleDifference = Vector3.Angle(transform.forward, inputDirection); return Mathf.Clamp01(1f - (angleDifference / maxTurnAngle * turnSpeedPenalty)); } private bool ShouldStop() { return currentSpeed < minSpeedThreshold && targetSpeed < minSpeedThreshold; } private void UpdateVelocityVector() { currentVelocity = transform.forward * currentSpeed; } #endregion private void ApplyDrag() { currentSpeed *= dragFactor; // 최소 속도 이하면 완전히 정지 if (currentSpeed < minSpeedThreshold) { currentSpeed = 0f; } // 현재 방향으로 감속된 속도 적용 currentVelocity = transform.forward * currentSpeed; } private void ApplyMovement() { transform.position += currentVelocity * Time.fixedDeltaTime; } private void DecelerateMovement() { // 입력이 없을 때는 서서히 감속 currentSpeed = Mathf.Lerp(currentSpeed, 0f, accelerationRate * Time.fixedDeltaTime); currentRotationSpeed = 0; } #region Input Handling public void OnMove(InputAction.CallbackContext context) { currentInput = context.ReadValue(); } #endregion }