2024-06-03 18:26:03 +00:00
|
|
|
using System.Collections;
|
|
|
|
using BlueWater.Audios;
|
|
|
|
using BlueWater.Interfaces;
|
|
|
|
using BlueWater.Utility;
|
2024-06-16 21:29:06 +00:00
|
|
|
using Sirenix.OdinInspector;
|
2024-06-03 18:26:03 +00:00
|
|
|
using UnityEngine;
|
|
|
|
using UnityEngine.InputSystem;
|
|
|
|
|
|
|
|
namespace BlueWater.Players.Combat
|
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
public class CombatAttacker : MonoBehaviour, IComboAttackable
|
2024-06-03 18:26:03 +00:00
|
|
|
{
|
|
|
|
// Variables
|
|
|
|
#region Variables
|
|
|
|
|
|
|
|
// Components
|
2024-06-16 21:29:06 +00:00
|
|
|
[SerializeField]
|
2024-06-03 18:26:03 +00:00
|
|
|
private Rigidbody _rigidbody;
|
2024-06-23 19:52:28 +00:00
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
private CapsuleCollider _capsuleCollider;
|
2024-06-16 21:29:06 +00:00
|
|
|
|
|
|
|
[SerializeField]
|
2024-06-03 18:26:03 +00:00
|
|
|
private AnimationController _animationController;
|
|
|
|
|
|
|
|
private IPhysicMovable _iPhysicMovable;
|
2024-06-16 21:29:06 +00:00
|
|
|
private IDashable _dashable;
|
|
|
|
private ISkillHandler _skillHandler;
|
|
|
|
private IStunnable _stunnable;
|
2024-06-03 18:26:03 +00:00
|
|
|
|
|
|
|
// ComboAttack
|
|
|
|
[field: SerializeField, Range(1, 21), Tooltip("한 번에 공격 가능한 개체 수")]
|
2024-06-16 21:29:06 +00:00
|
|
|
public int MaxHitCount { get; private set; } = 10;
|
2024-06-03 18:26:03 +00:00
|
|
|
|
|
|
|
[field: SerializeField]
|
2024-06-16 21:29:06 +00:00
|
|
|
public ComboAttack[] ComboAttacks { get; private set; } = new ComboAttack[2];
|
|
|
|
|
|
|
|
public bool IsAttackEnabled { get; private set; } = true;
|
|
|
|
|
|
|
|
public bool IsComboAttackPossible { get; private set; }
|
|
|
|
public bool IsComboAttacking { get; private set; }
|
2024-06-03 18:26:03 +00:00
|
|
|
|
|
|
|
private int _currentComboAttackCount;
|
|
|
|
public int CurrentComboAttackCount
|
|
|
|
{
|
|
|
|
get => _currentComboAttackCount;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
_currentComboAttackCount = value;
|
|
|
|
_animationController.SetAnimationParameter("comboAttackCount", CurrentComboAttackCount);
|
|
|
|
}
|
|
|
|
}
|
2024-06-16 21:29:06 +00:00
|
|
|
public Collider[] HitColliders { get; private set; }
|
2024-06-03 18:26:03 +00:00
|
|
|
|
|
|
|
[field: SerializeField]
|
|
|
|
public LayerMask TargetLayer { get; private set; }
|
|
|
|
|
2024-06-16 21:29:06 +00:00
|
|
|
[field: SerializeField]
|
|
|
|
public LayerMask MouseClickLayer { get; private set; }
|
2024-06-03 18:26:03 +00:00
|
|
|
|
|
|
|
// Particles
|
|
|
|
[SerializeField]
|
|
|
|
private ParticleSystem _swordAttackParticle;
|
|
|
|
|
|
|
|
// Camera effects
|
|
|
|
[SerializeField]
|
|
|
|
private float _comboAttackHitStopDuration = 0.07f;
|
|
|
|
|
|
|
|
// Variables
|
|
|
|
private Coroutine _firstComboAttackCoroutine;
|
|
|
|
private Coroutine _secondComboAttackCoroutine;
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
// Unity events
|
|
|
|
#region Unity events
|
2024-06-16 21:29:06 +00:00
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
{
|
|
|
|
InitializeComponents();
|
|
|
|
}
|
|
|
|
|
2024-06-03 18:26:03 +00:00
|
|
|
private void Start()
|
|
|
|
{
|
|
|
|
HitColliders = new Collider[MaxHitCount];
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
// Initialize
|
|
|
|
#region Initialize
|
|
|
|
|
2024-06-16 21:29:06 +00:00
|
|
|
[Button("컴포넌트 초기화")]
|
|
|
|
private void InitializeComponents()
|
2024-06-03 18:26:03 +00:00
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
_rigidbody = GetComponent<Rigidbody>();
|
2024-06-23 19:52:28 +00:00
|
|
|
_capsuleCollider = GetComponent<CapsuleCollider>();
|
2024-06-16 21:29:06 +00:00
|
|
|
_animationController = GetComponent<AnimationController>();
|
|
|
|
|
|
|
|
_iPhysicMovable = GetComponent<IPhysicMovable>();
|
|
|
|
_dashable = GetComponent<IDashable>();
|
|
|
|
_skillHandler = GetComponent<ISkillHandler>();
|
|
|
|
_stunnable = GetComponent<IStunnable>();
|
2024-06-03 18:26:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
// Methods
|
|
|
|
#region Methods
|
|
|
|
|
2024-06-16 21:29:06 +00:00
|
|
|
public bool CanAttack()
|
2024-06-03 18:26:03 +00:00
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
if (!IsAttackEnabled || CurrentComboAttackCount == 2) return false;
|
|
|
|
|
|
|
|
var isActivatingSkill = _skillHandler?.IsActivatingSkill ?? false;
|
|
|
|
var isStunned = _stunnable?.IsStunned ?? false;
|
|
|
|
|
|
|
|
if (isActivatingSkill || isStunned) return false;
|
|
|
|
|
|
|
|
var isDashing = _dashable?.IsDashing ?? false;
|
|
|
|
if (isDashing)
|
2024-06-03 18:26:03 +00:00
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
_dashable.EndDash();
|
2024-06-03 18:26:03 +00:00
|
|
|
}
|
|
|
|
|
2024-06-16 21:29:06 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Attack(bool usedMouseAttack)
|
2024-06-03 18:26:03 +00:00
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
if (!CanAttack()) return;
|
2024-06-03 18:26:03 +00:00
|
|
|
|
|
|
|
if (CurrentComboAttackCount == 1 && IsComboAttackPossible)
|
|
|
|
{
|
|
|
|
IsComboAttacking = true;
|
|
|
|
if (usedMouseAttack)
|
|
|
|
{
|
|
|
|
UseMouseAttack();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (usedMouseAttack)
|
|
|
|
{
|
|
|
|
UseMouseAttack();
|
|
|
|
}
|
|
|
|
|
|
|
|
Utils.StartUniqueCoroutine(this, ref _firstComboAttackCoroutine, FirstComboAttackCoroutine());
|
|
|
|
}
|
2024-06-16 21:29:06 +00:00
|
|
|
|
2024-06-03 18:26:03 +00:00
|
|
|
private void UseMouseAttack()
|
|
|
|
{
|
|
|
|
var mousePos = Mouse.current.position.ReadValue();
|
|
|
|
var ray = CombatCameraManager.Instance.MainCamera.ScreenPointToRay(mousePos);
|
|
|
|
|
2024-06-16 21:29:06 +00:00
|
|
|
if (!Physics.Raycast(ray, out var hit, float.MaxValue, MouseClickLayer))
|
2024-06-03 18:26:03 +00:00
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
EndAttack();
|
2024-06-03 18:26:03 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var attackDirection = (hit.point - _rigidbody.position).normalized;
|
|
|
|
attackDirection.y = 0f;
|
2024-06-16 21:29:06 +00:00
|
|
|
_iPhysicMovable.SetCurrentDirection(attackDirection);
|
2024-06-03 18:26:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private IEnumerator FirstComboAttackCoroutine()
|
|
|
|
{
|
|
|
|
CurrentComboAttackCount = 1;
|
|
|
|
|
|
|
|
var animationStarted = false;
|
|
|
|
yield return StartCoroutine(_animationController.WaitForAnimationToRun("ComboAttack1",
|
|
|
|
success => animationStarted = success));
|
|
|
|
|
|
|
|
if (!animationStarted)
|
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
EndAttack();
|
2024-06-03 18:26:03 +00:00
|
|
|
yield break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_animationController.SetCurrentAnimationSpeed(ComboAttacks[CurrentComboAttackCount - 1].Speed);
|
|
|
|
AudioManager.Instance.PlaySfx("FirstComboAttack");
|
|
|
|
IsComboAttackPossible = true;
|
|
|
|
var doDamage = false;
|
|
|
|
|
|
|
|
while (_animationController.IsComparingCurrentAnimation("ComboAttack1") &&
|
|
|
|
_animationController.GetCurrentAnimationNormalizedTime() < 1f)
|
|
|
|
{
|
|
|
|
if (!doDamage && _animationController.GetCurrentAnimationNormalizedTime() >= 0.28f)
|
|
|
|
{
|
|
|
|
var moveSpeed = ComboAttacks[CurrentComboAttackCount - 1].MovePower;
|
|
|
|
var finalVelocity = _iPhysicMovable.CurrentDirection * moveSpeed;
|
2024-06-08 12:52:51 +00:00
|
|
|
_rigidbody.MovePosition(transform.position + finalVelocity * moveSpeed * Time.deltaTime);
|
2024-06-03 18:26:03 +00:00
|
|
|
doDamage = true;
|
|
|
|
DoDamage(CurrentComboAttackCount, _iPhysicMovable.CurrentDirection);
|
|
|
|
}
|
|
|
|
yield return new WaitForFixedUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsComboAttacking)
|
|
|
|
{
|
|
|
|
Utils.StartUniqueCoroutine(this, ref _secondComboAttackCoroutine, SecondComboAttackCoroutine());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
EndAttack();
|
2024-06-03 18:26:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private IEnumerator SecondComboAttackCoroutine()
|
|
|
|
{
|
|
|
|
_animationController.ResetAnimationSpeed();
|
|
|
|
IsComboAttackPossible = false;
|
|
|
|
CurrentComboAttackCount = 2;
|
|
|
|
|
|
|
|
var animationStarted = false;
|
|
|
|
yield return StartCoroutine(_animationController.WaitForAnimationToRun("ComboAttack2",
|
|
|
|
success => animationStarted = success));
|
|
|
|
|
|
|
|
if (!animationStarted)
|
|
|
|
{
|
2024-06-16 21:29:06 +00:00
|
|
|
EndAttack();
|
2024-06-03 18:26:03 +00:00
|
|
|
yield break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_animationController.SetCurrentAnimationSpeed(ComboAttacks[CurrentComboAttackCount - 1].Speed);
|
|
|
|
AudioManager.Instance.PlaySfx("SecondComboAttack");
|
|
|
|
var doDamage = false;
|
|
|
|
|
|
|
|
while (_animationController.IsComparingCurrentAnimation("ComboAttack2") &&
|
|
|
|
_animationController.GetCurrentAnimationNormalizedTime() < 1f)
|
|
|
|
{
|
|
|
|
if (!doDamage && _animationController.GetCurrentAnimationNormalizedTime() >= 0.3f)
|
|
|
|
{
|
|
|
|
var moveSpeed = ComboAttacks[CurrentComboAttackCount - 1].MovePower;
|
|
|
|
var finalVelocity = _iPhysicMovable.CurrentDirection * moveSpeed;
|
2024-06-08 12:52:51 +00:00
|
|
|
_rigidbody.MovePosition(transform.position + finalVelocity * moveSpeed * Time.deltaTime);
|
2024-06-03 18:26:03 +00:00
|
|
|
doDamage = true;
|
|
|
|
DoDamage(CurrentComboAttackCount, _iPhysicMovable.CurrentDirection);
|
|
|
|
}
|
|
|
|
yield return new WaitForFixedUpdate();
|
|
|
|
}
|
|
|
|
|
2024-06-16 21:29:06 +00:00
|
|
|
EndAttack();
|
2024-06-03 18:26:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void DoDamage(int comboAttackCount, Vector3 attackDirection)
|
|
|
|
{
|
|
|
|
var hitCount = Physics.OverlapSphereNonAlloc(transform.position, ComboAttacks[comboAttackCount - 1].Range, HitColliders,
|
|
|
|
TargetLayer, QueryTriggerInteraction.Collide);
|
|
|
|
for (var i = 0; i < hitCount; i++)
|
|
|
|
{
|
|
|
|
var hitCollider = HitColliders[i];
|
|
|
|
var targetDirection = (hitCollider.transform.position - transform.position).normalized;
|
|
|
|
var angleBetween = Vector3.Angle(attackDirection, targetDirection);
|
|
|
|
|
|
|
|
if (angleBetween >= ComboAttacks[comboAttackCount - 1].Angle * 0.5f) continue;
|
|
|
|
|
|
|
|
var iDamageable = hitCollider.transform.GetComponentInParent<IDamageable>();
|
|
|
|
if (iDamageable != null)
|
|
|
|
{
|
|
|
|
iDamageable.TakeDamage(ComboAttacks[comboAttackCount - 1].Damage);
|
2024-06-15 01:20:43 +00:00
|
|
|
// TODO : Collider에 따라 잘 안보이는 경우가 있음
|
2024-06-23 19:52:28 +00:00
|
|
|
var spawnPosition = _capsuleCollider.bounds.center + targetDirection * 1.5f;
|
|
|
|
//var closestPoint = hitCollider.ClosestPoint(_capsuleCollider.bounds.center);
|
2024-06-03 18:26:03 +00:00
|
|
|
//var spawnPosition = closestPoint + Random.insideUnitSphere * 0.2f;
|
2024-06-23 19:52:28 +00:00
|
|
|
Instantiate(_swordAttackParticle, spawnPosition, Quaternion.identity);
|
2024-06-03 18:26:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (comboAttackCount == 2)
|
|
|
|
{
|
|
|
|
VisualFeedbackManager.Instance.TriggerHitStop(_comboAttackHitStopDuration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-16 21:29:06 +00:00
|
|
|
public void EndAttack()
|
2024-06-03 18:26:03 +00:00
|
|
|
{
|
|
|
|
Utils.EndUniqueCoroutine(this, ref _firstComboAttackCoroutine);
|
|
|
|
Utils.EndUniqueCoroutine(this, ref _secondComboAttackCoroutine);
|
|
|
|
|
|
|
|
CurrentComboAttackCount = 0;
|
|
|
|
IsComboAttacking = false;
|
|
|
|
IsComboAttackPossible = false;
|
|
|
|
_animationController.ResetAnimationSpeed();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|