From ae05e4ceda8bdaf36b4ca7cacf0d751704db8a20 Mon Sep 17 00:00:00 2001 From: Jeonghyeon Ha Date: Thu, 17 Jul 2025 12:04:09 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A0=88=EC=8A=A4=ED=86=A0=EB=9E=91=20?= =?UTF-8?q?=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=20=EB=AC=B4=EB=B8=8C?= =?UTF-8?q?=EB=A8=BC=ED=8A=B8=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RestaurantPlayerMovement.cs | 431 +++++++++++------- 1 file changed, 274 insertions(+), 157 deletions(-) diff --git a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerMovement.cs b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerMovement.cs index 8187f8d0f..e9c6f5775 100644 --- a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerMovement.cs +++ b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerMovement.cs @@ -9,16 +9,16 @@ namespace DDD { public class RestaurantPlayerMovement : RestaurantCharacterMovement { + #region Fields + private Rigidbody _rigidbody; private BoxCollider _boxCollider; - private RestaurantPlayerDataSo _playerDataSo; - - private LineRenderer _inputLineRenderer; - private LineRenderer _velocityLineRenderer; + private Vector3 _inputDirection; private Vector3 _currentDirection; private Vector3 _currentVelocity; + private bool _isMoving; private bool _isDashing; private bool _isDashCooldown; @@ -27,70 +27,280 @@ public class RestaurantPlayerMovement : RestaurantCharacterMovement public Action OnMoving; public Action OnDashing; - private const string InputDebugLineRenderer = "DebugLine_Input"; - private const string VelocityDebugLineRenderer = "DebugLine_Velocity"; - private const string SpriteDefaultShader = "Sprites/Default"; +#if UNITY_EDITOR + private MovementDebugVisualizer _debugVisualizer; +#endif + + #endregion + + #region Unity Lifecycle private void Awake() { - _rigidbody = GetComponent(); - _boxCollider = GetComponent(); + InitializeComponents(); } private async void Start() { - try - { - _playerDataSo = await AssetManager.LoadAsset(DataConstants.RestaurantPlayerDataSo); - - _playerDataSo.MoveActionReference.action.performed += OnMove; - _playerDataSo.MoveActionReference.action.canceled += OnMove; - _playerDataSo.DashActionReference.action.performed += OnDash; - - _isInitialized = true; - } - catch (Exception e) - { - Debug.LogError($"_playerData load failed\n{e}"); - } + await InitializePlayerData(); } private void FixedUpdate() { - if (_isInitialized == false) return; - - if (CanMove()) - { - Move(); - } + if (!_isInitialized) return; - if (_playerDataSo.IsDrawLineDebug) - { - DrawLineDebug(); - } + HandleMovement(); + +#if UNITY_EDITOR + HandleDebugVisualization(); +#endif } private void OnDestroy() { - if (_playerDataSo) + UnsubscribeFromInputEvents(); + } + + #endregion + + #region Initialization + + private void InitializeComponents() + { + _rigidbody = GetComponent(); + _boxCollider = GetComponent(); + +#if UNITY_EDITOR + _debugVisualizer = new MovementDebugVisualizer(transform); +#endif + } + + private async System.Threading.Tasks.Task InitializePlayerData() + { + try { - _playerDataSo.MoveActionReference.action.performed -= OnMove; - _playerDataSo.MoveActionReference.action.canceled -= OnMove; - _playerDataSo.DashActionReference.action.performed -= OnDash; + _playerDataSo = + await AssetManager.LoadAsset(DataConstants.RestaurantPlayerDataSo); + SubscribeToInputEvents(); + _isInitialized = true; + } + catch (Exception e) + { + Debug.LogError($"Player data load failed\n{e}"); } } - -#if UNITY_EDITOR - private void DrawLineDebug() + + private void SubscribeToInputEvents() { - Vector3 origin = transform.position; - - if (_inputDirection != Vector3.zero) + _playerDataSo.MoveActionReference.action.performed += OnMove; + _playerDataSo.MoveActionReference.action.canceled += OnMove; + _playerDataSo.DashActionReference.action.performed += OnDash; + } + + private void UnsubscribeFromInputEvents() + { + if (!_playerDataSo) return; + + _playerDataSo.MoveActionReference.action.performed -= OnMove; + _playerDataSo.MoveActionReference.action.canceled -= OnMove; + _playerDataSo.DashActionReference.action.performed -= OnDash; + } + + #endregion + + #region Movement + + private void HandleMovement() + { + if (CanMove()) { - Vector3 target = origin + _inputDirection.normalized * _playerDataSo.InputLineLength; + Move(); + } + } + + private bool CanMove() => _playerDataSo.IsMoveEnabled && !_isDashing; + + private void Move() + { + SetCurrentDirection(_inputDirection); + UpdateMovementState(); + UpdateVelocity(); + ApplyVelocity(); + } + + private void UpdateMovementState() + { + _isMoving = _inputDirection != Vector3.zero; + OnMoving?.Invoke(_isMoving); + } + + private void UpdateVelocity() + { + if (_isMoving) + { + Vector3 slideDirection = GetSlideAdjustedDirection(_inputDirection.normalized); + Vector3 targetVelocity = slideDirection * _playerDataSo.MoveSpeed; + _currentVelocity = Vector3.MoveTowards(_currentVelocity, targetVelocity, + _playerDataSo.Acceleration * Time.fixedDeltaTime); + } + else + { + _currentVelocity = Vector3.MoveTowards(_currentVelocity, Vector3.zero, + _playerDataSo.Deceleration * Time.fixedDeltaTime); + } + } + + private void ApplyVelocity() + { + _rigidbody.linearVelocity = _currentVelocity; + } + + #endregion + + #region Dash + + private void OnDash(InputAction.CallbackContext context) + { + if (CanDash()) + { + StartCoroutine(DashCoroutine()); + } + } + + private bool CanDash() => _playerDataSo.IsDashEnabled && !_isDashing && !_isDashCooldown; + + private IEnumerator DashCoroutine() + { + StartDash(); + yield return new WaitForSeconds(_playerDataSo.DashTime); + EndDash(); + yield return new WaitForSeconds(_playerDataSo.DashCooldown); + ResetDashCooldown(); + } + + private void StartDash() + { + _isDashing = true; + _isDashCooldown = true; + OnDashing?.Invoke(_playerDataSo.DashTime); + + Vector3 slideDashDirection = GetSlideAdjustedDirection(_currentDirection.normalized); + _rigidbody.linearVelocity = slideDashDirection * _playerDataSo.DashSpeed; + } + + private void EndDash() + { + _isDashing = false; + } + + private void ResetDashCooldown() + { + _isDashCooldown = false; + } + + #endregion + + #region Public Methods + + private void SetCurrentDirection(Vector3 normalDirection) + { + if (_inputDirection == Vector3.zero) return; + _currentDirection = normalDirection; + } + + public Vector3 GetCurrentDirection() => _currentDirection; + + #endregion + + #region Input Handling + + private void OnMove(InputAction.CallbackContext context) + { + Vector2 movementInput = context.ReadValue(); + _inputDirection = new Vector3(movementInput.x, 0f, movementInput.y); + } + + #endregion + + #region Collision Handling + + private Vector3 GetSlideAdjustedDirection(Vector3 inputDirection) + { + if (!TryGetCollisionInfo(inputDirection, out RaycastHit hit)) return inputDirection; + + Vector3 slide = Vector3.ProjectOnPlane(inputDirection, hit.normal).normalized; + float slideFactor = CalculateSlideFactor(inputDirection, hit.normal); + + return slideFactor < _playerDataSo.MinSlideFactorThreshold ? Vector3.zero : slide * slideFactor; + } + + private bool TryGetCollisionInfo(Vector3 direction, out RaycastHit hit) + { + Vector3 origin = _boxCollider.bounds.center; + Vector3 halfExtents = _boxCollider.bounds.extents; + float distance = Mathf.Min(_boxCollider.bounds.size.x, _boxCollider.bounds.size.z); + int layerMask = ~_playerDataSo.IgnoreSlidingLayerMask; + + return Physics.BoxCast(origin, halfExtents * _playerDataSo.BoxCastExtentScale, + direction, out hit, transform.rotation, distance, layerMask); + } + + private float CalculateSlideFactor(Vector3 direction, Vector3 normal) + { + float dot = Vector3.Dot(direction.normalized, normal); + return Mathf.Pow(1f - Mathf.Abs(dot), _playerDataSo.SlidingThreshold); + } + + #endregion + +#if UNITY_EDITOR + private void HandleDebugVisualization() + { + if (_playerDataSo.IsDrawLineDebug) + { + _debugVisualizer.UpdateVisualization(transform.position, _inputDirection, + _currentVelocity, _playerDataSo); + } + } +#endif + } + +#if UNITY_EDITOR + public class MovementDebugVisualizer + { + private const string InputDebugLineRenderer = "DebugLine_Input"; + private const string VelocityDebugLineRenderer = "DebugLine_Velocity"; + private const string SpriteDefaultShader = "Sprites/Default"; + + private LineRenderer _inputLineRenderer; + private LineRenderer _velocityLineRenderer; + private readonly Transform _transform; + + public MovementDebugVisualizer(Transform transform) + { + _transform = transform; + } + + public void UpdateVisualization(Vector3 position, Vector3 inputDirection, + Vector3 currentVelocity, RestaurantPlayerDataSo playerDataSo) + { + UpdateInputLine(position, inputDirection, playerDataSo); + UpdateVelocityLine(position, currentVelocity, playerDataSo); + } + + private void UpdateInputLine(Vector3 origin, Vector3 inputDirection, RestaurantPlayerDataSo playerDataSo) + { + if (inputDirection != Vector3.zero) + { + var target = origin + inputDirection.normalized * playerDataSo.InputLineLength; + if (_inputLineRenderer == null) { - _inputLineRenderer = CreateOrGetDebugLineRenderer(InputDebugLineRenderer, _playerDataSo.InputLineSortingOrder, _playerDataSo.InputLineWidth, Color.blue); + _inputLineRenderer = CreateDebugLineRenderer( + InputDebugLineRenderer, + playerDataSo.InputLineSortingOrder, + playerDataSo.InputLineWidth, + Color.blue); } UpdateLineRenderer(_inputLineRenderer, origin, target); @@ -100,15 +310,22 @@ private void DrawLineDebug() { _inputLineRenderer.enabled = false; } - - float speed = _currentVelocity.magnitude; - if (speed > _playerDataSo.VelocityMinThreshold) + } + + private void UpdateVelocityLine(Vector3 origin, Vector3 velocity, RestaurantPlayerDataSo playerDataSo) + { + float speed = velocity.magnitude; + if (speed > playerDataSo.VelocityMinThreshold) { - Vector3 target = origin + _currentVelocity.normalized * (speed * _playerDataSo.VelocityLineScale); + Vector3 target = origin + velocity.normalized * (speed * playerDataSo.VelocityLineScale); if (_velocityLineRenderer == null) { - _velocityLineRenderer = CreateOrGetDebugLineRenderer(VelocityDebugLineRenderer, _playerDataSo.VelocityLineSortingOrder, _playerDataSo.VelocityLineWidth, Color.red); + _velocityLineRenderer = CreateDebugLineRenderer( + VelocityDebugLineRenderer, + playerDataSo.VelocityLineSortingOrder, + playerDataSo.VelocityLineWidth, + Color.red); } UpdateLineRenderer(_velocityLineRenderer, origin, target); @@ -119,10 +336,10 @@ private void DrawLineDebug() _velocityLineRenderer.enabled = false; } } - - private LineRenderer CreateOrGetDebugLineRenderer(string name, int sortingIndex, float width, Color color) + + private LineRenderer CreateDebugLineRenderer(string name, int sortingIndex, float width, Color color) { - Transform existing = transform.Find(name); + Transform existing = _transform.Find(name); if (existing != null) { var lr = existing.GetComponent(); @@ -134,12 +351,12 @@ private LineRenderer CreateOrGetDebugLineRenderer(string name, int sortingIndex, } var newGameObject = new GameObject(name); - newGameObject.transform.SetParent(transform); + newGameObject.transform.SetParent(_transform); newGameObject.transform.localPosition = Vector3.zero; var lineRenderer = newGameObject.AddComponent(); lineRenderer.positionCount = 2; - lineRenderer.material = new Material(Shader.Find(SpriteDefaultShader)); // URP 호환 + lineRenderer.material = new Material(Shader.Find(SpriteDefaultShader)); lineRenderer.sortingOrder = sortingIndex; lineRenderer.startWidth = lineRenderer.endWidth = width; lineRenderer.startColor = lineRenderer.endColor = color; @@ -147,112 +364,12 @@ private LineRenderer CreateOrGetDebugLineRenderer(string name, int sortingIndex, return lineRenderer; } - + private void UpdateLineRenderer(LineRenderer lr, Vector3 start, Vector3 end) { lr.SetPosition(0, start); lr.SetPosition(1, end); } -#endif - - public void SetCurrentDirection(Vector3 normalDirection) - { - if (_inputDirection == Vector3.zero) return; - - _currentDirection = normalDirection; - } - - private void OnMove(InputAction.CallbackContext context) - { - Vector2 movementInput = context.ReadValue(); - _inputDirection = new Vector3(movementInput.x, 0f, movementInput.y); - } - - private bool CanMove() - { - return _playerDataSo.IsMoveEnabled && _isDashing == false; - } - - private void Move() - { - SetCurrentDirection(_inputDirection); - - _isMoving = _inputDirection != Vector3.zero; - OnMoving?.Invoke(_isMoving); - - if (_isMoving) - { - Vector3 slideDirection = GetSlideAdjustedDirection(_inputDirection.normalized); - Vector3 targetVelocity = slideDirection * _playerDataSo.MoveSpeed; - _currentVelocity = Vector3.MoveTowards(_currentVelocity, targetVelocity, _playerDataSo.Acceleration * Time.fixedDeltaTime); - } - else - { - _currentVelocity = Vector3.MoveTowards(_currentVelocity, Vector3.zero, _playerDataSo.Deceleration * Time.fixedDeltaTime); - } - - _rigidbody.linearVelocity = _currentVelocity; - } - - private Vector3 GetSlideAdjustedDirection(Vector3 inputDirection) - { - Vector3 origin = _boxCollider.bounds.center; - Vector3 halfExtents = _boxCollider.bounds.extents; - Quaternion rotation = transform.rotation; - float distance = _boxCollider.bounds.size.x <= _boxCollider.bounds.size.z - ? _boxCollider.bounds.size.x - : _boxCollider.bounds.size.z; - int layerMask = ~_playerDataSo.IgnoreSlidingLayerMask; - - if (Physics.BoxCast(origin, halfExtents * _playerDataSo.BoxCastExtentScale, inputDirection, out RaycastHit hit, rotation, distance, layerMask)) - { - Vector3 slide = Vector3.ProjectOnPlane(inputDirection, hit.normal).normalized; - - float dot = Vector3.Dot(inputDirection.normalized, hit.normal); - float slideFactor = Mathf.Pow(1f - Mathf.Abs(dot), _playerDataSo.SlidingThreshold); - - if (slideFactor < _playerDataSo.MinSlideFactorThreshold) return Vector3.zero; - - return slide * slideFactor; - } - - return inputDirection; - } - - private void OnDash(InputAction.CallbackContext context) - { - if (CanDash()) - { - StartCoroutine(DashCoroutine()); - } - } - - private bool CanDash() - { - return _playerDataSo.IsDashEnabled && _isDashing == false && _isDashCooldown == false; - } - - private IEnumerator DashCoroutine() - { - _isDashing = true; - _isDashCooldown = true; - - OnDashing?.Invoke(_playerDataSo.DashTime); - - Vector3 currentDirection = _currentDirection.normalized; - Vector3 slideDashDirection = GetSlideAdjustedDirection(currentDirection); - - Vector3 dashVelocity = slideDashDirection * _playerDataSo.DashSpeed; - _rigidbody.linearVelocity = dashVelocity; - - yield return new WaitForSeconds(_playerDataSo.DashTime); - - _isDashing = false; - - yield return new WaitForSeconds(_playerDataSo.DashCooldown); - _isDashCooldown = false; - } - - public Vector3 GetCurrentDirection() => _currentDirection; } +#endif } \ No newline at end of file