레스토랑 플레이어 무브먼트 리팩토링

This commit is contained in:
Jeonghyeon Ha 2025-07-17 12:04:09 +09:00
parent 7b62494ad5
commit ae05e4ceda

View File

@ -9,16 +9,16 @@ namespace DDD
{ {
public class RestaurantPlayerMovement : RestaurantCharacterMovement public class RestaurantPlayerMovement : RestaurantCharacterMovement
{ {
#region Fields
private Rigidbody _rigidbody; private Rigidbody _rigidbody;
private BoxCollider _boxCollider; private BoxCollider _boxCollider;
private RestaurantPlayerDataSo _playerDataSo; private RestaurantPlayerDataSo _playerDataSo;
private LineRenderer _inputLineRenderer;
private LineRenderer _velocityLineRenderer;
private Vector3 _inputDirection; private Vector3 _inputDirection;
private Vector3 _currentDirection; private Vector3 _currentDirection;
private Vector3 _currentVelocity; private Vector3 _currentVelocity;
private bool _isMoving; private bool _isMoving;
private bool _isDashing; private bool _isDashing;
private bool _isDashCooldown; private bool _isDashCooldown;
@ -27,70 +27,280 @@ public class RestaurantPlayerMovement : RestaurantCharacterMovement
public Action<bool> OnMoving; public Action<bool> OnMoving;
public Action<float> OnDashing; public Action<float> OnDashing;
private const string InputDebugLineRenderer = "DebugLine_Input"; #if UNITY_EDITOR
private const string VelocityDebugLineRenderer = "DebugLine_Velocity"; private MovementDebugVisualizer _debugVisualizer;
private const string SpriteDefaultShader = "Sprites/Default"; #endif
#endregion
#region Unity Lifecycle
private void Awake() private void Awake()
{ {
_rigidbody = GetComponent<Rigidbody>(); InitializeComponents();
_boxCollider = GetComponent<BoxCollider>();
} }
private async void Start() private async void Start()
{ {
try await InitializePlayerData();
{
_playerDataSo = await AssetManager.LoadAsset<RestaurantPlayerDataSo>(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() private void FixedUpdate()
{ {
if (_isInitialized == false) return; if (!_isInitialized) return;
if (CanMove()) HandleMovement();
{
Move();
}
if (_playerDataSo.IsDrawLineDebug) #if UNITY_EDITOR
{ HandleDebugVisualization();
DrawLineDebug(); #endif
}
} }
private void OnDestroy() private void OnDestroy()
{ {
if (_playerDataSo) UnsubscribeFromInputEvents();
}
#endregion
#region Initialization
private void InitializeComponents()
{ {
_rigidbody = GetComponent<Rigidbody>();
_boxCollider = GetComponent<BoxCollider>();
#if UNITY_EDITOR
_debugVisualizer = new MovementDebugVisualizer(transform);
#endif
}
private async System.Threading.Tasks.Task InitializePlayerData()
{
try
{
_playerDataSo =
await AssetManager.LoadAsset<RestaurantPlayerDataSo>(DataConstants.RestaurantPlayerDataSo);
SubscribeToInputEvents();
_isInitialized = true;
}
catch (Exception e)
{
Debug.LogError($"Player data load failed\n{e}");
}
}
private void SubscribeToInputEvents()
{
_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.performed -= OnMove;
_playerDataSo.MoveActionReference.action.canceled -= OnMove; _playerDataSo.MoveActionReference.action.canceled -= OnMove;
_playerDataSo.DashActionReference.action.performed -= OnDash; _playerDataSo.DashActionReference.action.performed -= OnDash;
} }
#endregion
#region Movement
private void HandleMovement()
{
if (CanMove())
{
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<Vector2>();
_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 #if UNITY_EDITOR
private void DrawLineDebug() public class MovementDebugVisualizer
{ {
Vector3 origin = transform.position; private const string InputDebugLineRenderer = "DebugLine_Input";
private const string VelocityDebugLineRenderer = "DebugLine_Velocity";
private const string SpriteDefaultShader = "Sprites/Default";
if (_inputDirection != Vector3.zero) private LineRenderer _inputLineRenderer;
private LineRenderer _velocityLineRenderer;
private readonly Transform _transform;
public MovementDebugVisualizer(Transform transform)
{ {
Vector3 target = origin + _inputDirection.normalized * _playerDataSo.InputLineLength; _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) 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); UpdateLineRenderer(_inputLineRenderer, origin, target);
@ -100,15 +310,22 @@ private void DrawLineDebug()
{ {
_inputLineRenderer.enabled = false; _inputLineRenderer.enabled = false;
} }
}
float speed = _currentVelocity.magnitude; private void UpdateVelocityLine(Vector3 origin, Vector3 velocity, RestaurantPlayerDataSo playerDataSo)
if (speed > _playerDataSo.VelocityMinThreshold)
{ {
Vector3 target = origin + _currentVelocity.normalized * (speed * _playerDataSo.VelocityLineScale); float speed = velocity.magnitude;
if (speed > playerDataSo.VelocityMinThreshold)
{
Vector3 target = origin + velocity.normalized * (speed * playerDataSo.VelocityLineScale);
if (_velocityLineRenderer == null) 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); UpdateLineRenderer(_velocityLineRenderer, origin, target);
@ -120,9 +337,9 @@ private void DrawLineDebug()
} }
} }
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) if (existing != null)
{ {
var lr = existing.GetComponent<LineRenderer>(); var lr = existing.GetComponent<LineRenderer>();
@ -134,12 +351,12 @@ private LineRenderer CreateOrGetDebugLineRenderer(string name, int sortingIndex,
} }
var newGameObject = new GameObject(name); var newGameObject = new GameObject(name);
newGameObject.transform.SetParent(transform); newGameObject.transform.SetParent(_transform);
newGameObject.transform.localPosition = Vector3.zero; newGameObject.transform.localPosition = Vector3.zero;
var lineRenderer = newGameObject.AddComponent<LineRenderer>(); var lineRenderer = newGameObject.AddComponent<LineRenderer>();
lineRenderer.positionCount = 2; lineRenderer.positionCount = 2;
lineRenderer.material = new Material(Shader.Find(SpriteDefaultShader)); // URP 호환 lineRenderer.material = new Material(Shader.Find(SpriteDefaultShader));
lineRenderer.sortingOrder = sortingIndex; lineRenderer.sortingOrder = sortingIndex;
lineRenderer.startWidth = lineRenderer.endWidth = width; lineRenderer.startWidth = lineRenderer.endWidth = width;
lineRenderer.startColor = lineRenderer.endColor = color; lineRenderer.startColor = lineRenderer.endColor = color;
@ -153,106 +370,6 @@ private void UpdateLineRenderer(LineRenderer lr, Vector3 start, Vector3 end)
lr.SetPosition(0, start); lr.SetPosition(0, start);
lr.SetPosition(1, end); lr.SetPosition(1, end);
} }
}
#endif #endif
public void SetCurrentDirection(Vector3 normalDirection)
{
if (_inputDirection == Vector3.zero) return;
_currentDirection = normalDirection;
}
private void OnMove(InputAction.CallbackContext context)
{
Vector2 movementInput = context.ReadValue<Vector2>();
_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;
}
} }