using System; using System.Collections; using UnityEngine; using UnityEngine.InputSystem; using Vector2 = UnityEngine.Vector2; using Vector3 = UnityEngine.Vector3; namespace DDD { public class RestaurantPlayerMovement : RestaurantCharacterMovement { 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; private bool _isInitialized; public Action OnMoving; public Action OnDashing; private const string InputDebugLineRenderer = "DebugLine_Input"; private const string VelocityDebugLineRenderer = "DebugLine_Velocity"; private const string SpriteDefaultShader = "Sprites/Default"; private void Awake() { _rigidbody = GetComponent(); _boxCollider = GetComponent(); } 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}"); } } private void FixedUpdate() { if (_isInitialized == false) return; if (CanMove()) { Move(); } if (_playerDataSo.IsDrawLineDebug) { DrawLineDebug(); } } private void OnDestroy() { if (_playerDataSo) { _playerDataSo.MoveActionReference.action.performed -= OnMove; _playerDataSo.MoveActionReference.action.canceled -= OnMove; _playerDataSo.DashActionReference.action.performed -= OnDash; } } #if UNITY_EDITOR private void DrawLineDebug() { Vector3 origin = transform.position; if (_inputDirection != Vector3.zero) { Vector3 target = origin + _inputDirection.normalized * _playerDataSo.InputLineLength; if (_inputLineRenderer == null) { _inputLineRenderer = CreateOrGetDebugLineRenderer(InputDebugLineRenderer, _playerDataSo.InputLineSortingOrder, _playerDataSo.InputLineWidth, Color.blue); } UpdateLineRenderer(_inputLineRenderer, origin, target); _inputLineRenderer.enabled = true; } else if (_inputLineRenderer != null) { _inputLineRenderer.enabled = false; } float speed = _currentVelocity.magnitude; if (speed > _playerDataSo.VelocityMinThreshold) { Vector3 target = origin + _currentVelocity.normalized * (speed * _playerDataSo.VelocityLineScale); if (_velocityLineRenderer == null) { _velocityLineRenderer = CreateOrGetDebugLineRenderer(VelocityDebugLineRenderer, _playerDataSo.VelocityLineSortingOrder, _playerDataSo.VelocityLineWidth, Color.red); } UpdateLineRenderer(_velocityLineRenderer, origin, target); _velocityLineRenderer.enabled = true; } else if (_velocityLineRenderer != null) { _velocityLineRenderer.enabled = false; } } private LineRenderer CreateOrGetDebugLineRenderer(string name, int sortingIndex, float width, Color color) { Transform existing = transform.Find(name); if (existing != null) { var lr = existing.GetComponent(); if (lr != null) { lr.startColor = lr.endColor = color; return lr; } } var newGameObject = new GameObject(name); 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.sortingOrder = sortingIndex; lineRenderer.startWidth = lineRenderer.endWidth = width; lineRenderer.startColor = lineRenderer.endColor = color; lineRenderer.useWorldSpace = true; 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; } }