OldBlueWater/BlueWater/Assets/02.Scripts/Character/CombatPlayer2D/CombatAttacker.cs
NTG f1d0c6d371 Closes #244, #254 CombatPlayer 개선, 하트 시스템 추가
+ CombatPlayer 기능별 분리
+ Combat, CombatUi 전용 input action map 추가
+ 스킬 시스템 로직 수정
+ 전투플레이어 스킬(검의 왈츠) 로직 변경
2024-05-08 00:37:33 +09:00

267 lines
9.9 KiB
C#

using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
// ReSharper disable once CheckNamespace
namespace BlueWaterProject
{
public class CombatAttacker : MonoBehaviour, IMeleeComboAttackable
{
// Components
private Rigidbody rb;
// Interfaces
private IAnimationStateController iAnimationStateController;
private IRigidMovable iRigidMovable;
// ComboAttack
[field: Range(1, 21), Tooltip("한 번에 공격 가능한 개체 수")]
[field: SerializeField] public int MaxHitCount { get; set; } = 10;
[field: SerializeField] public ComboAttack[] ComboAttacks { get; set; } = new ComboAttack[2];
public bool UsedMouseAttack { get; set; }
private int currentComboAttackCount;
public int CurrentComboAttackCount
{
get => currentComboAttackCount;
set
{
currentComboAttackCount = value;
iAnimationStateController.SetAnimationParameter(CombatPlayerAnimatorParameter.COMBO_ATTACK_COUNT, CurrentComboAttackCount);
}
}
public bool IsComboAttackPossible { get; set; }
public bool IsComboAttacking { get; set; }
public Collider[] HitColliders { get; set; }
private bool enableAttack = true;
[field: SerializeField] public LayerMask TargetLayer { get; set; }
[SerializeField] private LayerMask groundLayer;
// Particles
[SerializeField] private ParticleSystem swordHit;
// Camera effects
[SerializeField] private float comboAttackHitStopDuration = 0.15f;
// Variables
private Coroutine firstComboAttackCoroutine;
private Coroutine secondComboAttackCoroutine;
// Events
public delegate void AttackInDashEvent();
public event AttackInDashEvent OnAttackInDashing;
public delegate void StartAttackEvent();
public event StartAttackEvent OnStartAttack;
public delegate void EndAttackEvent();
public event EndAttackEvent OnEndAttack;
// Unity events
private void Start()
{
iAnimationStateController = GetComponent<IAnimationStateController>();
iRigidMovable = GetComponent<IRigidMovable>();
HitColliders = new Collider[MaxHitCount];
}
// Init
public void InitComponent(Rigidbody rigidbody)
{
rb = rigidbody;
}
// Event methods
public void HandleEnableAttack() => enableAttack = true;
public void HandleDisableAttack() => enableAttack = false;
public void HandleAttack(bool usedMouseAttack)
{
if (!enableAttack) return;
if (CurrentComboAttackCount == 1 && IsComboAttackPossible)
{
IsComboAttacking = true;
if (usedMouseAttack)
{
UseMouseAttack();
}
return;
}
if (CurrentComboAttackCount >= 1) return;
OnAttackInDashing?.Invoke();
if (usedMouseAttack)
{
UseMouseAttack();
}
firstComboAttackCoroutine = StartCoroutine(nameof(FirstComboAttackCoroutine));
}
// Methods
private void UseMouseAttack()
{
var mousePos = Mouse.current.position.ReadValue();
var ray = CameraManager.Inst.MainCam.ScreenPointToRay(mousePos);
if (!Physics.Raycast(ray, out var hit, float.MaxValue, groundLayer))
{
EndComboAttack();
return;
}
var attackDirection = (hit.point - rb.position).normalized;
attackDirection.y = 0f;
iRigidMovable.PreviousMoveDirection = attackDirection;
}
private IEnumerator FirstComboAttackCoroutine()
{
OnStartAttack?.Invoke();
CurrentComboAttackCount = 1;
var animationStarted = false;
yield return StartCoroutine(iAnimationStateController.WaitForAnimationToRun(CombatPlayerAnimationName.COMBO_ATTACK1,
success => animationStarted = success));
if (!animationStarted)
{
EndComboAttack();
yield break;
}
iAnimationStateController.SetCurrentAnimationSpeed(ComboAttacks[CurrentComboAttackCount - 1].Speed);
IsComboAttackPossible = true;
var doDamage = false;
while (iAnimationStateController.IsComparingCurrentAnimation(CombatPlayerAnimationName.COMBO_ATTACK1) &&
iAnimationStateController.GetCurrentAnimationNormalizedTime() < 1f)
{
if (!doDamage && iAnimationStateController.GetCurrentAnimationNormalizedTime() >= 0.28f)
{
var moveSpeed = ComboAttacks[CurrentComboAttackCount - 1].MovePower;
var finalVelocity = iRigidMovable.PreviousMoveDirection * moveSpeed;
//rb.velocity = finalVelocity;
rb.AddForce(finalVelocity, ForceMode.Impulse);
rb.velocity = Vector3.zero;
doDamage = true;
DoDamage(CurrentComboAttackCount, iRigidMovable.PreviousMoveDirection);
}
yield return new WaitForFixedUpdate();
}
if (IsComboAttacking)
{
secondComboAttackCoroutine = StartCoroutine(nameof(SecondComboAttackCoroutine));
}
else
{
EndComboAttack();
}
}
private IEnumerator SecondComboAttackCoroutine()
{
IsComboAttackPossible = false;
CurrentComboAttackCount = 2;
var animationStarted = false;
yield return StartCoroutine(iAnimationStateController.WaitForAnimationToRun(CombatPlayerAnimationName.COMBO_ATTACK2,
success => animationStarted = success));
if (!animationStarted)
{
EndComboAttack();
yield break;
}
iAnimationStateController.SetCurrentAnimationSpeed(ComboAttacks[CurrentComboAttackCount - 1].Speed);
var doDamage = false;
while (iAnimationStateController.IsComparingCurrentAnimation(CombatPlayerAnimationName.COMBO_ATTACK2) &&
iAnimationStateController.GetCurrentAnimationNormalizedTime() < 1f)
{
if (!doDamage && iAnimationStateController.GetCurrentAnimationNormalizedTime() >= 0.3f)
{
var moveSpeed = ComboAttacks[CurrentComboAttackCount - 1].MovePower;
var finalVelocity = iRigidMovable.PreviousMoveDirection * moveSpeed;
//rb.velocity = finalVelocity;
rb.AddForce(finalVelocity, ForceMode.Impulse);
rb.velocity = Vector3.zero;
doDamage = true;
DoDamage(CurrentComboAttackCount, iRigidMovable.PreviousMoveDirection);
}
yield return new WaitForFixedUpdate();
}
EndComboAttack();
}
private void DoDamage(int comboAttackCount, Vector3 attackDirection)
{
var size = Physics.OverlapSphereNonAlloc(transform.position, ComboAttacks[comboAttackCount - 1].Range, HitColliders,
TargetLayer, QueryTriggerInteraction.Collide);
for (var i = 0; i < size; i++)
{
var targetDirection = (HitColliders[i].transform.position - transform.position).normalized;
var angleBetween = Vector3.Angle(attackDirection, targetDirection);
if (angleBetween >= ComboAttacks[comboAttackCount - 1].Angle * 0.5f) continue;
if (HitColliders[i].gameObject.layer == LayerMask.NameToLayer("Enemy"))
{
var iDamageable = HitColliders[i].transform.GetComponent<IDamageable>();
if (iDamageable != null)
{
iDamageable.TakeDamage(ComboAttacks[comboAttackCount - 1].Damage);
var closestPoint = HitColliders[i].ClosestPoint(transform.position);
//var spawnPosition = closestPoint + Random.insideUnitSphere * 0.2f;
Instantiate(swordHit, closestPoint, Quaternion.identity);
}
if (comboAttackCount == 2)
{
VisualFeedbackManager.Inst.TriggerHitStop(comboAttackHitStopDuration);
}
}
else if (HitColliders[i].gameObject.layer == LayerMask.NameToLayer("Skill") &&
HitColliders[i].CompareTag("DestructiveSkill"))
{
var iDamageable = HitColliders[i].transform.GetComponent<IDamageable>();
iDamageable?.TakeDamage(0);
}
}
}
public void EndComboAttack()
{
if (firstComboAttackCoroutine != null)
{
StopCoroutine(firstComboAttackCoroutine);
firstComboAttackCoroutine = null;
}
if (secondComboAttackCoroutine != null)
{
StopCoroutine(secondComboAttackCoroutine);
secondComboAttackCoroutine = null;
}
rb.velocity = Vector3.zero;
IsComboAttacking = false;
IsComboAttackPossible = false;
iAnimationStateController.ResetAnimationSpeed();
CurrentComboAttackCount = 0;
OnEndAttack?.Invoke();
}
}
}