209 lines
5.7 KiB
C#
209 lines
5.7 KiB
C#
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
/// <summary>
|
|
/// 플레이어 배의 기본 이동과 회전을 처리하는 컴포넌트
|
|
/// </summary>
|
|
[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<Vector2>();
|
|
}
|
|
#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
|
|
} |