using System.Collections; using BlueWater.Audios; using BlueWater.Interfaces; using BlueWater.Utility; using Sirenix.OdinInspector; using UnityEngine; namespace BlueWater.Players.Combat { public class CombatMovement : MonoBehaviour, IDashable, IPhysicMovable { // Variables #region Variables // Components [field: SerializeField] public Rigidbody Rigidbody { get; private set; } [SerializeField] private Transform _visualLook; [SerializeField] private AnimationController _animationController; private IComboAttackable _comboAttackable; private ISkillHandler _skillHandler; private IStunnable _stunnable; // Move [field: SerializeField, Range(1f, 10f), Tooltip("이동 속도")] public float MoveSpeed { get; private set; } = 7f; public float MoveSpeedCoefficient { get; private set; }= 1f; public bool IsMoveEnabled { get; private set; } = true; private bool _isMoving; public bool IsMoving { get => _isMoving; private set { _isMoving = value; _animationController.SetAnimationParameter("isMoving", _isMoving); } } private Vector3 _inputDirection; private Vector3 _currentDirection = Vector3.back; public Vector3 CurrentDirection { get => _currentDirection; private set { if (value == Vector3.zero) return; _currentDirection = value; _animationController.SetAnimationParameter("xDirection", _currentDirection.x); _animationController.SetAnimationParameter("zDirection", _currentDirection.z); } } private float _finalSpeed; // Dash [field: SerializeField, Range(1f, 50f), Tooltip("대쉬 속도")] public float DashSpeed { get; private set; } = 20f; [field: SerializeField, Range(0.1f, 1f), Tooltip("대쉬 시간")] public float DashTime { get; private set; } = 0.2f; [field: SerializeField, Range(0f, 5f), Tooltip("대쉬 쿨타임")] public float DashCooldown { get; private set; } = 0.5f; public bool IsDashEnabled { get; private set; } = true; private bool _isDashing; public bool IsDashing { get => _isDashing; private set { _isDashing = value; _animationController.SetAnimationParameter("isDashing", _isDashing); } } public bool IsDashCoolDownActive { get; private set; } private Coroutine _dashCoroutine; #endregion // Unity events #region Unity events private void Awake() { InitializeComponents(); } private void Update() { FlipVisualLook(); } private void FixedUpdate() { if (!CanMove()) return; Move(); } #endregion // Initialize Methods #region Initialize Methods [Button("컴포넌트 초기화")] private void InitializeComponents() { Rigidbody = GetComponent(); _visualLook = transform.Find("VisualLook"); _animationController = GetComponent(); _comboAttackable = GetComponent(); _skillHandler = GetComponent(); _stunnable = GetComponent(); } #endregion // Methods #region Methods // Event methods public void InputMovement(Vector2 movementInput) { _inputDirection = new Vector3(movementInput.x, 0, movementInput.y).normalized; } public void SetMoveSpeedCoefficient(float value) => MoveSpeedCoefficient = value; public void ResetMoveSpeedCoefficient() => MoveSpeedCoefficient = 1f; public void SetCurrentDirection(Vector3 normalDirection) => CurrentDirection = normalDirection; // Methods private void FlipVisualLook() { var localScale = _visualLook.localScale; localScale.x = CurrentDirection.x switch { > 0.01f => Mathf.Abs(localScale.x), < -0.01f => -Mathf.Abs(localScale.x), _ => localScale.x }; _visualLook.localScale = localScale; } // Move public bool CanMove() { if (!IsMoveEnabled || IsDashing) return false; var isActivatingSkill = _skillHandler?.IsActivatingSkill ?? false; var isStunned = _stunnable?.IsStunned ?? false; var isAttacking = _comboAttackable?.CurrentComboAttackCount > 0; return !isActivatingSkill && !isStunned && !isAttacking; } public void AddForce(Vector3 force, ForceMode forceMode) { if (IsDashing) return; Rigidbody.AddForce(force, forceMode); } public void Move() { CurrentDirection = _inputDirection; IsMoving = _inputDirection != Vector3.zero; var finalVelocity = _inputDirection * (MoveSpeed * MoveSpeedCoefficient * Time.deltaTime); Rigidbody.MovePosition(transform.position + finalVelocity); } // Dash public bool CanDash() { if (!IsDashEnabled || IsDashing || IsDashCoolDownActive) return false; var isActivatingSkill = _skillHandler?.IsActivatingSkill ?? false; var isStunned = _stunnable?.IsStunned ?? false; if (isActivatingSkill || isStunned) return false; var isAttacking = _comboAttackable?.CurrentComboAttackCount > 0; if (isAttacking) { _comboAttackable.EndAttack(); } return true; } public void Dash() { if (!CanDash()) return; Utils.StartUniqueCoroutine(this, ref _dashCoroutine, DashCoroutine()); } private IEnumerator DashCoroutine() { IsDashing = true; IsDashCoolDownActive = true; var dashDirection = CurrentDirection; var animationStarted = false; yield return StartCoroutine(_animationController.WaitForAnimationToRun("DashState", success => animationStarted = success)); if (!animationStarted) { EndDash(0); yield break; } _animationController.SetCurrentAnimationSpeed(DashTime); AudioManager.Instance.PlaySfx("CombatPlayerDash"); while (_animationController.IsComparingCurrentAnimation("DashState") && _animationController.GetCurrentAnimationNormalizedTime() < 1f) { var finalVelocity = transform.position + dashDirection * (DashSpeed * Time.fixedDeltaTime); Rigidbody.MovePosition(finalVelocity); yield return new WaitForFixedUpdate(); } EndDash(DashCooldown); } public void EndDash(float dashCooldown = float.PositiveInfinity) { Utils.EndUniqueCoroutine(this, ref _dashCoroutine); Rigidbody.linearVelocity = Vector3.zero; _animationController.ResetAnimationSpeed(); IsDashing = false; if (float.IsPositiveInfinity(dashCooldown)) { dashCooldown = DashCooldown; } StartCoroutine(Utils.CoolDownCoroutine(dashCooldown, () => IsDashCoolDownActive = false)); } #endregion } }