using BehaviorDesigner.Runtime; using UnityEngine; using UnityEngine.AI; using UnityEngine.Animations; // ReSharper disable once CheckNamespace namespace BlueWaterProject { public abstract class CombatAi : HumanAi { #region Properties and variables // 일반 변수 [SerializeField] protected bool isDrawGizmosInFieldOfView = true; [SerializeField] protected bool isAttacking; [SerializeField] protected LayerMask targetLayer; [SerializeField] protected Vector3 defensePos; [SerializeField] protected Transform targetTransform; [SerializeField] protected Collider[] colliderWithinRange = new Collider[TARGET_MAX_SIZE]; [SerializeField] protected IslandInfo attackingIslandInfo; [SerializeField] protected IslandInfo defendingIslandInfo; // 컴포넌트 관련 변수 protected Animator combatAnimator; protected NavMeshAgent combatAgent; protected CapsuleCollider myCollider; protected CapsuleCollider hitBoxCollider; protected LookAtConstraint lookAtConstraint; protected BehaviorTree behaviorTree; // 애니메이션 관련 변수 protected static readonly int SpeedHash = Animator.StringToHash("Speed"); protected static readonly int AttackHash = Animator.StringToHash("Attack"); protected static readonly int DamageHash = Animator.StringToHash("TakeDamage"); protected static readonly int DeathTypeHash = Animator.StringToHash("DeathType"); protected static readonly int DeathHash = Animator.StringToHash("Death"); protected static readonly int ShieldHash = Animator.StringToHash("Shield"); protected static readonly int OutlineColorHash = Shader.PropertyToID("_OutlineColor"); protected const int TARGET_MAX_SIZE = 30; #endregion #region Abstract methods protected abstract void SetLayer(); protected abstract void SetCurrentHp(float value, bool useBehaviorTreeVariable = false); protected abstract void RemoveAiListElement(); public abstract void FindTarget(); public abstract bool CanAttack(); public abstract void Attack(); #endregion #region Unity built-in methods protected override void Awake() { base.Awake(); FlagLookAtCamera(); SetLayer(); } private void Update() { if (combatAnimator.runtimeAnimatorController != null && combatAnimator.isActiveAndEnabled) { combatAnimator.SetFloat(SpeedHash, combatAgent.velocity.magnitude); } UpdateLookAtTarget(); } #endregion #region Custom methods protected override void InitComponent() { base.InitComponent(); combatAnimator = Utils.GetComponentAndAssert(transform); combatAgent = Utils.GetComponentAndAssert(transform); myCollider = Utils.GetComponentAndAssert(transform); hitBoxCollider = Utils.GetComponentAndAssert(transform.Find("HitBox")); lookAtConstraint = Utils.GetComponentAndAssert(flagContainer); } protected void FlagLookAtCamera() { if (CameraManager.Inst.MainCam != null) { var source = new ConstraintSource { sourceTransform = CameraManager.Inst.MainCam.transform, weight = 1f }; lookAtConstraint.AddSource(source); } lookAtConstraint.constraintActive = true; } protected void SetBehaviorTree(ExternalBehaviorTree externalBehaviorTree) { if (!externalBehaviorTree) { print("externalBehaviorTree == null error"); } var bt = gameObject.GetComponent(); if (bt != null) { Destroy(bt); } behaviorTree = gameObject.AddComponent(); behaviorTree.StartWhenEnabled = false; behaviorTree.ExternalBehavior = externalBehaviorTree; behaviorTree.EnableBehavior(); } public void MoveTarget(Vector3 targetPos, float stopDistance) { if (Vector3.Distance(combatAgent.destination, targetPos) < 0.1f) return; combatAgent.stoppingDistance = stopDistance; combatAgent.SetDestination(targetPos); } private void UpdateLookAtTarget() { if (CanAttack()) { combatAgent.updateRotation = false; var targetPos = targetTransform.position; targetPos.y = transform.position.y; transform.LookAt(targetPos); } else { combatAgent.updateRotation = true; } } public void SetTargetTransform(Transform value, bool useBehaviorTreeVariable = false) { targetTransform = value; if (!useBehaviorTreeVariable) return; Utils.SetBehaviorVariable(behaviorTree, "TargetTransform", value); } public void Die() { RemoveIslandInfo(); RemoveAiListElement(); StopAllCoroutines(); combatAgent.enabled = false; myCollider.enabled = false; hitBoxCollider.enabled = false; var randomValue = Random.Range(0, 2); combatAnimator.SetInteger(DeathTypeHash, randomValue); // TODO : 죽었을 때 처리(죽는 애니메이션 이후 사라지는 효과 등) combatAnimator.SetTrigger(DeathHash); } private void RemoveIslandInfo() { if (defendingIslandInfo == null) return; defendingIslandInfo.RemoveListElement(defendingIslandInfo.EnemyList, transform); defendingIslandInfo.RemoveListElement(defendingIslandInfo.ExceptHouseList, transform); defendingIslandInfo.RemoveListElement(defendingIslandInfo.TargetAllList, transform); } protected void SetAnimatorController(string controllerName) => combatAnimator.runtimeAnimatorController = UnitManager.Inst.AIAnimatorControllerList.Find(obj => obj.name == controllerName); protected void SetMoveSpeed(float value) => combatAgent.speed = value; public void SetAttackingIslandInfo(IslandInfo info) => attackingIslandInfo = info; public void SetDefendingIslandInfo(IslandInfo info) => defendingIslandInfo = info; public Transform GetTargetTransform() => targetTransform; public Vector3 GetDefensePos() => defensePos; public NavMeshAgent GetCombatAgent() => combatAgent; #endregion } }