298 lines
11 KiB
C#
298 lines
11 KiB
C#
|
using System;
|
||
|
using System.Collections;
|
||
|
using Sirenix.OdinInspector;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.InputSystem;
|
||
|
|
||
|
// ReSharper disable once CheckNamespace
|
||
|
namespace BlueWaterProject
|
||
|
{
|
||
|
public class CombatPlayerController : MonoBehaviour, IDamageable
|
||
|
{
|
||
|
/***********************************************************************
|
||
|
* Definitions
|
||
|
***********************************************************************/
|
||
|
#region Class
|
||
|
|
||
|
[Serializable]
|
||
|
public class Components
|
||
|
{
|
||
|
public PlayerInput playerInput;
|
||
|
public Transform visualLook;
|
||
|
public Animator animator;
|
||
|
public PhysicsMovement movement;
|
||
|
}
|
||
|
|
||
|
[Serializable]
|
||
|
public class CharacterOption
|
||
|
{
|
||
|
[Range(0f, 1000f), Tooltip("최대 체력")]
|
||
|
public float maxHp = 100f;
|
||
|
|
||
|
[Title("공격")]
|
||
|
[Range(1, 21), Tooltip("한 번에 공격 가능한 개체 수")]
|
||
|
public int maxHitNum = 10;
|
||
|
|
||
|
[Range(0f, 100f), Tooltip("공격 데미지")]
|
||
|
public float attackDamage = 10f;
|
||
|
|
||
|
[Range(0.1f, 2f), Tooltip("콤보 공격 포함 총 걸리는 시간")]
|
||
|
public float attackTime = 0.7f;
|
||
|
|
||
|
[Range(0.1f, 5f), Tooltip("공격 범위 사거리(반지름)")]
|
||
|
public float attackRange = 1.5f;
|
||
|
|
||
|
[Range(0f, 360f), Tooltip("공격 범위 각도")]
|
||
|
public float attackAngle = 180f;
|
||
|
|
||
|
[Tooltip("공격할 레이어 설정")]
|
||
|
public LayerMask targetLayer = -1;
|
||
|
}
|
||
|
|
||
|
[Serializable]
|
||
|
[DisableIf("@true")]
|
||
|
public class CurrentState
|
||
|
{
|
||
|
public bool isAttacking;
|
||
|
public bool isComboPossible;
|
||
|
public bool isComboAttacking;
|
||
|
}
|
||
|
|
||
|
[DisableIf("@true")]
|
||
|
[Serializable]
|
||
|
public class CurrentValue
|
||
|
{
|
||
|
[Tooltip("현재 체력")]
|
||
|
public float currentHp;
|
||
|
|
||
|
[Tooltip("최근에 공격 받은 충돌체 배열")]
|
||
|
public Collider[] hitColliders;
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
/***********************************************************************
|
||
|
* Variables
|
||
|
***********************************************************************/
|
||
|
#region Variables
|
||
|
|
||
|
[field: SerializeField] public Components MyComponents { get; private set; }
|
||
|
[field: SerializeField] public CharacterOption MyCharacterOption { get; private set; }
|
||
|
[field: SerializeField] public CurrentState MyCurrentState { get; set; }
|
||
|
[field: SerializeField] public CurrentValue MyCurrentValue { get; set; }
|
||
|
|
||
|
public static readonly int IsMovingHash = Animator.StringToHash("isMoving");
|
||
|
public static readonly int XDirectionHash = Animator.StringToHash("xDirection");
|
||
|
public static readonly int ZDirectionHash = Animator.StringToHash("zDirection");
|
||
|
public static readonly int IsAttackingHash = Animator.StringToHash("isAttacking");
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
/***********************************************************************
|
||
|
* Unity Events
|
||
|
***********************************************************************/
|
||
|
#region Unity Events
|
||
|
|
||
|
private void OnEnable()
|
||
|
{
|
||
|
MyComponents.playerInput.actions.FindAction("Attack").performed += OnAttackEvent;
|
||
|
}
|
||
|
|
||
|
private void OnDisable()
|
||
|
{
|
||
|
MyComponents.playerInput.actions.FindAction("Attack").performed -= OnAttackEvent;
|
||
|
}
|
||
|
|
||
|
private void Start()
|
||
|
{
|
||
|
InitComponents();
|
||
|
InitStartValue();
|
||
|
}
|
||
|
|
||
|
private void Update()
|
||
|
{
|
||
|
MoveAnimation();
|
||
|
FlipVisualLook(MyComponents.movement.GetPreviousMoveDirection().x);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
/***********************************************************************
|
||
|
* Init Methods
|
||
|
***********************************************************************/
|
||
|
#region Unity Events
|
||
|
|
||
|
private void InitComponents()
|
||
|
{
|
||
|
if (!TryGetComponent(out MyComponents.movement))
|
||
|
{
|
||
|
MyComponents.movement = gameObject.AddComponent<PhysicsMovement>();
|
||
|
}
|
||
|
|
||
|
MyComponents.movement.SetAnimator(MyComponents.animator);
|
||
|
}
|
||
|
|
||
|
private void InitStartValue()
|
||
|
{
|
||
|
MyCurrentValue.hitColliders = new Collider[MyCharacterOption.maxHitNum];
|
||
|
SetCurrentHp(MyCharacterOption.maxHp);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
/***********************************************************************
|
||
|
* Player Input
|
||
|
***********************************************************************/
|
||
|
#region Unity Events
|
||
|
|
||
|
private void OnAttackEvent(InputAction.CallbackContext context)
|
||
|
{
|
||
|
if (MyCurrentState.isAttacking && !MyCurrentState.isComboPossible) return;
|
||
|
|
||
|
// var control = context.control;
|
||
|
//
|
||
|
// if (control.name.Equals("leftButton"))
|
||
|
// {
|
||
|
// UseMouseAttack = true;
|
||
|
// }
|
||
|
// else if (control.name.Equals("k"))
|
||
|
// {
|
||
|
// UseMouseAttack = false;
|
||
|
// }
|
||
|
|
||
|
if (MyCurrentState.isComboPossible)
|
||
|
{
|
||
|
MyCurrentState.isComboAttacking = true;
|
||
|
return;
|
||
|
}
|
||
|
MyComponents.animator.SetBool(IsAttackingHash, true);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
/***********************************************************************
|
||
|
* Interfaces
|
||
|
***********************************************************************/
|
||
|
#region Interfaces
|
||
|
|
||
|
// IDamageable
|
||
|
public void TakeDamage(float attackerPower, Vector3? attackPos = null)
|
||
|
{
|
||
|
if (MyComponents.movement.GetIsDashing()) return;
|
||
|
|
||
|
var changeHp = Mathf.Max(MyCurrentValue.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");
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
/***********************************************************************
|
||
|
* Methods
|
||
|
***********************************************************************/
|
||
|
#region Methods
|
||
|
|
||
|
public void AttackTiming()
|
||
|
{
|
||
|
var attackDirection = MyComponents.movement.GetPreviousMoveDirection();
|
||
|
|
||
|
Array.Clear(MyCurrentValue.hitColliders, 0, MyCharacterOption.maxHitNum);
|
||
|
|
||
|
var size = Physics.OverlapSphereNonAlloc(transform.position, MyCharacterOption.attackRange, MyCurrentValue.hitColliders, MyCharacterOption.targetLayer);
|
||
|
|
||
|
for (var i = 0; i < size; i++)
|
||
|
{
|
||
|
var targetDirection = (MyCurrentValue.hitColliders[i].transform.position - transform.position).normalized;
|
||
|
var angleBetween = Vector3.Angle(attackDirection, targetDirection);
|
||
|
|
||
|
if (angleBetween >= MyCharacterOption.attackAngle * 0.5f) continue;
|
||
|
|
||
|
if (MyCurrentValue.hitColliders[i].gameObject.layer == LayerMask.NameToLayer("Enemy"))
|
||
|
{
|
||
|
var iDamageable = MyCurrentValue.hitColliders[i].transform.GetComponent<IDamageable>();
|
||
|
iDamageable.TakeDamage(MyCharacterOption.attackDamage);
|
||
|
VisualFeedbackManager.Inst.TriggerHitStop(0.1f);
|
||
|
}
|
||
|
else if (MyCurrentValue.hitColliders[i].gameObject.layer == LayerMask.NameToLayer("Skill") &&
|
||
|
MyCurrentValue.hitColliders[i].CompareTag("DestructiveSkill"))
|
||
|
{
|
||
|
var iDamageable = MyCurrentValue.hitColliders[i].transform.GetComponent<IDamageable>();
|
||
|
iDamageable.TakeDamage(MyCharacterOption.attackDamage);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void MoveAnimation()
|
||
|
{
|
||
|
var isMoving = MyComponents.movement.GetIsMoving();
|
||
|
var previousDirection = MyComponents.movement.GetPreviousMoveDirection();
|
||
|
MyComponents.animator.SetBool(IsMovingHash, isMoving);
|
||
|
if (isMoving)
|
||
|
{
|
||
|
MyComponents.animator.SetFloat(XDirectionHash, previousDirection.x);
|
||
|
MyComponents.animator.SetFloat(ZDirectionHash, previousDirection.z);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void FlipVisualLook(float previousDirectionX)
|
||
|
{
|
||
|
var localScale = MyComponents.visualLook.localScale;
|
||
|
localScale.x = previousDirectionX switch
|
||
|
{
|
||
|
> 0.01f => Mathf.Abs(localScale.x),
|
||
|
< -0.01f => -Mathf.Abs(localScale.x),
|
||
|
_ => localScale.x
|
||
|
};
|
||
|
MyComponents.visualLook.localScale = localScale;
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
[Button("현재 체력 변경")]
|
||
|
private void SetCurrentHp(float value) => MyCurrentValue.currentHp = value;
|
||
|
public bool GetIsAttacking() => MyCurrentState.isAttacking;
|
||
|
public bool GetIsComboAttacking() => MyCurrentState.isComboAttacking;
|
||
|
public float GetDashCooldown() => MyComponents.movement.GetDashCooldown();
|
||
|
public float GetDashTime() => MyComponents.movement.GetDashTime();
|
||
|
public float GetAttackTime() => MyCharacterOption.attackTime;
|
||
|
public void SetIsAttacking(bool value) => MyCurrentState.isAttacking = value;
|
||
|
public void SetIsComboAttacking(bool value) => MyCurrentState.isComboAttacking = value;
|
||
|
public void SetIsComboPossible(bool value) => MyCurrentState.isComboPossible = value;
|
||
|
public void SetIsDashing(bool value) => MyComponents.movement.SetIsDashing(value);
|
||
|
public void SetEnableDashing(bool value) => MyComponents.movement.SetEnableDashing(value);
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|