using UnityEngine; using UnityEngine.InputSystem; /// /// 플레이어 배의 기본 이동과 회전을 처리하는 컴포넌트 /// [RequireComponent(typeof(Rigidbody))] public class VoyagePlayerShipMovement : MonoBehaviour { #region Settings [System.Serializable] public class MovementSettings { [Tooltip("배의 최대 이동 속도")] public float maxSpeed = 20f; public float accelerationRate = 1f; public float dragFactor = 0.98f; public float minSpeedThreshold = 0.1f; } [System.Serializable] public class RotationSettings { public float maxRotationSpeed = 270f; public float minRotationSpeed = 90f; public float accelerationRate = 5f; [Tooltip("선회 시 감속 정도 (0: 감속 없음, 1: 완전 정지)")] public float turnSpeedPenalty = 0.5f; [Tooltip("최대 감속이 적용되는 각도")] public float maxTurnAngle = 180f; } #endregion #region Inspector Fields [SerializeField] private MovementSettings movementSettings = new(); [SerializeField] private RotationSettings rotationSettings = new(); #endregion #region Properties public Vector2 CurrentInput => currentInput; public float CurrentRotationSpeed => currentRotationSpeed; public float CurrentSpeed => currentSpeed; public float MaxSpeed => movementSettings.maxSpeed; public Vector3 CurrentVelocity => currentVelocity; #endregion #region Private Fields private Vector3 currentVelocity; private Vector2 currentInput; private float currentRotationSpeed; private float targetSpeed; private float currentSpeed; #endregion #region Unity Messages private void FixedUpdate() { UpdateShipMovement(); } #endregion #region Public Methods public void OnMove(InputAction.CallbackContext context) { currentInput = context.ReadValue(); } #endregion #region Movement Methods private void UpdateShipMovement() { if (IsMoving()) { UpdateMovementWithInput(); } else { DecelerateShip(); } ApplyDragForce(); ApplyFinalMovement(); } private void UpdateMovementWithInput() { UpdateSpeed(); UpdateRotation(); } private void UpdateSpeed() { float baseTargetSpeed = CalculateBaseTargetSpeed(); float turnPenaltyFactor = CalculateTurnPenaltyFactor(); targetSpeed = baseTargetSpeed * turnPenaltyFactor; currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, movementSettings.accelerationRate * Time.fixedDeltaTime); if (ShouldStop()) { StopShip(); } UpdateVelocityVector(); } private void UpdateRotation() { if (!IsMoving()) return; Quaternion targetRotation = CalculateTargetRotation(); float rotationSpeed = CalculateRotationSpeed(); ApplyRotation(targetRotation, rotationSpeed); } #endregion #region Helper Methods private bool IsMoving() { return currentInput.magnitude > movementSettings.minSpeedThreshold; } private float CalculateBaseTargetSpeed() { return Mathf.Clamp01(currentInput.magnitude) * movementSettings.maxSpeed; } private float CalculateTurnPenaltyFactor() { Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized; float angleDifference = Vector3.Angle(transform.forward, inputDirection); float penaltyFactor = angleDifference / rotationSettings.maxTurnAngle * rotationSettings.turnSpeedPenalty; return Mathf.Clamp01(1f - penaltyFactor); } private Quaternion CalculateTargetRotation() { Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized; return Quaternion.LookRotation(inputDirection, Vector3.up); } private float CalculateRotationSpeed() { float speedBasedRotation = rotationSettings.maxRotationSpeed * (currentSpeed / movementSettings.maxSpeed); float desiredRotationSpeed = Mathf.Max(speedBasedRotation, rotationSettings.minRotationSpeed); return Mathf.Lerp(currentRotationSpeed, desiredRotationSpeed, rotationSettings.accelerationRate * Time.fixedDeltaTime); } private void ApplyRotation(Quaternion targetRotation, float rotationSpeed) { currentRotationSpeed = rotationSpeed; transform.rotation = Quaternion.RotateTowards( transform.rotation, targetRotation, rotationSpeed * Time.fixedDeltaTime ); } private bool ShouldStop() { return currentSpeed < movementSettings.minSpeedThreshold && targetSpeed < movementSettings.minSpeedThreshold; } private void StopShip() { currentSpeed = 0f; currentVelocity = Vector3.zero; } private void UpdateVelocityVector() { currentVelocity = transform.forward * currentSpeed; } private void DecelerateShip() { currentSpeed = Mathf.Lerp(currentSpeed, 0f, movementSettings.accelerationRate * Time.fixedDeltaTime); currentRotationSpeed = 0f; } private void ApplyDragForce() { currentSpeed *= movementSettings.dragFactor; if (currentSpeed < movementSettings.minSpeedThreshold) { StopShip(); } else { UpdateVelocityVector(); } } private void ApplyFinalMovement() { transform.position += currentVelocity * Time.fixedDeltaTime; } #endregion }