DDD-43 배 이동 관련 코드 분리
This commit is contained in:
parent
ccf8772ccd
commit
4a06cc0565
@ -12,6 +12,8 @@ GameObject:
|
||||
- component: {fileID: 1657872600039613395}
|
||||
- component: {fileID: 2479726504690309911}
|
||||
- component: {fileID: -1082383067592254908}
|
||||
- component: {fileID: 7722456790212551628}
|
||||
- component: {fileID: 3140672700990605547}
|
||||
m_Layer: 0
|
||||
m_Name: PlayerShip
|
||||
m_TagString: Untagged
|
||||
@ -114,6 +116,19 @@ MonoBehaviour:
|
||||
rotationAccelerationRate: 5
|
||||
turnSpeedPenalty: 0.5
|
||||
maxTurnAngle: 180
|
||||
--- !u!114 &7722456790212551628
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1553910019582315619}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a09f8b932e18409aa7f5d2a221921f45, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
meshTransform: {fileID: 2507610487897935131}
|
||||
maxRotationTiltAngle: 15
|
||||
rotationTiltSpeed: 5
|
||||
rotationTiltReturnSpeed: 3
|
||||
@ -130,7 +145,18 @@ MonoBehaviour:
|
||||
speedWaveMultiplier: 5
|
||||
randomWaveOffset: 0.5
|
||||
waveUnitSpeed: 10
|
||||
meshTransform: {fileID: 2507610487897935131}
|
||||
--- !u!114 &3140672700990605547
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1553910019582315619}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3d5c5f51b32b4633b887d096554c6cd9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
showDebugVisuals: 1
|
||||
debugLineLength: 5
|
||||
debugLineWidth: 0.1
|
||||
|
@ -22,41 +22,20 @@ public class VoyagePlayerShipMovement : MonoBehaviour
|
||||
[SerializeField] private float rotationAccelerationRate = 5f;
|
||||
[SerializeField] private float turnSpeedPenalty = 0.5f;
|
||||
[SerializeField] private float maxTurnAngle = 180f;
|
||||
|
||||
[Header("회전 틸트 설정")]
|
||||
[SerializeField] private float maxRotationTiltAngle = 15f;
|
||||
[SerializeField] private float rotationTiltSpeed = 5f;
|
||||
[SerializeField] private float rotationTiltReturnSpeed = 3f;
|
||||
[SerializeField] private float angularVelocityMultiplier = 2f;
|
||||
|
||||
[Header("가속 틸트 설정")]
|
||||
[SerializeField] private float maxAccelTiltAngle = 15f;
|
||||
[SerializeField] private float accelTiltForce = 15f;
|
||||
[SerializeField] private float accelTiltDamping = 0.9f;
|
||||
[SerializeField] private float accelTiltSpeed = 10f;
|
||||
[SerializeField] private float springStiffness = 30f;
|
||||
[SerializeField] private float springDamping = 15f;
|
||||
|
||||
[Header("파도 효과 설정")]
|
||||
[SerializeField] private float minSpeedWaveHeight = 0.2f;
|
||||
[SerializeField] private float maxSpeedWaveHeight = 0.05f;
|
||||
[SerializeField] private float baseWaveFrequency = 1f;
|
||||
[SerializeField] private float speedWaveMultiplier = 5f;
|
||||
[SerializeField] private float randomWaveOffset = 0.5f;
|
||||
[SerializeField] private float waveUnitSpeed = 10f;
|
||||
|
||||
[Header("메시 설정")]
|
||||
[SerializeField] private Transform meshTransform;
|
||||
|
||||
|
||||
#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;
|
||||
@ -73,41 +52,16 @@ public class VoyagePlayerShipMovement : MonoBehaviour
|
||||
private float waveRandomOffset;
|
||||
private float currentWaveHeight;
|
||||
|
||||
// 메시 원본 상태
|
||||
private Quaternion originalMeshRotation;
|
||||
private Vector3 originalMeshPosition;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Messages
|
||||
|
||||
private void Start()
|
||||
{
|
||||
InitializeMeshTransform();
|
||||
InitializeWaveEffect();
|
||||
#if UNITY_EDITOR
|
||||
InitializeDebugVisuals();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
UpdateMovement();
|
||||
UpdateVisualEffects();
|
||||
#if UNITY_EDITOR
|
||||
UpdateDebugVisuals();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
ValidateMeshTransform();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Movement Methods
|
||||
|
||||
private void UpdateMovement()
|
||||
{
|
||||
if (IsMoving())
|
||||
@ -145,43 +99,6 @@ public class VoyagePlayerShipMovement : MonoBehaviour
|
||||
UpdateVelocityVector();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#region Visual Effects
|
||||
|
||||
private void UpdateVisualEffects()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
UpdateMeshRotationTilt();
|
||||
UpdateAccelerationTilt();
|
||||
ApplyMeshTilt();
|
||||
UpdateWaveMotion();
|
||||
ApplyMeshOffset();
|
||||
}
|
||||
|
||||
private void HandleRotation()
|
||||
{
|
||||
if (IsMoving())
|
||||
@ -203,99 +120,30 @@ public class VoyagePlayerShipMovement : MonoBehaviour
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMeshRotationTilt()
|
||||
|
||||
private float CalculateBaseTargetSpeed()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
// 현재 Y축 회전값과 각속도 계산
|
||||
float currentRotationY = transform.eulerAngles.y;
|
||||
float deltaRotation = Mathf.DeltaAngle(lastRotationY, currentRotationY);
|
||||
currentAngularVelocity = deltaRotation / Time.fixedDeltaTime;
|
||||
|
||||
// 목표 틸트 각도 계산
|
||||
float targetTilt = -currentAngularVelocity * angularVelocityMultiplier;
|
||||
targetTilt = Mathf.Clamp(targetTilt, -maxRotationTiltAngle, maxRotationTiltAngle);
|
||||
|
||||
// 틸트 적용 또는 복귀
|
||||
if (Mathf.Abs(currentAngularVelocity) > 0.1f)
|
||||
{
|
||||
currentRotationTilt =
|
||||
Mathf.Lerp(currentRotationTilt, targetTilt, rotationTiltSpeed * Time.fixedDeltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 입력이 없을 때는 원래 자세로 천천히 복귀
|
||||
currentRotationTilt = Mathf.Lerp(currentRotationTilt, 0f, rotationTiltReturnSpeed * Time.fixedDeltaTime);
|
||||
}
|
||||
|
||||
lastRotationY = currentRotationY;
|
||||
return Mathf.Clamp01(currentInput.magnitude) * maxSpeed;
|
||||
}
|
||||
|
||||
private void UpdateAccelerationTilt()
|
||||
private float CalculateTurnPenaltyFactor()
|
||||
{
|
||||
// 가속도 계산
|
||||
float acceleration = (currentSpeed - prevSpeed) / Time.fixedDeltaTime;
|
||||
|
||||
// 스프링 물리 시스템 구현
|
||||
float springForce = -springStiffness * currentAccelTilt; // 복원력
|
||||
float dampingForce = -springDamping * accelTiltVelocity; // 감쇠력
|
||||
float accelerationForce = -acceleration * accelTiltForce; // 가속에 의한 힘
|
||||
|
||||
// 전체 힘 계산
|
||||
float totalForce = springForce + dampingForce + accelerationForce;
|
||||
|
||||
// 가속도 계산 (F = ma, 질량은 1로 가정)
|
||||
float tiltAcceleration = totalForce;
|
||||
|
||||
// 속도 업데이트
|
||||
accelTiltVelocity += tiltAcceleration;
|
||||
accelTiltVelocity *= accelTiltDamping; // 감쇠 적용
|
||||
accelTiltVelocity *= Time.fixedDeltaTime;
|
||||
|
||||
// 위치(각도) 업데이트
|
||||
currentAccelTilt = Mathf.Lerp(currentAccelTilt, currentAccelTilt + accelTiltVelocity,
|
||||
accelTiltSpeed * Time.fixedDeltaTime);
|
||||
currentAccelTilt = Mathf.Clamp(currentAccelTilt, -maxAccelTiltAngle, maxAccelTiltAngle);
|
||||
|
||||
prevSpeed = currentSpeed;
|
||||
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 void ApplyMeshTilt()
|
||||
private bool ShouldStop()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
// 회전 틸트와 가속 틸트를 조합
|
||||
// 메시에 최종 틸트 적용
|
||||
meshTransform.localRotation = originalMeshRotation * Quaternion.Euler(
|
||||
currentAccelTilt, // X축 (가속 틸트)
|
||||
0, // Y축
|
||||
currentRotationTilt // Z축 (회전 틸트)
|
||||
);
|
||||
return currentSpeed < minSpeedThreshold && targetSpeed < minSpeedThreshold;
|
||||
}
|
||||
|
||||
private void UpdateWaveMotion()
|
||||
private void UpdateVelocityVector()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
// 현재 속도에 비례하여 파도 주기 조절
|
||||
float waveSpeedFactor = 1f + (currentSpeed / waveUnitSpeed) * speedWaveMultiplier;
|
||||
waveTime += Time.fixedDeltaTime * baseWaveFrequency * waveSpeedFactor;
|
||||
float currentSpeedByUnit = currentSpeed / waveUnitSpeed;
|
||||
currentSpeedByUnit = Mathf.Clamp01(currentSpeedByUnit);
|
||||
float waveHeight = Mathf.Lerp(minSpeedWaveHeight, maxSpeedWaveHeight, currentSpeedByUnit);
|
||||
|
||||
currentWaveHeight = waveHeight * Mathf.Sin(waveTime + waveRandomOffset);
|
||||
currentVelocity = transform.forward * currentSpeed;
|
||||
}
|
||||
|
||||
private void ApplyMeshOffset()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
Vector3 position = originalMeshPosition + (Vector3.up * currentWaveHeight);
|
||||
meshTransform.localPosition = position;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ApplyDrag()
|
||||
{
|
||||
currentSpeed *= dragFactor;
|
||||
@ -310,7 +158,6 @@ public class VoyagePlayerShipMovement : MonoBehaviour
|
||||
currentVelocity = transform.forward * currentSpeed;
|
||||
}
|
||||
|
||||
|
||||
private void ApplyMovement()
|
||||
{
|
||||
transform.position += currentVelocity * Time.fixedDeltaTime;
|
||||
@ -323,227 +170,10 @@ public class VoyagePlayerShipMovement : MonoBehaviour
|
||||
currentRotationSpeed = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Input Handling
|
||||
|
||||
public void OnMove(InputAction.CallbackContext context)
|
||||
{
|
||||
currentInput = context.ReadValue<Vector2>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
||||
private void InitializeMeshTransform()
|
||||
{
|
||||
if (meshTransform is null)
|
||||
{
|
||||
Debug.LogError("Mesh Transform이 할당되지 않았습니다.");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
originalMeshPosition = meshTransform.localPosition;
|
||||
originalMeshRotation = meshTransform.localRotation;
|
||||
lastRotationY = transform.eulerAngles.y;
|
||||
}
|
||||
|
||||
private void InitializeWaveEffect()
|
||||
{
|
||||
waveTime = 0f;
|
||||
waveRandomOffset = Random.Range(-randomWaveOffset, randomWaveOffset);
|
||||
}
|
||||
|
||||
private void ValidateMeshTransform()
|
||||
{
|
||||
if (Application.isEditor && !Application.isPlaying && meshTransform is null)
|
||||
{
|
||||
Debug.LogWarning("Mesh Transform을 Inspector에서 할당해주세요.");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[Header("Debug Visualization")]
|
||||
[SerializeField] private bool showDebugVisuals = true;
|
||||
|
||||
[SerializeField] private float debugLineLength = 5f;
|
||||
[SerializeField] private float debugLineWidth = 0.1f;
|
||||
|
||||
private LineRenderer _speedLineRenderer;
|
||||
private LineRenderer _rotationSpeedLineRenderer;
|
||||
private LineRenderer _rotationDeltaLineRenderer;
|
||||
private LineRenderer _TiltLineRenderer;
|
||||
private LineRenderer _waveHeightLineRenderer;
|
||||
private LineRenderer _wavePatternLineRenderer;
|
||||
|
||||
private void InitializeDebugVisuals()
|
||||
{
|
||||
if (!showDebugVisuals) return;
|
||||
|
||||
// 속도 표시
|
||||
_speedLineRenderer = CreateLineRenderer("SpeedLine", Color.green);
|
||||
// 회전 방향 표시
|
||||
_rotationSpeedLineRenderer = CreateLineRenderer("RotationSpeedLine", Color.magenta);
|
||||
// 회전 방향 표시
|
||||
_rotationDeltaLineRenderer = CreateLineRenderer("RotationDeltaLine", Color.yellow);
|
||||
// 틸트 표시
|
||||
_TiltLineRenderer = CreateLineRenderer("TiltLine", Color.red);
|
||||
// 파도 높이 표시
|
||||
_waveHeightLineRenderer = CreateLineRenderer("WaveHeightLine", Color.blue);
|
||||
// 파도 패턴 표시
|
||||
_wavePatternLineRenderer = CreateLineRenderer("WavePatternLine", Color.cyan);
|
||||
_wavePatternLineRenderer.positionCount = 50; // 파도 패턴을 위한 더 많은 점
|
||||
}
|
||||
|
||||
private void UpdateDebugVisuals()
|
||||
{
|
||||
if (!showDebugVisuals) return;
|
||||
|
||||
// 속도 벡터 표시
|
||||
UpdateSpeedLine();
|
||||
|
||||
// 회전 방향 및 각속도 표시
|
||||
UpdateRotationSpeedLine();
|
||||
UpdateRotationDeltaLine();
|
||||
// 회전 틸트 표시
|
||||
UpdateTiltLine();
|
||||
// 파도 높이와 패턴 표시
|
||||
UpdateWaveVisualization();
|
||||
}
|
||||
|
||||
private void UpdateSpeedLine()
|
||||
{
|
||||
Vector3 start = transform.position + Vector3.up * 1.5f;
|
||||
Vector3 end = start + transform.forward * (currentSpeed / maxSpeed) * debugLineLength * 2;
|
||||
DrawLine(_speedLineRenderer, start, end);
|
||||
}
|
||||
|
||||
private void UpdateRotationSpeedLine()
|
||||
{
|
||||
Vector3 start = transform.position + Vector3.up * 1.2f;
|
||||
// 각속도를 호로 표현
|
||||
if (Mathf.Abs(currentRotationSpeed) > 0.1f)
|
||||
{
|
||||
Vector3[] arcPoints = new Vector3[10];
|
||||
float radius = debugLineLength * 1f;
|
||||
float angleStep = currentRotationSpeed * 1f / (arcPoints.Length - 1);
|
||||
|
||||
for (int i = 0; i < arcPoints.Length; i++)
|
||||
{
|
||||
float angle = angleStep * i;
|
||||
Vector3 point = start + Quaternion.Euler(0, angle, 0) * transform.forward * radius;
|
||||
arcPoints[i] = point;
|
||||
}
|
||||
|
||||
_rotationSpeedLineRenderer.positionCount = arcPoints.Length;
|
||||
_rotationSpeedLineRenderer.SetPositions(arcPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rotationSpeedLineRenderer.positionCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRotationDeltaLine()
|
||||
{
|
||||
float deltaAngle = 0f;
|
||||
if (currentInput.magnitude > minSpeedThreshold)
|
||||
{
|
||||
Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized;
|
||||
Quaternion targetRotation = Quaternion.LookRotation(inputDirection, Vector3.up);
|
||||
deltaAngle = Quaternion.Angle(transform.rotation, targetRotation);
|
||||
}
|
||||
|
||||
Vector3 start = transform.position + Vector3.up * 1.2f;
|
||||
// 각속도를 호로 표현
|
||||
if (Mathf.Abs(deltaAngle) > 0.1f)
|
||||
{
|
||||
Vector3[] arcPoints = new Vector3[10];
|
||||
float radius = debugLineLength * 1.05f;
|
||||
float angleStep = deltaAngle * 1f / (arcPoints.Length - 1);
|
||||
|
||||
for (int i = 0; i < arcPoints.Length; i++)
|
||||
{
|
||||
float angle = angleStep * i;
|
||||
Vector3 point = start + Quaternion.Euler(0, angle, 0) * transform.forward * radius;
|
||||
arcPoints[i] = point;
|
||||
}
|
||||
|
||||
_rotationDeltaLineRenderer.positionCount = arcPoints.Length;
|
||||
_rotationDeltaLineRenderer.SetPositions(arcPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rotationDeltaLineRenderer.positionCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTiltLine()
|
||||
{
|
||||
Vector3 start = transform.position + Vector3.up * 1.5f;
|
||||
Vector3 tiltDirection = meshTransform.up;
|
||||
DrawLine(_TiltLineRenderer, start, start + tiltDirection * debugLineLength * 0.4f);
|
||||
}
|
||||
|
||||
private void UpdateWaveVisualization()
|
||||
{
|
||||
// 현재 파도 높이 표시
|
||||
Vector3 waveStart = transform.position + Vector3.up * 1.5f - transform.forward * 1.5f;
|
||||
Vector3 waveEnd = waveStart + Vector3.up * currentWaveHeight * debugLineLength;
|
||||
DrawLine(_waveHeightLineRenderer, waveStart, waveEnd);
|
||||
|
||||
// 파도 패턴 시각화
|
||||
Vector3[] wavePoints = new Vector3[_wavePatternLineRenderer.positionCount];
|
||||
float waveLength = debugLineLength * 2f;
|
||||
|
||||
for (int i = 0; i < wavePoints.Length; i++)
|
||||
{
|
||||
float t = (float)i / (_wavePatternLineRenderer.positionCount - 1);
|
||||
float x = t * waveLength - waveLength * 0.5f;
|
||||
float currentSpeedByUnit = currentSpeed / waveUnitSpeed;
|
||||
currentSpeedByUnit = Mathf.Clamp01(currentSpeedByUnit);
|
||||
float waveHeight = Mathf.Lerp(minSpeedWaveHeight, maxSpeedWaveHeight, currentSpeedByUnit);
|
||||
float y = Mathf.Sin((waveTime + x) * baseWaveFrequency) * waveHeight;
|
||||
|
||||
wavePoints[i] = transform.position +
|
||||
Vector3.right * x +
|
||||
Vector3.up * (y + 2f); // 높이 오프셋
|
||||
wavePoints[i] += Vector3.back * 3f + Vector3.down * 1f;
|
||||
}
|
||||
|
||||
_wavePatternLineRenderer.SetPositions(wavePoints);
|
||||
}
|
||||
|
||||
private LineRenderer CreateLineRenderer(string name, Color color)
|
||||
{
|
||||
GameObject lineObj = new GameObject(name);
|
||||
lineObj.transform.SetParent(transform);
|
||||
LineRenderer line = lineObj.AddComponent<LineRenderer>();
|
||||
|
||||
line.startWidth = debugLineWidth;
|
||||
line.endWidth = debugLineWidth;
|
||||
line.material = new Material(Shader.Find("Universal Render Pipeline/Unlit"));
|
||||
line.startColor = color;
|
||||
line.endColor = color;
|
||||
line.positionCount = 2;
|
||||
|
||||
line.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
line.receiveShadows = false;
|
||||
line.material.color = color;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
private void DrawLine(LineRenderer line, Vector3 start, Vector3 end)
|
||||
{
|
||||
if (line is null) return;
|
||||
line.positionCount = 2;
|
||||
line.SetPosition(0, start);
|
||||
line.SetPosition(1, end);
|
||||
}
|
||||
#endif
|
||||
}
|
222
Assets/0_Voyage/_Scripts/Ship/VoyagePlayerShipMovementDebug.cs
Normal file
222
Assets/0_Voyage/_Scripts/Ship/VoyagePlayerShipMovementDebug.cs
Normal file
@ -0,0 +1,222 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Voyage
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(VoyagePlayerShipMovement)), RequireComponent(typeof(VoyagePlayerShipMovementVisual))]
|
||||
public class VoyagePlayerShipDebug : MonoBehaviour
|
||||
{
|
||||
[Header("Debug Visualization")]
|
||||
[SerializeField] private bool showDebugVisuals = true;
|
||||
[SerializeField] private float debugLineLength = 5f;
|
||||
[SerializeField] private float debugLineWidth = 0.1f;
|
||||
|
||||
private LineRenderer _speedLineRenderer;
|
||||
private LineRenderer _rotationSpeedLineRenderer;
|
||||
private LineRenderer _rotationDeltaLineRenderer;
|
||||
private LineRenderer _TiltLineRenderer;
|
||||
private LineRenderer _waveHeightLineRenderer;
|
||||
private LineRenderer _wavePatternLineRenderer;
|
||||
|
||||
private VoyagePlayerShipMovement movement;
|
||||
private VoyagePlayerShipMovementVisual movementVisual;
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (!showDebugVisuals) return;
|
||||
|
||||
movement = GetComponent<VoyagePlayerShipMovement>();
|
||||
movementVisual = GetComponent<VoyagePlayerShipMovementVisual>();
|
||||
|
||||
InitializeDebugVisuals();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!showDebugVisuals) return;
|
||||
UpdateDebugVisuals();
|
||||
}
|
||||
|
||||
private void InitializeDebugVisuals()
|
||||
{
|
||||
if (!showDebugVisuals) return;
|
||||
|
||||
// 속도 표시
|
||||
_speedLineRenderer = CreateLineRenderer("SpeedLine", Color.green);
|
||||
// 회전 방향 표시
|
||||
_rotationSpeedLineRenderer = CreateLineRenderer("RotationSpeedLine", Color.magenta);
|
||||
// 회전 방향 표시
|
||||
_rotationDeltaLineRenderer = CreateLineRenderer("RotationDeltaLine", Color.yellow);
|
||||
// 틸트 표시
|
||||
_TiltLineRenderer = CreateLineRenderer("TiltLine", Color.red);
|
||||
// 파도 높이 표시
|
||||
_waveHeightLineRenderer = CreateLineRenderer("WaveHeightLine", Color.blue);
|
||||
// 파도 패턴 표시
|
||||
_wavePatternLineRenderer = CreateLineRenderer("WavePatternLine", Color.cyan);
|
||||
_wavePatternLineRenderer.positionCount = 50; // 파도 패턴을 위한 더 많은 점
|
||||
}
|
||||
|
||||
private void UpdateDebugVisuals()
|
||||
{
|
||||
if (!showDebugVisuals) return;
|
||||
|
||||
// 속도 벡터 표시
|
||||
UpdateSpeedLine();
|
||||
|
||||
// 회전 방향 및 각속도 표시
|
||||
UpdateRotationSpeedLine();
|
||||
UpdateRotationDeltaLine();
|
||||
// 회전 틸트 표시
|
||||
UpdateTiltLine();
|
||||
// 파도 높이와 패턴 표시
|
||||
UpdateWaveVisualization();
|
||||
}
|
||||
|
||||
private float GetCurrentSpeed() => movement.CurrentSpeed;
|
||||
private float GetMaxSpeed() => movement.MaxSpeed;
|
||||
private float GetRotationSpeed() => movement.CurrentRotationSpeed;
|
||||
private Vector2 GetCurrentInput() => movement.CurrentInput;
|
||||
|
||||
private void UpdateSpeedLine()
|
||||
{
|
||||
Vector3 start = transform.position + Vector3.up * 1.5f;
|
||||
Vector3 end = start + transform.forward * ((GetCurrentSpeed() / GetMaxSpeed()) * debugLineLength * 2);
|
||||
DrawLine(_speedLineRenderer, start, end);
|
||||
}
|
||||
|
||||
private void UpdateRotationSpeedLine()
|
||||
{
|
||||
Vector3 start = transform.position + Vector3.up * 1.2f;
|
||||
// 각속도를 호로 표현
|
||||
if (Mathf.Abs(GetRotationSpeed()) > 0.1f)
|
||||
{
|
||||
Vector3[] arcPoints = new Vector3[10];
|
||||
float radius = debugLineLength * 1f;
|
||||
float angleStep = GetRotationSpeed() * 1f / (arcPoints.Length - 1);
|
||||
|
||||
for (int i = 0; i < arcPoints.Length; i++)
|
||||
{
|
||||
float angle = angleStep * i;
|
||||
Vector3 point = start + Quaternion.Euler(0, angle, 0) * transform.forward * radius;
|
||||
arcPoints[i] = point;
|
||||
}
|
||||
|
||||
_rotationSpeedLineRenderer.positionCount = arcPoints.Length;
|
||||
_rotationSpeedLineRenderer.SetPositions(arcPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rotationSpeedLineRenderer.positionCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRotationDeltaLine()
|
||||
{
|
||||
float deltaAngle = 0f;
|
||||
if (GetCurrentInput().magnitude > 0.1f)
|
||||
{
|
||||
Vector3 inputDirection = new Vector3(GetCurrentInput().x, 0, GetCurrentInput().y).normalized;
|
||||
Quaternion targetRotation = Quaternion.LookRotation(inputDirection, Vector3.up);
|
||||
deltaAngle = Quaternion.Angle(transform.rotation, targetRotation);
|
||||
}
|
||||
|
||||
Vector3 start = transform.position + Vector3.up * 1.2f;
|
||||
// 각속도를 호로 표현
|
||||
if (Mathf.Abs(deltaAngle) > 0.1f)
|
||||
{
|
||||
Vector3[] arcPoints = new Vector3[10];
|
||||
float radius = debugLineLength * 1.05f;
|
||||
float angleStep = deltaAngle * 1f / (arcPoints.Length - 1);
|
||||
|
||||
for (int i = 0; i < arcPoints.Length; i++)
|
||||
{
|
||||
float angle = angleStep * i;
|
||||
Vector3 point = start + Quaternion.Euler(0, angle, 0) * transform.forward * radius;
|
||||
arcPoints[i] = point;
|
||||
}
|
||||
|
||||
_rotationDeltaLineRenderer.positionCount = arcPoints.Length;
|
||||
_rotationDeltaLineRenderer.SetPositions(arcPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rotationDeltaLineRenderer.positionCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTiltLine()
|
||||
{
|
||||
Vector3 start = transform.position + Vector3.up * 1.5f;
|
||||
Vector3 tiltDirection = movementVisual.MeshTransform.up;
|
||||
DrawLine(_TiltLineRenderer, start, start + tiltDirection * (debugLineLength * 0.4f));
|
||||
}
|
||||
|
||||
private void UpdateWaveVisualization()
|
||||
{
|
||||
// 현재 파도 높이 표시
|
||||
Vector3 waveStart = transform.position + Vector3.up * 1.5f - transform.forward * 1.5f;
|
||||
Vector3 waveEnd = waveStart + Vector3.up * (GetCurrentWaveHeight() * debugLineLength);
|
||||
DrawLine(_waveHeightLineRenderer, waveStart, waveEnd);
|
||||
|
||||
// 파도 패턴 시각화
|
||||
Vector3[] wavePoints = new Vector3[_wavePatternLineRenderer.positionCount];
|
||||
float waveLength = debugLineLength * 2f;
|
||||
|
||||
for (int i = 0; i < wavePoints.Length; i++)
|
||||
{
|
||||
float t = (float)i / (_wavePatternLineRenderer.positionCount - 1);
|
||||
float x = t * waveLength - waveLength * 0.5f;
|
||||
float currentSpeedByUnit = GetCurrentSpeed() / GetWaveUnitSpeed();
|
||||
currentSpeedByUnit = Mathf.Clamp01(currentSpeedByUnit);
|
||||
float waveHeight = Mathf.Lerp(GetMinSpeedWaveHeight(), GetMaxSpeedWaveHeight(), currentSpeedByUnit);
|
||||
float y = Mathf.Sin((GetWaveTime() + x) * GetBaseWaveFrequency()) * waveHeight;
|
||||
|
||||
wavePoints[i] = transform.position +
|
||||
Vector3.right * x +
|
||||
Vector3.up * (y + 2f); // 높이 오프셋
|
||||
wavePoints[i] += Vector3.back * 3f + Vector3.down * 1f;
|
||||
}
|
||||
|
||||
_wavePatternLineRenderer.SetPositions(wavePoints);
|
||||
}
|
||||
|
||||
private float GetCurrentWaveHeight() => movementVisual.CurrentWaveHeight;
|
||||
private float GetWaveUnitSpeed() => movementVisual.WaveUnitSpeed;
|
||||
private float GetMinSpeedWaveHeight() => movementVisual.MinSpeedWaveHeight;
|
||||
private float GetMaxSpeedWaveHeight() => movementVisual.MaxSpeedWaveHeight;
|
||||
private float GetWaveTime() => movementVisual.WaveTime;
|
||||
private float GetBaseWaveFrequency() => movementVisual.BaseWaveFrequency;
|
||||
|
||||
private LineRenderer CreateLineRenderer(string name, Color color)
|
||||
{
|
||||
GameObject lineObj = new GameObject(name);
|
||||
lineObj.transform.SetParent(transform);
|
||||
LineRenderer line = lineObj.AddComponent<LineRenderer>();
|
||||
|
||||
line.startWidth = debugLineWidth;
|
||||
line.endWidth = debugLineWidth;
|
||||
line.material = new Material(Shader.Find("Universal Render Pipeline/Unlit"));
|
||||
line.startColor = color;
|
||||
line.endColor = color;
|
||||
line.positionCount = 2;
|
||||
|
||||
line.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
line.receiveShadows = false;
|
||||
line.material.color = color;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
private void DrawLine(LineRenderer line, Vector3 start, Vector3 end)
|
||||
{
|
||||
if (line is null) return;
|
||||
line.positionCount = 2;
|
||||
line.SetPosition(0, start);
|
||||
line.SetPosition(1, end);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d5c5f51b32b4633b887d096554c6cd9
|
||||
timeCreated: 1752575924
|
224
Assets/0_Voyage/_Scripts/Ship/VoyagePlayerShipMovementVisual.cs
Normal file
224
Assets/0_Voyage/_Scripts/Ship/VoyagePlayerShipMovementVisual.cs
Normal file
@ -0,0 +1,224 @@
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(VoyagePlayerShipMovement))]
|
||||
public class VoyagePlayerShipMovementVisual : MonoBehaviour
|
||||
{
|
||||
[Header("메시 설정")]
|
||||
[SerializeField] private Transform meshTransform;
|
||||
public Transform MeshTransform => meshTransform;
|
||||
|
||||
[Header("회전 틸트 설정")]
|
||||
[SerializeField] private float maxRotationTiltAngle = 15f;
|
||||
[SerializeField] private float rotationTiltSpeed = 5f;
|
||||
[SerializeField] private float rotationTiltReturnSpeed = 3f;
|
||||
[SerializeField] private float angularVelocityMultiplier = 2f;
|
||||
|
||||
[Header("가속 틸트 설정")]
|
||||
[SerializeField] private float maxAccelTiltAngle = 15f;
|
||||
[SerializeField] private float accelTiltForce = 15f;
|
||||
[SerializeField] private float accelTiltDamping = 0.9f;
|
||||
[SerializeField] private float accelTiltSpeed = 10f;
|
||||
[SerializeField] private float springStiffness = 30f;
|
||||
[SerializeField] private float springDamping = 15f;
|
||||
|
||||
[Header("파도 효과 설정")]
|
||||
[SerializeField] private float minSpeedWaveHeight = 0.2f;
|
||||
public float MinSpeedWaveHeight => minSpeedWaveHeight;
|
||||
[SerializeField] private float maxSpeedWaveHeight = 0.05f;
|
||||
public float MaxSpeedWaveHeight => maxSpeedWaveHeight;
|
||||
[SerializeField] private float baseWaveFrequency = 1f;
|
||||
public float BaseWaveFrequency => baseWaveFrequency;
|
||||
[SerializeField] private float speedWaveMultiplier = 5f;
|
||||
[SerializeField] private float randomWaveOffset = 0.5f;
|
||||
[SerializeField] private float waveUnitSpeed = 10f;
|
||||
public float WaveUnitSpeed => waveUnitSpeed;
|
||||
|
||||
private VoyagePlayerShipMovement movement;
|
||||
private Quaternion originalMeshRotation;
|
||||
private Vector3 originalMeshPosition;
|
||||
|
||||
// 틸트 관련 변수들
|
||||
private float currentRotationTilt;
|
||||
private float lastRotationY;
|
||||
private float currentAngularVelocity;
|
||||
private float currentAccelTilt;
|
||||
private float accelTiltVelocity;
|
||||
private float prevSpeed;
|
||||
|
||||
// 파도 관련 변수들
|
||||
private float waveTime;
|
||||
public float WaveTime => waveTime;
|
||||
private float waveRandomOffset;
|
||||
private float currentWaveHeight;
|
||||
public float CurrentWaveHeight => currentWaveHeight;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
InitializeComponents();
|
||||
InitializeMeshTransform();
|
||||
InitializeWaveEffect();
|
||||
}
|
||||
|
||||
#region Initialization
|
||||
|
||||
private void InitializeWaveEffect()
|
||||
{
|
||||
waveTime = 0f;
|
||||
waveRandomOffset = Random.Range(-randomWaveOffset, randomWaveOffset);
|
||||
}
|
||||
|
||||
private void ValidateMeshTransform()
|
||||
{
|
||||
if (Application.isEditor && !Application.isPlaying && meshTransform is null)
|
||||
{
|
||||
Debug.LogWarning("Mesh Transform을 Inspector에서 할당해주세요.");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeMeshTransform()
|
||||
{
|
||||
if (meshTransform is null)
|
||||
{
|
||||
Debug.LogError("Mesh Transform이 할당되지 않았습니다.");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
originalMeshPosition = meshTransform.localPosition;
|
||||
originalMeshRotation = meshTransform.localRotation;
|
||||
lastRotationY = transform.eulerAngles.y;
|
||||
}
|
||||
|
||||
private void InitializeComponents()
|
||||
{
|
||||
movement = GetComponent<VoyagePlayerShipMovement>();
|
||||
|
||||
if (meshTransform == null)
|
||||
{
|
||||
Debug.LogError("Mesh Transform이 할당되지 않았습니다.");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
originalMeshPosition = meshTransform.localPosition;
|
||||
originalMeshRotation = meshTransform.localRotation;
|
||||
lastRotationY = transform.eulerAngles.y;
|
||||
waveTime = 0f;
|
||||
waveRandomOffset = Random.Range(-randomWaveOffset, randomWaveOffset);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
UpdateMeshRotationTilt();
|
||||
UpdateAccelerationTilt();
|
||||
ApplyMeshTilt();
|
||||
UpdateWaveMotion();
|
||||
ApplyMeshOffset();
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
ValidateMeshTransform();
|
||||
}
|
||||
|
||||
private void UpdateMeshRotationTilt()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
// 현재 Y축 회전값과 각속도 계산
|
||||
float currentRotationY = transform.eulerAngles.y;
|
||||
float deltaRotation = Mathf.DeltaAngle(lastRotationY, currentRotationY);
|
||||
currentAngularVelocity = deltaRotation / Time.fixedDeltaTime;
|
||||
|
||||
// 목표 틸트 각도 계산
|
||||
float targetTilt = -currentAngularVelocity * angularVelocityMultiplier;
|
||||
targetTilt = Mathf.Clamp(targetTilt, -maxRotationTiltAngle, maxRotationTiltAngle);
|
||||
|
||||
// 틸트 적용 또는 복귀
|
||||
if (Mathf.Abs(currentAngularVelocity) > 0.1f)
|
||||
{
|
||||
currentRotationTilt =
|
||||
Mathf.Lerp(currentRotationTilt, targetTilt, rotationTiltSpeed * Time.fixedDeltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 입력이 없을 때는 원래 자세로 천천히 복귀
|
||||
currentRotationTilt = Mathf.Lerp(currentRotationTilt, 0f, rotationTiltReturnSpeed * Time.fixedDeltaTime);
|
||||
}
|
||||
|
||||
lastRotationY = currentRotationY;
|
||||
}
|
||||
|
||||
private void UpdateAccelerationTilt()
|
||||
{
|
||||
// 가속도 계산
|
||||
float acceleration = (GetCurrentSpeed() - prevSpeed) / Time.fixedDeltaTime;
|
||||
|
||||
// 스프링 물리 시스템 구현
|
||||
float springForce = -springStiffness * currentAccelTilt; // 복원력
|
||||
float dampingForce = -springDamping * accelTiltVelocity; // 감쇠력
|
||||
float accelerationForce = -acceleration * accelTiltForce; // 가속에 의한 힘
|
||||
|
||||
// 전체 힘 계산
|
||||
float totalForce = springForce + dampingForce + accelerationForce;
|
||||
|
||||
// 가속도 계산 (F = ma, 질량은 1로 가정)
|
||||
float tiltAcceleration = totalForce;
|
||||
|
||||
// 속도 업데이트
|
||||
accelTiltVelocity += tiltAcceleration;
|
||||
accelTiltVelocity *= accelTiltDamping; // 감쇠 적용
|
||||
accelTiltVelocity *= Time.fixedDeltaTime;
|
||||
|
||||
// 위치(각도) 업데이트
|
||||
currentAccelTilt = Mathf.Lerp(currentAccelTilt, currentAccelTilt + accelTiltVelocity,
|
||||
accelTiltSpeed * Time.fixedDeltaTime);
|
||||
currentAccelTilt = Mathf.Clamp(currentAccelTilt, -maxAccelTiltAngle, maxAccelTiltAngle);
|
||||
|
||||
prevSpeed = GetCurrentSpeed();
|
||||
}
|
||||
|
||||
private void ApplyMeshTilt()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
// 회전 틸트와 가속 틸트를 조합
|
||||
// 메시에 최종 틸트 적용
|
||||
meshTransform.localRotation = originalMeshRotation * Quaternion.Euler(
|
||||
currentAccelTilt, // X축 (가속 틸트)
|
||||
0, // Y축
|
||||
currentRotationTilt // Z축 (회전 틸트)
|
||||
);
|
||||
}
|
||||
|
||||
private void UpdateWaveMotion()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
// 현재 속도에 비례하여 파도 주기 조절
|
||||
float waveSpeedFactor = 1f + (GetCurrentSpeed() / waveUnitSpeed) * speedWaveMultiplier;
|
||||
waveTime += Time.fixedDeltaTime * baseWaveFrequency * waveSpeedFactor;
|
||||
float currentSpeedByUnit = GetCurrentSpeed() / waveUnitSpeed;
|
||||
currentSpeedByUnit = Mathf.Clamp01(currentSpeedByUnit);
|
||||
float waveHeight = Mathf.Lerp(minSpeedWaveHeight, maxSpeedWaveHeight, currentSpeedByUnit);
|
||||
|
||||
currentWaveHeight = waveHeight * Mathf.Sin(waveTime + waveRandomOffset);
|
||||
}
|
||||
|
||||
private void ApplyMeshOffset()
|
||||
{
|
||||
if (meshTransform is null) return;
|
||||
|
||||
Vector3 position = originalMeshPosition + (Vector3.up * currentWaveHeight);
|
||||
meshTransform.localPosition = position;
|
||||
}
|
||||
|
||||
private float GetCurrentSpeed()
|
||||
{
|
||||
return movement.CurrentSpeed;
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a09f8b932e18409aa7f5d2a221921f45
|
||||
timeCreated: 1752575398
|
Loading…
Reference in New Issue
Block a user