diff --git a/Assets/Scripts/AI.meta b/Assets/Scripts/AI.meta new file mode 100644 index 00000000..f05f5535 --- /dev/null +++ b/Assets/Scripts/AI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd7f6bc7e9d2e9140802e1bc3a3ebffd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AI/BehaviorActions.meta b/Assets/Scripts/AI/BehaviorActions.meta new file mode 100644 index 00000000..a893af4f --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7648a6805082131489f501769b3c0f18 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AI/BehaviorActions/Actions.meta b/Assets/Scripts/AI/BehaviorActions/Actions.meta new file mode 100644 index 00000000..cc859693 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 09f6a44dd1b90c348a0dfb17312c5804 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/ChaseTargetAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/ChaseTargetAction.cs new file mode 100644 index 00000000..eb5f4408 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/ChaseTargetAction.cs @@ -0,0 +1,76 @@ +using System; +using Unity.Behavior; +using UnityEngine; +using Action = Unity.Behavior.Action; +using Unity.Properties; + +[Serializable, GeneratePropertyBag] +[NodeDescription(name: "ChaseTarget", story: "타겟 추적", category: "Action", id: "0889fbb015b8bf414ef569af08bb6868")] +public partial class ChaseTargetAction : Action +{ + + [SerializeReference] + public BlackboardVariable Target; + + [SerializeReference] + public BlackboardVariable Speed = new BlackboardVariable(0f); + + [SerializeReference] + public BlackboardVariable StopDistance = new BlackboardVariable(2f); + + private UnityEngine.AI.NavMeshAgent agent; + + protected override Status OnStart() + { + if (Target.Value == null) + { + return Status.Failure; + } + + agent = GameObject.GetComponent(); + if (agent == null) + { + Debug.LogWarning("[ChaseTarget] NavMeshAgent not found"); + return Status.Failure; + } + + // Speed가 0 이하면 NavMeshAgent의 기존 speed 유지 (EnemyData에서 설정한 값) + if (Speed.Value > 0f) + { + agent.speed = Speed.Value; + } + agent.stoppingDistance = StopDistance.Value; + agent.isStopped = false; + + return Status.Running; + } + + protected override Status OnUpdate() + { + if (Target.Value == null) + { + return Status.Failure; + } + + // 이미 사거리 내에 있으면 성공 + float distance = Vector3.Distance(GameObject.transform.position, Target.Value.transform.position); + if (distance <= StopDistance.Value) + { + agent.isStopped = true; + return Status.Success; + } + + // 타겟 위치로 이동 + agent.SetDestination(Target.Value.transform.position); + return Status.Running; + } + + protected override void OnEnd() + { + if (agent != null) + { + agent.isStopped = true; + } + } +} + diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/ChaseTargetAction.cs.meta b/Assets/Scripts/AI/BehaviorActions/Actions/ChaseTargetAction.cs.meta new file mode 100644 index 00000000..1312495c --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/ChaseTargetAction.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 14296786101ccd742ac9f752f1fd3393 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs new file mode 100644 index 00000000..a6258db8 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs @@ -0,0 +1,35 @@ +using System; +using Unity.Behavior; +using UnityEngine; +using Action = Unity.Behavior.Action; +using Unity.Properties; + +[Serializable, GeneratePropertyBag] +[NodeDescription(name: "FindTarget", story: "[타겟] 탐색", category: "Action", id: "bb947540549026f3c5625c6d19213311")] +public partial class FindTargetAction : Action +{ + [SerializeReference] + public BlackboardVariable Target; + + [SerializeReference] + public BlackboardVariable Tag = new BlackboardVariable("Player"); + + protected override Status OnStart() + { + if (Tag.Value == null || string.IsNullOrEmpty(Tag.Value)) + { + return Status.Failure; + } + + GameObject foundTarget = GameObject.FindGameObjectWithTag(Tag.Value); + + if (foundTarget == null) + { + return Status.Failure; + } + + Target.Value = foundTarget; + return Status.Success; + } +} + diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs.meta b/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs.meta new file mode 100644 index 00000000..593cce34 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/FindTargetAction.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f569cb803bceb7c4291d0f3074346741 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/PlayAnimationAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/PlayAnimationAction.cs new file mode 100644 index 00000000..377dda62 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/PlayAnimationAction.cs @@ -0,0 +1,63 @@ +using System; +using Unity.Behavior; +using UnityEngine; +using Action = Unity.Behavior.Action; +using Unity.Properties; + +[Serializable, GeneratePropertyBag] +[NodeDescription(name: "PlayAnimation", story: "[애니메이션] 재생", category: "Action", id: "b558ee0df6e00cd2e6bb2273b4f59cd2")] +public partial class PlayAnimationAction : Action +{ + [SerializeReference] + public BlackboardVariable AnimationName = new BlackboardVariable(""); + + [SerializeReference] + public BlackboardVariable WaitForCompletion = new BlackboardVariable(true); + + private Animator animator; + private bool animationStarted; + + protected override Status OnStart() + { + animator = GameObject.GetComponentInChildren(); + + if (animator == null || string.IsNullOrEmpty(AnimationName.Value)) + { + return Status.Failure; + } + + animator.Play(AnimationName.Value); + animationStarted = true; + + if (!WaitForCompletion.Value) + { + return Status.Success; + } + + return Status.Running; + } + + protected override Status OnUpdate() + { + if (!WaitForCompletion.Value) + { + return Status.Success; + } + + if (animator == null) + { + return Status.Failure; + } + + var stateInfo = animator.GetCurrentAnimatorStateInfo(0); + + // 애니메이션이 완료되었는지 확인 + if (stateInfo.IsName(AnimationName.Value) && stateInfo.normalizedTime >= 1f) + { + return Status.Success; + } + + return Status.Running; + } +} + diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/PlayAnimationAction.cs.meta b/Assets/Scripts/AI/BehaviorActions/Actions/PlayAnimationAction.cs.meta new file mode 100644 index 00000000..3dcc558a --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/PlayAnimationAction.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3bc57afe07d75aa4e80ef5394b57f774 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/RotateToTargetAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/RotateToTargetAction.cs new file mode 100644 index 00000000..2c646bd4 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/RotateToTargetAction.cs @@ -0,0 +1,54 @@ +using System; +using Unity.Behavior; +using UnityEngine; +using Action = Unity.Behavior.Action; +using Unity.Properties; + +[Serializable, GeneratePropertyBag] +[NodeDescription(name: "RotateToTarget", story: "[대상을] 바라보도록 회전", category: "Action", id: "30341f7e1af0565c0aca0253341b3e28")] +public partial class RotateToTargetAction : Action +{ + [SerializeReference] + public BlackboardVariable Target; + + [SerializeReference] + public BlackboardVariable RotationSpeed = new BlackboardVariable(10f); + + [SerializeReference] + public BlackboardVariable AngleThreshold = new BlackboardVariable(5f); + + protected override Status OnUpdate() + { + if (Target.Value == null) + { + return Status.Failure; + } + + Vector3 direction = Target.Value.transform.position - GameObject.transform.position; + direction.y = 0f; + + if (direction == Vector3.zero) + { + return Status.Success; + } + + Quaternion targetRotation = Quaternion.LookRotation(direction); + float angleDifference = Quaternion.Angle(GameObject.transform.rotation, targetRotation); + + // 임계값 이내면 성공 + if (angleDifference <= AngleThreshold.Value) + { + return Status.Success; + } + + // 부드럽게 회전 + GameObject.transform.rotation = Quaternion.Slerp( + GameObject.transform.rotation, + targetRotation, + RotationSpeed.Value * Time.deltaTime + ); + + return Status.Running; + } +} + diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/RotateToTargetAction.cs.meta b/Assets/Scripts/AI/BehaviorActions/Actions/RotateToTargetAction.cs.meta new file mode 100644 index 00000000..842cdc5f --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/RotateToTargetAction.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 33182146e0ade8443a539cf7780735e2 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/SetAnimatorTriggerAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/SetAnimatorTriggerAction.cs new file mode 100644 index 00000000..f0c4f3d0 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/SetAnimatorTriggerAction.cs @@ -0,0 +1,33 @@ +using System; +using Unity.Behavior; +using UnityEngine; +using UnityEngine.EventSystems; +using Action = Unity.Behavior.Action; +using Unity.Properties; + +[Serializable, GeneratePropertyBag] +[NodeDescription(name: "SetAnimatorTrigger", story: "애니메이션 [트리거] 설정", category: "Action", id: "bfef104cbd43df3d01f24570a4caa8ed")] +public partial class SetAnimatorTriggerAction : Action +{ + [SerializeReference] + public BlackboardVariable TriggerName = new BlackboardVariable(""); + + protected override Status OnStart() + { + if (string.IsNullOrEmpty(TriggerName.Value)) + { + return Status.Failure; + } + + Animator animator = GameObject.GetComponentInChildren(); + + if (animator == null) + { + return Status.Failure; + } + + animator.SetTrigger(TriggerName.Value); + return Status.Success; + } +} + diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/SetAnimatorTriggerAction.cs.meta b/Assets/Scripts/AI/BehaviorActions/Actions/SetAnimatorTriggerAction.cs.meta new file mode 100644 index 00000000..9c4bedb0 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/SetAnimatorTriggerAction.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 77cf4f2ff35fe3040af16374f428a648 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs new file mode 100644 index 00000000..0acbe056 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs @@ -0,0 +1,62 @@ +using System; +using Unity.Behavior; +using UnityEngine; +using Action = Unity.Behavior.Action; +using Unity.Properties; + +[Serializable, GeneratePropertyBag] +[NodeDescription(name: "SetTargetInRange", story: "[거리] 내에 [대상이] 있는지 확인", category: "Action", id: "93b7a5d823a58618d5371c01ef894948")] +public partial class SetTargetInRangeAction : Action +{ + [SerializeReference] + public BlackboardVariable Target; + + [SerializeReference] + public BlackboardVariable Tag = new BlackboardVariable("Player"); + + [SerializeReference] + public BlackboardVariable Range = new BlackboardVariable(10f); + + protected override Status OnStart() + { + if (string.IsNullOrEmpty(Tag.Value)) + { + return Status.Failure; + } + + // 모든 타겟 태그 오브젝트 찾기 + GameObject[] targets = GameObject.FindGameObjectsWithTag(Tag.Value); + + if (targets == null || targets.Length == 0) + { + return Status.Failure; + } + + // 가장 가까운 타겟 찾기 + GameObject nearestTarget = null; + float nearestDistance = Range.Value; // Range 내에서만 검색 + + foreach (GameObject potentialTarget in targets) + { + float distance = Vector3.Distance( + GameObject.transform.position, + potentialTarget.transform.position + ); + + if (distance < nearestDistance) + { + nearestDistance = distance; + nearestTarget = potentialTarget; + } + } + + if (nearestTarget == null) + { + return Status.Failure; + } + + Target.Value = nearestTarget; + return Status.Success; + } +} + diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs.meta b/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs.meta new file mode 100644 index 00000000..8c27aa31 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/SetTargetInRangeAction.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 02cd3ab41f67bf344b667b6a0c12a4d0 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/StopMovementAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/StopMovementAction.cs new file mode 100644 index 00000000..972a7b78 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/StopMovementAction.cs @@ -0,0 +1,24 @@ +using System; +using Unity.Behavior; +using UnityEngine; +using Action = Unity.Behavior.Action; +using Unity.Properties; + +[Serializable, GeneratePropertyBag] +[NodeDescription(name: "StopMovement", story: "이동 정지", category: "Action", id: "1a0e2eb87421ed94502031790df56f37")] +public partial class StopMovementAction : Action +{ + + protected override Status OnStart() + { + UnityEngine.AI.NavMeshAgent agent = GameObject.GetComponent(); + + if (agent != null) + { + agent.isStopped = true; + } + + return Status.Success; + } +} + diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/StopMovementAction.cs.meta b/Assets/Scripts/AI/BehaviorActions/Actions/StopMovementAction.cs.meta new file mode 100644 index 00000000..fc571441 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/StopMovementAction.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: afacb51ec60675d498bfc8b7ce942368 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/WaitAction.cs b/Assets/Scripts/AI/BehaviorActions/Actions/WaitAction.cs new file mode 100644 index 00000000..99dddb87 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/WaitAction.cs @@ -0,0 +1,31 @@ +using System; +using Unity.Behavior; +using UnityEngine; +using Action = Unity.Behavior.Action; +using Unity.Properties; + +[Serializable, GeneratePropertyBag] +[NodeDescription(name: "Wait", story: "대기", category: "Action", id: "73b84bd4bc0eb61c998ac84c9853a69d")] +public partial class WaitAction : Action +{ + [SerializeReference] + public BlackboardVariable Duration = new BlackboardVariable(1f); + + private float startTime; + + protected override Status OnStart() + { + startTime = Time.time; + return Status.Running; + } + + protected override Status OnUpdate() + { + if (Time.time - startTime >= Duration.Value) + { + return Status.Success; + } + return Status.Running; + } +} + diff --git a/Assets/Scripts/AI/BehaviorActions/Actions/WaitAction.cs.meta b/Assets/Scripts/AI/BehaviorActions/Actions/WaitAction.cs.meta new file mode 100644 index 00000000..9ccbe83a --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Actions/WaitAction.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 324b69471491c9448ae5d71a426dd596 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions.meta b/Assets/Scripts/AI/BehaviorActions/Conditions.meta new file mode 100644 index 00000000..09ead66f --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 422be729ebdf10b43b44b80b8593258a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs b/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs new file mode 100644 index 00000000..2b4daa1c --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs @@ -0,0 +1,24 @@ +using System; +using UnityEngine; +using Unity.Behavior; +using Unity.Properties; +using Condition = Unity.Behavior.Condition; + +namespace Colosseum.AI.BehaviorActions.Conditions +{ + /// + /// 타겟이 존재하는지 확인합니다. + /// + [Serializable, GeneratePropertyBag] + [NodeDescription(name: "Has Target", story: "Has [Target]", category: "Combat")] + public partial class HasTargetCondition : Condition + { + [SerializeReference] + public BlackboardVariable Target; + + public override bool IsTrue() + { + return Target.Value != null && Target.Value.activeInHierarchy; + } + } +} diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs.meta b/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs.meta new file mode 100644 index 00000000..71c37fdb --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/HasTargetCondition.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5aba729f8b5ddea468304d6b1bf43dce \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/IsHealthBelowCondition.cs b/Assets/Scripts/AI/BehaviorActions/Conditions/IsHealthBelowCondition.cs new file mode 100644 index 00000000..3afa6635 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/IsHealthBelowCondition.cs @@ -0,0 +1,33 @@ +using System; +using UnityEngine; +using Unity.Behavior; +using Unity.Properties; +using Condition = Unity.Behavior.Condition; +using Colosseum.Enemy; + +namespace Colosseum.AI.BehaviorActions.Conditions +{ + /// + /// 체력이 지정된 비율 이하인지 확인합니다. + /// + [Serializable, GeneratePropertyBag] + [NodeDescription(name: "Is Health Below", story: "Check if health is below [HealthPercent] percent", category: "Combat")] + public partial class IsHealthBelowCondition : Condition + { + [SerializeReference] + public BlackboardVariable HealthPercent = new BlackboardVariable(50f); + + public override bool IsTrue() + { + EnemyBase enemy = GameObject.GetComponent(); + + if (enemy == null) + { + return false; + } + + float currentHealthPercent = (enemy.CurrentHealth / enemy.MaxHealth) * 100f; + return currentHealthPercent <= HealthPercent.Value; + } + } +} diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/IsHealthBelowCondition.cs.meta b/Assets/Scripts/AI/BehaviorActions/Conditions/IsHealthBelowCondition.cs.meta new file mode 100644 index 00000000..e87c8b39 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/IsHealthBelowCondition.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e03ad43d6a068dd44bef3d524d20a4c1 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/IsInAttackRangeCondition.cs b/Assets/Scripts/AI/BehaviorActions/Conditions/IsInAttackRangeCondition.cs new file mode 100644 index 00000000..718d0084 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/IsInAttackRangeCondition.cs @@ -0,0 +1,37 @@ +using System; +using UnityEngine; +using Unity.Behavior; +using Unity.Properties; +using Condition = Unity.Behavior.Condition; + +namespace Colosseum.AI.BehaviorActions.Conditions +{ + /// + /// 타겟이 공격 사거리 내에 있는지 확인합니다. + /// + [Serializable, GeneratePropertyBag] + [NodeDescription(name: "Is In Attack Range", story: "Is [Target] within [Range]", category: "Combat")] + public partial class IsInAttackRangeCondition : Condition + { + [SerializeReference] + public BlackboardVariable Target; + + [SerializeReference] + public BlackboardVariable Range = new BlackboardVariable(2f); + + public override bool IsTrue() + { + if (Target.Value == null) + { + return false; + } + + float distance = Vector3.Distance( + GameObject.transform.position, + Target.Value.transform.position + ); + + return distance <= Range.Value; + } + } +} diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/IsInAttackRangeCondition.cs.meta b/Assets/Scripts/AI/BehaviorActions/Conditions/IsInAttackRangeCondition.cs.meta new file mode 100644 index 00000000..cf3fc203 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/IsInAttackRangeCondition.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 10007872b79b2b641980c0d8dfd4f6a4 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/IsInRangeCondition.cs b/Assets/Scripts/AI/BehaviorActions/Conditions/IsInRangeCondition.cs new file mode 100644 index 00000000..1296adbe --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/IsInRangeCondition.cs @@ -0,0 +1,33 @@ +using System; +using UnityEngine; +using Unity.Behavior; +using Unity.Properties; +using Condition = Unity.Behavior.Condition; + +namespace Colosseum.AI.BehaviorActions.Conditions +{ + /// + /// 타겟이 지정된 거리 내에 있는지 확인합니다. + /// + [Serializable, GeneratePropertyBag] + [NodeDescription(name: "Is In Range", story: "Is [Target] within [Range] distance", category: "Combat")] + public partial class IsInRangeCondition : Condition + { + [SerializeReference] + public BlackboardVariable Target; + + [SerializeReference] + public BlackboardVariable Range = new BlackboardVariable(2f); + + public override bool IsTrue() + { + if (Target.Value == null) + { + return false; + } + + float distance = Vector3.Distance(GameObject.transform.position, Target.Value.transform.position); + return distance <= Range.Value; + } + } +} diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/IsInRangeCondition.cs.meta b/Assets/Scripts/AI/BehaviorActions/Conditions/IsInRangeCondition.cs.meta new file mode 100644 index 00000000..4f3aa074 --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/IsInRangeCondition.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0035b82da5a602d44b552684970273a8 \ No newline at end of file diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/RandomChanceCondition.cs b/Assets/Scripts/AI/BehaviorActions/Conditions/RandomChanceCondition.cs new file mode 100644 index 00000000..4d51896d --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/RandomChanceCondition.cs @@ -0,0 +1,25 @@ +using System; +using UnityEngine; +using Unity.Behavior; +using Unity.Properties; +using Condition = Unity.Behavior.Condition; + +namespace Colosseum.AI.BehaviorActions.Conditions +{ + /// + /// 무작위 확률로 성공/실패를 반환합니다. + /// + [Serializable, GeneratePropertyBag] + [NodeDescription(name: "Random Chance", story: "Random chance of [Chance] percent", category: "Utility")] + public partial class RandomChanceCondition : Condition + { + [SerializeReference] + public BlackboardVariable Chance = new BlackboardVariable(50f); + + public override bool IsTrue() + { + float roll = UnityEngine.Random.Range(0f, 100f); + return roll <= Chance.Value; + } + } +} diff --git a/Assets/Scripts/AI/BehaviorActions/Conditions/RandomChanceCondition.cs.meta b/Assets/Scripts/AI/BehaviorActions/Conditions/RandomChanceCondition.cs.meta new file mode 100644 index 00000000..80e11b7f --- /dev/null +++ b/Assets/Scripts/AI/BehaviorActions/Conditions/RandomChanceCondition.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3abe3d08ab4653b43a5a1708770fd3a1 \ No newline at end of file