From aeb4fc28472c95f50648a521772ab11be0ac1955 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Sat, 14 Mar 2026 15:08:47 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20AI=20=ED=83=80=EA=B2=9F=ED=8C=85=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20-=20=EC=82=AC=EB=A7=9D=ED=95=9C=20?= =?UTF-8?q?=EB=8C=80=EC=83=81=20=EB=AC=B4=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FindTargetAction: IDamageable.IsDead 체크로 사망한 타겟 제외 - SetTargetInRangeAction: 사망한 타겟을 거리 검색에서 제외 - HasTargetCondition: 타겟 생존 여부 추가 확인 - BossArea: FindObjectOfType → FindFirstObjectByType 변경 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../Actions/FindTargetAction.cs | 23 +++++++++++++++---- .../Actions/SetTargetInRangeAction.cs | 10 +++++++- .../Conditions/HasTargetCondition.cs | 17 ++++++++++++-- Assets/Scripts/Enemy/BossArea.cs | 2 +- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs index a6258db8..de614bed 100644 --- a/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs +++ b/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs @@ -3,6 +3,7 @@ using Unity.Behavior; using UnityEngine; using Action = Unity.Behavior.Action; using Unity.Properties; +using Colosseum.Combat; [Serializable, GeneratePropertyBag] [NodeDescription(name: "FindTarget", story: "[타겟] 탐색", category: "Action", id: "bb947540549026f3c5625c6d19213311")] @@ -21,15 +22,29 @@ public partial class FindTargetAction : Action return Status.Failure; } - GameObject foundTarget = GameObject.FindGameObjectWithTag(Tag.Value); + // 모든 타겟 후보 검색 + GameObject[] candidates = GameObject.FindGameObjectsWithTag(Tag.Value); - if (foundTarget == null) + if (candidates == null || candidates.Length == 0) { return Status.Failure; } - Target.Value = foundTarget; - return Status.Success; + // 사망하지 않은 타겟 찾기 + foreach (GameObject candidate in candidates) + { + IDamageable damageable = candidate.GetComponent(); + + // IDamageable이 없거나 살아있는 경우 타겟으로 선택 + if (damageable == null || !damageable.IsDead) + { + Target.Value = candidate; + return Status.Success; + } + } + + // 살아있는 타겟이 없음 + return Status.Failure; } } diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs index 0acbe056..240a9ae6 100644 --- a/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs +++ b/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs @@ -3,6 +3,7 @@ using Unity.Behavior; using UnityEngine; using Action = Unity.Behavior.Action; using Unity.Properties; +using Colosseum.Combat; [Serializable, GeneratePropertyBag] [NodeDescription(name: "SetTargetInRange", story: "[거리] 내에 [대상이] 있는지 확인", category: "Action", id: "93b7a5d823a58618d5371c01ef894948")] @@ -32,12 +33,19 @@ public partial class SetTargetInRangeAction : Action return Status.Failure; } - // 가장 가까운 타겟 찾기 + // 가장 가까운 살아있는 타겟 찾기 GameObject nearestTarget = null; float nearestDistance = Range.Value; // Range 내에서만 검색 foreach (GameObject potentialTarget in targets) { + // 사망한 타겟은 제외 + IDamageable damageable = potentialTarget.GetComponent(); + if (damageable != null && damageable.IsDead) + { + continue; + } + float distance = Vector3.Distance( GameObject.transform.position, potentialTarget.transform.position diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs b/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs index 2b4daa1c..1c1c4cf0 100644 --- a/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs @@ -3,11 +3,12 @@ using UnityEngine; using Unity.Behavior; using Unity.Properties; using Condition = Unity.Behavior.Condition; +using Colosseum.Combat; namespace Colosseum.AI.BehaviorActions.Conditions { /// - /// 타겟이 존재하는지 확인합니다. + /// 타겟이 존재하고 살아있는지 확인합니다. /// [Serializable, GeneratePropertyBag] [NodeDescription(name: "Has Target", story: "Has [Target]", category: "Combat")] @@ -18,7 +19,19 @@ namespace Colosseum.AI.BehaviorActions.Conditions public override bool IsTrue() { - return Target.Value != null && Target.Value.activeInHierarchy; + if (Target.Value == null || !Target.Value.activeInHierarchy) + { + return false; + } + + // 타겟이 사망했는지 확인 + IDamageable damageable = Target.Value.GetComponent(); + if (damageable != null && damageable.IsDead) + { + return false; + } + + return true; } } } diff --git a/Assets/Scripts/Enemy/BossArea.cs b/Assets/Scripts/Enemy/BossArea.cs index 1dfe2605..2b2beeb2 100644 --- a/Assets/Scripts/Enemy/BossArea.cs +++ b/Assets/Scripts/Enemy/BossArea.cs @@ -73,7 +73,7 @@ namespace Colosseum.Enemy // BossHealthBarUI 자동 검색 if (bossHealthBarUI == null) { - bossHealthBarUI = FindObjectOfType(); + bossHealthBarUI = FindFirstObjectByType(); if (bossHealthBarUI == null) { Debug.LogWarning($"[BossArea] {name}: BossHealthBarUI를 찾을 수 없습니다.");