+ Red Sprite V3(이누야샤) 캐릭터 추가 + 03.CombatTest Scene(전투 테스트용 씬) 추가 + 4방향 Idle, Walk, Dash, ComboAttack 구현 + ComboAttack 애니메이션 복제 및 수정
238 lines
8.1 KiB
C#
238 lines
8.1 KiB
C#
using System;
|
|
using System.Collections;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace BlueWaterProject
|
|
{
|
|
public class CombatPlayer : MonoBehaviour, IDamageable
|
|
{
|
|
[Title("초기화 방식")]
|
|
[SerializeField] private bool autoInit = true;
|
|
|
|
[Title("캐릭터 변수")]
|
|
[SerializeField] private float maxHp = 100f;
|
|
[SerializeField] private float currentHp;
|
|
[SerializeField] private float moveSpeed = 5f;
|
|
|
|
[field: Title("대쉬")]
|
|
[field: SerializeField] public float DashForce { get; set; } = 20f;
|
|
[field: SerializeField] public float DashCooldown { get; set; } = 0.5f;
|
|
[field: DisableIf("@true")]
|
|
[field: SerializeField] public bool IsDashing { get; set; }
|
|
[field: DisableIf("@true")]
|
|
[field: SerializeField] public bool EnableDash { get; set; } = true;
|
|
|
|
[field: Title("공격")]
|
|
[field: SerializeField] public int MaxHitNum { get; set; } = 10;
|
|
[field: SerializeField] public float AttackDamage { get; set; } = 10f;
|
|
[field: SerializeField] public float AttackRange { get; set; } = 1.5f;
|
|
[field: SerializeField] public float AttackAngle { get; set; } = 180f;
|
|
[field: SerializeField] public float ComboTime { get; set; } = 0.5f;
|
|
[field: SerializeField] public LayerMask TargetLayer { get; set; }
|
|
[field: DisableIf("@true")]
|
|
[field: SerializeField] public bool IsAttacking { get; set; }
|
|
[field: DisableIf("@true")]
|
|
[field: SerializeField] public bool IsComboAttacking { get; set; }
|
|
[field: DisableIf("@true")]
|
|
[field: SerializeField] public bool IsComboPossible { get; set; }
|
|
|
|
[Title("컴포넌트")]
|
|
[SerializeField] private PlayerInput playerInput;
|
|
[field: SerializeField] public Rigidbody Rb { get; set; }
|
|
[SerializeField] private Transform visualLook;
|
|
[field: SerializeField] public Animator Animator { get; set; }
|
|
|
|
private Vector2 movementInput;
|
|
public Vector3 PreviousDir { get; set; } = Vector3.back;
|
|
[field: SerializeField] public Collider[] HitColliders { get; set; }
|
|
|
|
private static readonly int XDirectionHash = Animator.StringToHash("xDirection");
|
|
private static readonly int ZDirectionHash = Animator.StringToHash("zDirection");
|
|
private static readonly int IsMovingHash = Animator.StringToHash("isMoving");
|
|
public readonly int isDashingHash = Animator.StringToHash("isDashing");
|
|
public readonly int isAttackingHash = Animator.StringToHash("isAttacking");
|
|
|
|
[Button("셋팅 초기화")]
|
|
private void Init()
|
|
{
|
|
playerInput = GetComponent<PlayerInput>();
|
|
Rb = GetComponent<Rigidbody>();
|
|
visualLook = transform.Find("VisualLook");
|
|
Animator = visualLook.GetComponent<Animator>();
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (autoInit)
|
|
{
|
|
Init();
|
|
}
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
HitColliders = new Collider[MaxHitNum];
|
|
|
|
SetCurrentHp(maxHp);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
playerInput.actions.FindAction("Attack").performed += OnAttackEvent;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
playerInput.actions.FindAction("Attack").performed -= OnAttackEvent;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
HandleMovement();
|
|
}
|
|
|
|
public void TakeDamage(float attackerPower, Vector3? attackPos = null)
|
|
{
|
|
if (IsDashing) return;
|
|
|
|
var changeHp = Mathf.Max(currentHp - attackerPower, 0);
|
|
SetCurrentHp(changeHp);
|
|
|
|
if (InIslandCamera.Inst.InIslandCam)
|
|
{
|
|
VisualFeedbackManager.Inst.CameraShake(InIslandCamera.Inst.InIslandCam);
|
|
}
|
|
|
|
// 죽었는지 체크
|
|
if (changeHp == 0f)
|
|
{
|
|
Die();
|
|
return;
|
|
}
|
|
}
|
|
|
|
public void Die()
|
|
{
|
|
print("Combat Player Die");
|
|
}
|
|
|
|
private void OnMove(InputValue value)
|
|
{
|
|
movementInput = value.Get<Vector2>();
|
|
}
|
|
|
|
public void OnDash()
|
|
{
|
|
if (!EnableDash || IsDashing) return;
|
|
|
|
Animator.SetBool(isDashingHash, true);
|
|
}
|
|
|
|
private void OnAttackEvent(InputAction.CallbackContext context)
|
|
{
|
|
if (IsAttacking && !IsComboPossible) return;
|
|
|
|
// var control = context.control;
|
|
//
|
|
// if (control.name.Equals("leftButton"))
|
|
// {
|
|
// UseMouseAttack = true;
|
|
// }
|
|
// else if (control.name.Equals("k"))
|
|
// {
|
|
// UseMouseAttack = false;
|
|
// }
|
|
|
|
if (IsComboPossible)
|
|
{
|
|
IsComboAttacking = true;
|
|
return;
|
|
}
|
|
Animator.SetBool(isAttackingHash, true);
|
|
}
|
|
|
|
private void HandleMovement()
|
|
{
|
|
if (!IsDashing)
|
|
{
|
|
var movement = new Vector3(movementInput.x, 0, movementInput.y);
|
|
var moveDirection = movement.normalized;
|
|
Rb.velocity = moveDirection * moveSpeed;
|
|
}
|
|
|
|
var localScale = visualLook.localScale;
|
|
localScale.x = Rb.velocity.x switch
|
|
{
|
|
> 0.01f => Mathf.Abs(localScale.x),
|
|
< -0.01f => -Mathf.Abs(localScale.x),
|
|
_ => localScale.x
|
|
};
|
|
visualLook.localScale = localScale;
|
|
|
|
if (Rb.velocity != Vector3.zero)
|
|
{
|
|
PreviousDir = Rb.velocity.normalized;
|
|
Animator.SetFloat(XDirectionHash, PreviousDir.x);
|
|
Animator.SetFloat(ZDirectionHash, PreviousDir.z);
|
|
}
|
|
var isMoving = Rb.velocity != Vector3.zero;
|
|
Animator.SetBool(IsMovingHash, isMoving);
|
|
}
|
|
|
|
public void AttackTiming()
|
|
{
|
|
var attackDirection = PreviousDir;
|
|
|
|
Array.Clear(HitColliders, 0, MaxHitNum);
|
|
|
|
var size = Physics.OverlapSphereNonAlloc(transform.position, AttackRange, HitColliders, TargetLayer);
|
|
|
|
for (var i = 0; i < size; i++)
|
|
{
|
|
var targetDirection = (HitColliders[i].transform.position - transform.position).normalized;
|
|
var angleBetween = Vector3.Angle(attackDirection, targetDirection);
|
|
|
|
if (angleBetween >= AttackAngle * 0.5f) continue;
|
|
|
|
if (HitColliders[i].gameObject.layer == LayerMask.NameToLayer("Enemy"))
|
|
{
|
|
var iDamageable = HitColliders[i].transform.GetComponent<IDamageable>();
|
|
iDamageable.TakeDamage(AttackDamage);
|
|
VisualFeedbackManager.Inst.TriggerHitStop(0.1f);
|
|
}
|
|
else if (HitColliders[i].gameObject.layer == LayerMask.NameToLayer("Skill") &&
|
|
HitColliders[i].CompareTag("DestructiveSkill"))
|
|
{
|
|
var iDamageable = HitColliders[i].transform.GetComponent<IDamageable>();
|
|
iDamageable.TakeDamage(AttackDamage);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CoolDown(float waitTime, Action onCooldownComplete = null)
|
|
{
|
|
StartCoroutine(CoolDownCoroutine(waitTime, onCooldownComplete));
|
|
}
|
|
|
|
private IEnumerator CoolDownCoroutine(float waitTime, Action onCooldownComplete = null)
|
|
{
|
|
var time = 0f;
|
|
|
|
while (time <= waitTime)
|
|
{
|
|
time += Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
|
|
onCooldownComplete?.Invoke();
|
|
}
|
|
|
|
private void SetCurrentHp(float value)
|
|
{
|
|
currentHp = value;
|
|
}
|
|
}
|
|
} |