using System; using UnityEngine; using UnityEngine.AI; using Random = UnityEngine.Random; // ReSharper disable once CheckNamespace namespace BlueWaterProject { public enum AttackerType { NONE, PLAYER, PIRATE, ENEMY } [Serializable] public abstract class AiController : MonoBehaviour, IDamageable { #region Property and variable protected bool isAttacking; protected AttackerType attackerType; protected Animator aiAnimator; protected AiMover aiMover; protected FieldOfView fieldOfView; protected NavMeshAgent navMeshAgent; public static readonly int SpeedHash = Animator.StringToHash("Speed"); public static readonly int AttackHash = Animator.StringToHash("Attack"); public static readonly int DamageHash = Animator.StringToHash("TakeDamage"); public static readonly int DeathTypeHash = Animator.StringToHash("DeathType"); public static readonly int DeathHash = Animator.StringToHash("Death"); #endregion #region abstract function protected abstract void Attack(); #endregion #region Unity built-in function protected virtual void Awake() { aiAnimator = Utils.GetComponentAndAssert(transform); aiMover = Utils.GetComponentAndAssert(transform); fieldOfView = Utils.GetComponentAndAssert(transform); navMeshAgent = Utils.GetComponentAndAssert(transform); if (gameObject.layer == LayerMask.NameToLayer("Player")) { attackerType = AttackerType.PLAYER; } else if (gameObject.layer == LayerMask.NameToLayer("Pirate")) { attackerType = AttackerType.PIRATE; } else if (gameObject.layer == LayerMask.NameToLayer("Enemy")) { attackerType = AttackerType.ENEMY; } } private void Start() { SetCurrentHp(AiStat.maxHp); } private void LateUpdate() { Attack(); } #endregion #region interface property and function [field: SerializeField] public AiStat AiStat { get; set; } = new(); public float GetCurrentHp() => AiStat.currentHp; public void TakeDamage(AiStat attacker, AiStat defender) { // 회피 성공 체크 if (Random.Range(0, 100) < defender.avoidanceRate) { // TODO : 회피 처리 return; } var finalDamage = Utils.CalcDamage(attacker, defender); // 방패 막기 체크 if (finalDamage == 0f) { // TODO : 방패로 막힘 처리(애니메이션 등) return; } var changeHp = Mathf.Max(defender.currentHp - finalDamage, 0); SetCurrentHp(changeHp); // 죽었는지 체크 if (changeHp == 0f) { var randomValue = Random.Range(0, 2); aiAnimator.SetInteger(DeathTypeHash, randomValue); // TODO : 죽었을 때 처리(죽는 애니메이션 이후 사라지는 효과 등) aiAnimator.SetTrigger(DeathHash); Invoke(nameof(Destroy), 5f); return; } aiAnimator.SetTrigger(DamageHash); } #endregion #region Custom function private void Destroy() => Destroy(gameObject); public bool GetIsAttacking() => isAttacking; public FieldOfView GetFieldOfView() => fieldOfView; public NavMeshAgent GetNavMeshAgent() => navMeshAgent; public Animator GetAnimator() => aiAnimator; public void SetCurrentHp(float value) => AiStat.currentHp = value; #endregion } }