CapersProject/Assets/02.Scripts/Character/Player/Combat/CombatAttacker.cs

290 lines
10 KiB
C#
Raw Normal View History

2024-06-03 18:26:03 +00:00
using System.Collections;
using BlueWater.Audios;
using BlueWater.Interfaces;
using BlueWater.Utility;
using Sirenix.OdinInspector;
2024-06-03 18:26:03 +00:00
using UnityEngine;
using UnityEngine.InputSystem;
namespace BlueWater.Players.Combat
{
public class CombatAttacker : MonoBehaviour, IComboAttackable
2024-06-03 18:26:03 +00:00
{
// Variables
#region Variables
// Components
[SerializeField]
2024-06-03 18:26:03 +00:00
private Rigidbody _rigidbody;
[SerializeField]
private CapsuleCollider _capsuleCollider;
[SerializeField]
2024-06-03 18:26:03 +00:00
private AnimationController _animationController;
private IPhysicMovable _iPhysicMovable;
private IDashable _dashable;
private ISkillHandler _skillHandler;
private IStunnable _stunnable;
2024-06-03 18:26:03 +00:00
// ComboAttack
[field: SerializeField, Range(1, 21), Tooltip("한 번에 공격 가능한 개체 수")]
public int MaxHitCount { get; private set; } = 10;
2024-06-03 18:26:03 +00:00
[field: SerializeField]
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);
}
}
public Collider[] HitColliders { get; private set; }
2024-06-03 18:26:03 +00:00
[field: SerializeField]
public LayerMask TargetLayer { get; private set; }
[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
private void Awake()
{
InitializeComponents();
}
2024-06-03 18:26:03 +00:00
private void Start()
{
HitColliders = new Collider[MaxHitCount];
}
#endregion
// Initialize
#region Initialize
[Button("컴포넌트 초기화")]
private void InitializeComponents()
2024-06-03 18:26:03 +00:00
{
_rigidbody = GetComponent<Rigidbody>();
_capsuleCollider = GetComponent<CapsuleCollider>();
_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
public bool CanAttack()
2024-06-03 18:26:03 +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
{
_dashable.EndDash();
2024-06-03 18:26:03 +00:00
}
return true;
}
public void Attack(bool usedMouseAttack)
2024-06-03 18:26:03 +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-03 18:26:03 +00:00
private void UseMouseAttack()
{
var mousePos = Mouse.current.position.ReadValue();
var ray = CombatCameraManager.Instance.MainCamera.ScreenPointToRay(mousePos);
if (!Physics.Raycast(ray, out var hit, float.MaxValue, MouseClickLayer))
2024-06-03 18:26:03 +00:00
{
EndAttack();
2024-06-03 18:26:03 +00:00
return;
}
var attackDirection = (hit.point - _rigidbody.position).normalized;
attackDirection.y = 0f;
_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)
{
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
{
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)
{
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();
}
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.CanDamage())
2024-06-03 18:26:03 +00:00
{
iDamageable.TakeDamage(ComboAttacks[comboAttackCount - 1].Damage);
// TODO : Collider에 따라 잘 안보이는 경우가 있음
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;
Instantiate(_swordAttackParticle, spawnPosition, Quaternion.identity);
2024-06-03 18:26:03 +00:00
}
if (comboAttackCount == 2)
{
VisualFeedbackManager.Instance.TriggerHitStop(_comboAttackHitStopDuration);
}
}
}
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
}
}