Merge pull request '레스토랑 플레이어 무브먼트 리팩토링' (#9) from feature/playermovement_refactoring into develop
Reviewed-on: #9
This commit is contained in:
commit
929f7ecf55
@ -62,7 +62,7 @@ Rigidbody:
|
||||
m_ImplicitTensor: 1
|
||||
m_UseGravity: 0
|
||||
m_IsKinematic: 0
|
||||
m_Interpolate: 0
|
||||
m_Interpolate: 1
|
||||
m_Constraints: 116
|
||||
m_CollisionDetection: 1
|
||||
--- !u!65 &732677841941379807
|
||||
|
@ -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<bool> OnMoving;
|
||||
public Action<float> 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<Rigidbody>();
|
||||
_boxCollider = GetComponent<BoxCollider>();
|
||||
InitializeComponents();
|
||||
}
|
||||
|
||||
private async void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
_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}");
|
||||
}
|
||||
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<Rigidbody>();
|
||||
_boxCollider = GetComponent<BoxCollider>();
|
||||
|
||||
#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<RestaurantPlayerDataSo>(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<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, QueryTriggerInteraction.Ignore);
|
||||
}
|
||||
|
||||
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<LineRenderer>();
|
||||
@ -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>();
|
||||
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<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;
|
||||
}
|
||||
#endif
|
||||
}
|
Loading…
Reference in New Issue
Block a user