From ccf8772ccddf827ef9ff2d9e6364ea139d83ce09 Mon Sep 17 00:00:00 2001 From: Jeonghyeon Ha Date: Tue, 15 Jul 2025 19:24:43 +0900 Subject: [PATCH] =?UTF-8?q?DDD-43=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/0_Voyage/Ship/Player/PlayerShip.prefab | 12 +- .../_Scripts/Ship/VoyagePlayerShipMovement.cs | 691 ++++++++++-------- 2 files changed, 388 insertions(+), 315 deletions(-) diff --git a/Assets/0_Voyage/Ship/Player/PlayerShip.prefab b/Assets/0_Voyage/Ship/Player/PlayerShip.prefab index 69715f9e6..de2a26ea0 100644 --- a/Assets/0_Voyage/Ship/Player/PlayerShip.prefab +++ b/Assets/0_Voyage/Ship/Player/PlayerShip.prefab @@ -106,17 +106,17 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: maxSpeed: 20 - rotationSpeed: 180 - minRotationSpeed: 60 - rotationAccelerationRate: 5 accelerationRate: 1 - minSpeedThreshold: 0.1 dragFactor: 0.98 + minSpeedThreshold: 0.1 + rotationSpeed: 270 + minRotationSpeed: 90 + rotationAccelerationRate: 5 turnSpeedPenalty: 0.5 maxTurnAngle: 180 maxRotationTiltAngle: 15 rotationTiltSpeed: 5 - RotationTiltReturnSpeed: 3 + rotationTiltReturnSpeed: 3 angularVelocityMultiplier: 2 maxAccelTiltAngle: 15 accelTiltForce: 15 @@ -130,7 +130,7 @@ MonoBehaviour: speedWaveMultiplier: 5 randomWaveOffset: 0.5 waveUnitSpeed: 10 - meshObjectName: Ship_Mesh + meshTransform: {fileID: 2507610487897935131} showDebugVisuals: 1 debugLineLength: 5 debugLineWidth: 0.1 diff --git a/Assets/0_Voyage/_Scripts/Ship/VoyagePlayerShipMovement.cs b/Assets/0_Voyage/_Scripts/Ship/VoyagePlayerShipMovement.cs index 70012c929..95691d6f5 100644 --- a/Assets/0_Voyage/_Scripts/Ship/VoyagePlayerShipMovement.cs +++ b/Assets/0_Voyage/_Scripts/Ship/VoyagePlayerShipMovement.cs @@ -1,87 +1,90 @@ using UnityEngine; using UnityEngine.InputSystem; -using UnityEngine.Serialization; +/// +/// 플레이어 배의 움직임을 제어하는 컴포넌트 +/// 속도, 회전, 틸트, 파도 효과 등을 관리합니다. +/// public class VoyagePlayerShipMovement : MonoBehaviour { - [Header("Movement Settings")] + #region Inspector Fields + + [Header("기본 이동 설정")] + [Tooltip("배의 최대 이동 속도")] [SerializeField] private float maxSpeed = 20f; - [SerializeField] private float rotationSpeed = 180f; - [SerializeField] private float minRotationSpeed = 60f; - [SerializeField] private float rotationAccelerationRate = 5f; [SerializeField] private float accelerationRate = 1f; - [SerializeField] private float minSpeedThreshold = 0.1f; [SerializeField] private float dragFactor = 0.98f; - private Vector3 _currentVelocity; - private Vector2 _currentInput; - private float _currentRotationSpeed; - private float _targetSpeed; - private float _currentSpeed; - - [Header("Turn Settings")] - [SerializeField] private float turnSpeedPenalty = 0.5f; // 선회 시 감속 정도 (0: 감속 없음, 1: 완전 정지) - [SerializeField] private float maxTurnAngle = 180f; // 최대 감속이 적용되는 각도 - - // Rotation Tilt - [Header("Rotation Tilt Settings")] + [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; + + [Header("회전 틸트 설정")] [SerializeField] private float maxRotationTiltAngle = 15f; [SerializeField] private float rotationTiltSpeed = 5f; - [SerializeField] private float RotationTiltReturnSpeed = 3f; // 원래 자세로 돌아오는 속도 - [SerializeField] private float angularVelocityMultiplier = 2f; // 각속도 영향력 - private float _currentRotationTilt = 0f; - private float _lastRotationY; // 이전 프레임의 Y축 회전값 - private float _currentAngularVelocity; // 현재 각속도 + [SerializeField] private float rotationTiltReturnSpeed = 3f; + [SerializeField] private float angularVelocityMultiplier = 2f; - // Acceleration Tilt - [Header("Acceleration Tilt Settings")] - [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; // 스프링 감쇠 - private float _currentAccelTilt; - private float _accelTiltVelocity; - private float _prevSpeed; - - // Wave offset - [Header("Wave Settings")] - [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; // 기준 속력 - private float _waveTime; - private float _waveRandomOffset; - private float _currentWaveHeight; + [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; + private float currentRotationSpeed; + private float targetSpeed; + private float currentSpeed; + + // 회전 틸트 관련 + 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; + + // 메시 원본 상태 + private Quaternion originalMeshRotation; + private Vector3 originalMeshPosition; + + #endregion + + #region Unity Messages - - [Header("Mesh Settings")] - [SerializeField] private string meshObjectName = "Ship_Mesh"; - private Transform _meshTransform; - - - private Quaternion _originalMeshRotation; - private Vector3 _originalMeshPosition; - private void Start() { - _meshTransform = transform.Find(meshObjectName); - if (_meshTransform == null) - { - Debug.LogError($"메시 오브젝트를 찾을 수 없습니다: {meshObjectName}"); - enabled = false; - return; - } - - _originalMeshPosition = _meshTransform.localPosition; - _originalMeshRotation = _meshTransform.localRotation; - _lastRotationY = transform.eulerAngles.y; - _waveTime = 0f; - _waveRandomOffset = Random.Range(-randomWaveOffset, randomWaveOffset); - + InitializeMeshTransform(); + InitializeWaveEffect(); #if UNITY_EDITOR InitializeDebugVisuals(); #endif @@ -89,118 +92,154 @@ public class VoyagePlayerShipMovement : MonoBehaviour private void FixedUpdate() { - if (_currentInput.magnitude > minSpeedThreshold) + UpdateMovement(); + UpdateVisualEffects(); +#if UNITY_EDITOR + UpdateDebugVisuals(); +#endif + } + + private void OnValidate() + { + ValidateMeshTransform(); + } + + #endregion + + #region Movement Methods + + private void UpdateMovement() + { + if (IsMoving()) { HandleMovement(); HandleRotation(); } else { - // 입력이 없을 때는 서서히 감속 - _currentSpeed = Mathf.Lerp(_currentSpeed, 0f, accelerationRate * Time.fixedDeltaTime); - _currentRotationSpeed = 0; + DecelerateMovement(); } + ApplyDrag(); ApplyMovement(); + } - // Cosmetic mesh tilting - UpdateMeshRotationTilt(); - UpdateAccelerationTilt(); - ApplyMeshTilt(); - // Cosmetic mesh wave offset - UpdateWaveMotion(); - ApplyMeshOffset(); - -#if UNITY_EDITOR - UpdateDebugVisuals(); -#endif + private bool IsMoving() + { + return currentInput.magnitude > minSpeedThreshold; } private void HandleMovement() { - // 기본 목표 속도 계산 (입력 크기에 비례) - float baseTargetSpeed = Mathf.Clamp01(_currentInput.magnitude) * maxSpeed; + float baseTargetSpeed = CalculateBaseTargetSpeed(); + float turnPenaltyFactor = CalculateTurnPenaltyFactor(); + + targetSpeed = baseTargetSpeed * turnPenaltyFactor; + currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, accelerationRate * Time.fixedDeltaTime); - // 현재 방향과 목표 방향 사이의 각도 계산 - Vector3 inputDirection = new Vector3(_currentInput.x, 0, _currentInput.y).normalized; - float angleDifference = Vector3.Angle(transform.forward, inputDirection); - - // 각도에 따른 속도 페널티 계산 (각도가 클수록 더 큰 감속) - float turnPenaltyFactor = 1f - (angleDifference / maxTurnAngle * turnSpeedPenalty); - turnPenaltyFactor = Mathf.Clamp01(turnPenaltyFactor); - - // 최종 목표 속도 계산 (기본 속도에 선회 페널티 적용) - _targetSpeed = baseTargetSpeed * turnPenaltyFactor; - - // 현재 속도를 목표 속도로 부드럽게 보간 - _currentSpeed = Mathf.Lerp(_currentSpeed, _targetSpeed, accelerationRate * Time.fixedDeltaTime); - - // 최소 임계값 이하면 완전히 정지 - if (_currentSpeed < minSpeedThreshold && _targetSpeed < minSpeedThreshold) + if (ShouldStop()) { - _currentSpeed = 0f; + currentSpeed = 0f; } - // 현재 바라보는 방향으로 속도 벡터 업데이트 - _currentVelocity = transform.forward * _currentSpeed; + 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 (_currentInput.magnitude > minSpeedThreshold) + if (IsMoving()) { - Vector3 inputDirection = new Vector3(_currentInput.x, 0, _currentInput.y).normalized; + Vector3 inputDirection = new Vector3(currentInput.x, 0, currentInput.y).normalized; Quaternion targetRotation = Quaternion.LookRotation(inputDirection, Vector3.up); - + // 회전 속도를 현재 속도에 비례하도록 설정 - float desiredRotationSpeed = rotationSpeed * (_currentSpeed / maxSpeed); + float desiredRotationSpeed = rotationSpeed * (currentSpeed / maxSpeed); desiredRotationSpeed = Mathf.Max(desiredRotationSpeed, minRotationSpeed); - _currentRotationSpeed = Mathf.Lerp(_currentRotationSpeed, desiredRotationSpeed, rotationAccelerationRate * Time.fixedDeltaTime); - + currentRotationSpeed = Mathf.Lerp(currentRotationSpeed, desiredRotationSpeed, + rotationAccelerationRate * Time.fixedDeltaTime); + // 기본 회전 적용 (오브젝트 전체) transform.rotation = Quaternion.RotateTowards( transform.rotation, targetRotation, - _currentRotationSpeed * Time.fixedDeltaTime + currentRotationSpeed * Time.fixedDeltaTime ); } } private void UpdateMeshRotationTilt() { - if (_meshTransform is null) return; + if (meshTransform is null) return; // 현재 Y축 회전값과 각속도 계산 float currentRotationY = transform.eulerAngles.y; - float deltaRotation = Mathf.DeltaAngle(_lastRotationY, currentRotationY); - _currentAngularVelocity = deltaRotation / Time.fixedDeltaTime; + float deltaRotation = Mathf.DeltaAngle(lastRotationY, currentRotationY); + currentAngularVelocity = deltaRotation / Time.fixedDeltaTime; // 목표 틸트 각도 계산 - float targetTilt = -_currentAngularVelocity * angularVelocityMultiplier; + float targetTilt = -currentAngularVelocity * angularVelocityMultiplier; targetTilt = Mathf.Clamp(targetTilt, -maxRotationTiltAngle, maxRotationTiltAngle); // 틸트 적용 또는 복귀 - if (Mathf.Abs(_currentAngularVelocity) > 0.1f) + if (Mathf.Abs(currentAngularVelocity) > 0.1f) { - _currentRotationTilt = Mathf.Lerp(_currentRotationTilt, targetTilt, rotationTiltSpeed * Time.fixedDeltaTime); + currentRotationTilt = + Mathf.Lerp(currentRotationTilt, targetTilt, rotationTiltSpeed * Time.fixedDeltaTime); } else { // 입력이 없을 때는 원래 자세로 천천히 복귀 - _currentRotationTilt = Mathf.Lerp(_currentRotationTilt, 0f, RotationTiltReturnSpeed * Time.fixedDeltaTime); + currentRotationTilt = Mathf.Lerp(currentRotationTilt, 0f, rotationTiltReturnSpeed * Time.fixedDeltaTime); } - - _lastRotationY = currentRotationY; + + lastRotationY = currentRotationY; } private void UpdateAccelerationTilt() { // 가속도 계산 - float acceleration = (_currentSpeed - _prevSpeed) / Time.fixedDeltaTime; + float acceleration = (currentSpeed - prevSpeed) / Time.fixedDeltaTime; // 스프링 물리 시스템 구현 - float springForce = -springStiffness * _currentAccelTilt; // 복원력 - float dampingForce = -springDamping * _accelTiltVelocity; // 감쇠력 + float springForce = -springStiffness * currentAccelTilt; // 복원력 + float dampingForce = -springDamping * accelTiltVelocity; // 감쇠력 float accelerationForce = -acceleration * accelTiltForce; // 가속에 의한 힘 // 전체 힘 계산 @@ -210,90 +249,127 @@ public class VoyagePlayerShipMovement : MonoBehaviour float tiltAcceleration = totalForce; // 속도 업데이트 - _accelTiltVelocity += tiltAcceleration; - _accelTiltVelocity *= accelTiltDamping; // 감쇠 적용 - _accelTiltVelocity *= Time.fixedDeltaTime; + accelTiltVelocity += tiltAcceleration; + accelTiltVelocity *= accelTiltDamping; // 감쇠 적용 + accelTiltVelocity *= Time.fixedDeltaTime; // 위치(각도) 업데이트 - _currentAccelTilt = Mathf.Lerp(_currentAccelTilt, _currentAccelTilt + _accelTiltVelocity, accelTiltSpeed * Time.fixedDeltaTime); - _currentAccelTilt = Mathf.Clamp(_currentAccelTilt, -maxAccelTiltAngle, maxAccelTiltAngle); + currentAccelTilt = Mathf.Lerp(currentAccelTilt, currentAccelTilt + accelTiltVelocity, + accelTiltSpeed * Time.fixedDeltaTime); + currentAccelTilt = Mathf.Clamp(currentAccelTilt, -maxAccelTiltAngle, maxAccelTiltAngle); - _prevSpeed = _currentSpeed; + prevSpeed = currentSpeed; } private void ApplyMeshTilt() { - if (_meshTransform is null) return; + if (meshTransform is null) return; // 회전 틸트와 가속 틸트를 조합 // 메시에 최종 틸트 적용 - _meshTransform.localRotation = _originalMeshRotation * Quaternion.Euler( - _currentAccelTilt, // X축 (가속 틸트) + meshTransform.localRotation = originalMeshRotation * Quaternion.Euler( + currentAccelTilt, // X축 (가속 틸트) 0, // Y축 - _currentRotationTilt // Z축 (회전 틸트) + currentRotationTilt // Z축 (회전 틸트) ); } private void UpdateWaveMotion() { - if (_meshTransform is null) return; - + if (meshTransform is null) return; + // 현재 속도에 비례하여 파도 주기 조절 - float waveSpeedFactor = 1f + (_currentSpeed / waveUnitSpeed) * speedWaveMultiplier; - _waveTime += Time.fixedDeltaTime * baseWaveFrequency * waveSpeedFactor; - float currentSpeedByUnit = _currentSpeed / waveUnitSpeed; + 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); + + currentWaveHeight = waveHeight * Mathf.Sin(waveTime + waveRandomOffset); } private void ApplyMeshOffset() { - if (_meshTransform is null) return; + if (meshTransform is null) return; - Vector3 position = _originalMeshPosition + (Vector3.up * _currentWaveHeight); - _meshTransform.localPosition = position; + Vector3 position = originalMeshPosition + (Vector3.up * currentWaveHeight); + meshTransform.localPosition = position; } private void ApplyDrag() { - _currentSpeed *= dragFactor; + currentSpeed *= dragFactor; // 최소 속도 이하면 완전히 정지 - if (_currentSpeed < minSpeedThreshold) + if (currentSpeed < minSpeedThreshold) { - _currentSpeed = 0f; + currentSpeed = 0f; } // 현재 방향으로 감속된 속도 적용 - _currentVelocity = transform.forward * _currentSpeed; + currentVelocity = transform.forward * currentSpeed; } - + private void ApplyMovement() { - transform.position += _currentVelocity * Time.fixedDeltaTime; + transform.position += currentVelocity * Time.fixedDeltaTime; } + private void DecelerateMovement() + { + // 입력이 없을 때는 서서히 감속 + currentSpeed = Mathf.Lerp(currentSpeed, 0f, accelerationRate * Time.fixedDeltaTime); + currentRotationSpeed = 0; + } + + #endregion + + #region Input Handling + public void OnMove(InputAction.CallbackContext context) { - _currentInput = context.ReadValue(); + currentInput = context.ReadValue(); } - - private void OnValidate() + + #endregion + + #region Initialization + + private void InitializeMeshTransform() { - // 에디터에서 메시 오브젝트 이름이 변경될 때 자동으로 찾기 - if (Application.isEditor && !Application.isPlaying) + if (meshTransform is null) { - _meshTransform = transform.Find(meshObjectName); + 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; @@ -304,173 +380,170 @@ public class VoyagePlayerShipMovement : MonoBehaviour 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) + private void InitializeDebugVisuals() { - Vector3[] arcPoints = new Vector3[10]; - float radius = debugLineLength * 1f; - float angleStep = _currentRotationSpeed * 1f / (arcPoints.Length - 1); - - for (int i = 0; i < arcPoints.Length; i++) + 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) { - float angle = angleStep * i; - Vector3 point = start + Quaternion.Euler(0, angle, 0) * transform.forward * radius; - arcPoints[i] = point; + 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); } - - _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++) + else { - float angle = angleStep * i; - Vector3 point = start + Quaternion.Euler(0, angle, 0) * transform.forward * radius; - arcPoints[i] = point; + _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); } - _rotationDeltaLineRenderer.positionCount = arcPoints.Length; - _rotationDeltaLineRenderer.SetPositions(arcPoints); + 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; + } } - else + + private void UpdateTiltLine() { - _rotationDeltaLineRenderer.positionCount = 0; + Vector3 start = transform.position + Vector3.up * 1.5f; + Vector3 tiltDirection = meshTransform.up; + DrawLine(_TiltLineRenderer, start, start + tiltDirection * debugLineLength * 0.4f); } -} -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++) + private void UpdateWaveVisualization() { - 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; + // 현재 파도 높이 표시 + 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); } - - _wavePatternLineRenderer.SetPositions(wavePoints); -} -private LineRenderer CreateLineRenderer(string name, Color color) -{ - GameObject lineObj = new GameObject(name); - lineObj.transform.SetParent(transform); - LineRenderer line = lineObj.AddComponent(); - - 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 LineRenderer CreateLineRenderer(string name, Color color) + { + GameObject lineObj = new GameObject(name); + lineObj.transform.SetParent(transform); + LineRenderer line = lineObj.AddComponent(); -private void DrawLine(LineRenderer line, Vector3 start, Vector3 end) -{ - if (line == null) return; - line.positionCount = 2; - line.SetPosition(0, start); - line.SetPosition(1, end); -} + 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 - } \ No newline at end of file