378 lines
12 KiB
C#
378 lines
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using BlueWater.Audios;
|
|
using BlueWater.Interfaces;
|
|
using BlueWater.Utility;
|
|
using DG.Tweening;
|
|
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;
|
|
|
|
[SerializeField]
|
|
private ParticleSystem _movementParticle;
|
|
|
|
[SerializeField]
|
|
private ParticleSystem _dashParticle;
|
|
|
|
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
|
|
{
|
|
if (_isMoving == value) return;
|
|
|
|
_isMoving = value;
|
|
_animationController.SetAnimationParameter("isMoving", _isMoving);
|
|
|
|
if (!_movementParticle) return;
|
|
switch (_isMoving)
|
|
{
|
|
case true:
|
|
_movementParticle.Play();
|
|
break;
|
|
case false:
|
|
_movementParticle.Stop();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
[field: SerializeField, DisableIf("@true")]
|
|
public Vector3 PushDirection { get; private set; }
|
|
|
|
[field: SerializeField, DisableIf("@true")]
|
|
public float PushPower { get; private set; }
|
|
|
|
[field: SerializeField]
|
|
public float PushPowerReduction { get; private set; } = 20f;
|
|
|
|
private Vector3 _finalVelocity;
|
|
|
|
// Dash
|
|
[field: Title("대쉬")]
|
|
[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;
|
|
|
|
[field: SerializeField, Tooltip("대쉬가 쿨타임일 때 이펙트 사용 여부")]
|
|
public bool EnableDashCooldownEffect { get; private set; } = true;
|
|
|
|
[SerializeField]
|
|
private DOTweenAnimation _dashCooldownEffect;
|
|
|
|
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;
|
|
private bool _isQuitting;
|
|
|
|
#endregion
|
|
|
|
// Unity events
|
|
#region Unity events
|
|
|
|
private void Awake()
|
|
{
|
|
InitializeComponents();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
FlipVisualLook();
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (!CanMove()) return;
|
|
|
|
Move();
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
_isQuitting = true;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (_isQuitting) return;
|
|
|
|
_dashCooldownEffect.DOKill();
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Initialize Methods
|
|
#region Initialize Methods
|
|
|
|
[Button("컴포넌트 초기화")]
|
|
private void InitializeComponents()
|
|
{
|
|
Rigidbody = GetComponent<Rigidbody>();
|
|
_visualLook = transform.Find("VisualLook");
|
|
_animationController = GetComponent<AnimationController>();
|
|
|
|
_comboAttackable = GetComponent<IComboAttackable>();
|
|
_skillHandler = GetComponent<ISkillHandler>();
|
|
_stunnable = GetComponent<IStunnable>();
|
|
_dashCooldownEffect = _visualLook.GetComponent<DOTweenAnimation>();
|
|
}
|
|
|
|
#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()
|
|
{
|
|
return IsMoveEnabled;
|
|
|
|
// if (!IsMoveEnabled || IsDashing) return false;
|
|
//
|
|
// var isActivatingSkill = _skillHandler?.IsActivatingSkill ?? false;
|
|
// var isStunned = _stunnable?.IsStunned ?? false;
|
|
// // if (isStunned)
|
|
// // {
|
|
// // IsMoving = false;
|
|
// // }
|
|
// var isAttacking = _comboAttackable?.CurrentComboAttackCount > 0;
|
|
//
|
|
// var canMove = !isActivatingSkill && !isStunned && !isAttacking;
|
|
// if (!canMove)
|
|
// {
|
|
// if (!Rigidbody.isKinematic)
|
|
// {
|
|
// Rigidbody.linearVelocity = Vector3.zero;
|
|
// }
|
|
// IsMoving = false;
|
|
// }
|
|
//
|
|
// return canMove;
|
|
}
|
|
|
|
public void Move()
|
|
{
|
|
if (!_isDashing && _comboAttackable?.CurrentComboAttackCount <= 0 && _skillHandler?.IsActivatingSkill == false)
|
|
{
|
|
var velocityDirection = _inputDirection;
|
|
if (_stunnable?.IsStunned == true)
|
|
{
|
|
velocityDirection = Vector3.zero;
|
|
}
|
|
else
|
|
{
|
|
CurrentDirection = velocityDirection;
|
|
}
|
|
|
|
IsMoving = velocityDirection != Vector3.zero;
|
|
_finalVelocity = velocityDirection * (MoveSpeed * MoveSpeedCoefficient);
|
|
var pushVelocity = PushDirection * PushPower;
|
|
_finalVelocity += pushVelocity;
|
|
if (!Rigidbody.isKinematic)
|
|
{
|
|
Rigidbody.linearVelocity = _finalVelocity;
|
|
}
|
|
}
|
|
|
|
PushPower = Mathf.Max(0, PushPower - PushPowerReduction * Time.deltaTime);
|
|
|
|
// CurrentDirection = _inputDirection;
|
|
// IsMoving = _inputDirection != Vector3.zero;
|
|
// var finalVelocity = _inputDirection * (MoveSpeed * MoveSpeedCoefficient);
|
|
// Rigidbody.linearVelocity = finalVelocity;
|
|
}
|
|
|
|
public void AddForce(Vector3 force, ForceMode forceMode)
|
|
{
|
|
if (IsDashing) return;
|
|
|
|
Rigidbody.AddForce(force, forceMode);
|
|
}
|
|
|
|
public void SetPush(Vector3 pushDirection, float pushPower)
|
|
{
|
|
PushDirection = pushDirection;
|
|
PushPower = pushPower;
|
|
}
|
|
|
|
// Dash
|
|
public bool CanDash()
|
|
{
|
|
if (!IsDashEnabled || IsDashing) return false;
|
|
|
|
if (IsDashCoolDownActive)
|
|
{
|
|
if (EnableDashCooldownEffect)
|
|
{
|
|
_dashCooldownEffect.DORestart();
|
|
}
|
|
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;
|
|
if (_dashParticle)
|
|
{
|
|
_dashParticle.Play();
|
|
}
|
|
|
|
var dashDirection = _inputDirection;
|
|
if (dashDirection == Vector3.zero)
|
|
{
|
|
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)
|
|
{
|
|
// MovePosition()는 벽을 뚫는 현상이 있음
|
|
// var finalVelocity = transform.position + dashDirection * (DashSpeed * Time.fixedDeltaTime);
|
|
// Rigidbody.MovePosition(finalVelocity);
|
|
|
|
var finalVelocity = dashDirection * DashSpeed;
|
|
Rigidbody.linearVelocity = 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
|
|
}
|
|
} |