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
}