public enum State
{
Idle,
Move,
Jump
}
public class Player : MonoBehaviour
{
State currentState;
void Update()
{
switch (currentState)
{
case State.Idle:
Idle();
break;
case State.Jump:
Jump();
break;
}
}
}
상태(State)수가 적을 때(~3개)는 사용하기 적당함, 많으면 복잡
상태(State)수가 늘수록 코드 복잡도 폭증
상태(State) -> 전이(Transition) 연결
상태(State)가 많아질수록 비슷한상태(State)간 조건 중복이 많이 됨
상태(State)들간 결합도가 커질 수 있음
루트 노드 -> 자식 노드를 순서대로 조건부 판단
Node들로 이뤄져있다.
public enum NodeState { Success, Failure, Running}
public abstract class Node
{
public abstract NodeState Evaluate();
}
Evaluate()실행 결과를 반환
루트 노드가 매 프레임(또는 지정 주기)에 발행하는 평가 호출으로, 트리 전체를 한 번 평가
Success / Failure / Running)에 따라 다음 자식에게 틱을 넘길지 상위로 결과를 반환할지 결정작업(Action) 또는 조건 판단(Condition) 을 수행하는 단위
Success : 작업 완료 또는 조건 충족Failure : 작업 불가 또는 조건 불충족Running : 작업 진행 중 → 다음 틱에서 재평가Success / Failure만 반환하고, Action 노드가 장기 작업 시 Running을 반환할 수 있다.Failure 반환Success 반환Running을 반환하면 Running을 반환하고 다음 틱에서 다시 호출Success나 Running을 반환하면 즉시 중단하고 그 상태를 반환 (어느 순서에 나오든 Success나 Running이면 중단)Failure 반환Success / Failure를 반환Success / Failure / Running 을 반환Enemy가 Player 추적 및 공격 구현 (탐지범위에 있으면 추적, 공격범위에 있으면 공격)
[Root]
|
[Selector] - OR구조
/ \
[Attack] [Chase]
|
[Sequence] - And 구조
|
[IsTargetNear] - Condition(Leaf)
|
[MoveToTarget] - Action(Leaf)
public enum NodeState:byte { Success, Failure, Running }
public abstract class Node
{
public abstract NodeState Evaluate();
}
using System.Collections.Generic;
public class Sequence : Node
{
List<Node> _childrenList;
public Sequence(List<Node> childrenList)
{
_childrenList = childrenList;
}
public override NodeState Evaluate()
{
foreach (Node node in _childrenList)
{
NodeState result = node.Evaluate();
if (result == NodeState.Failure)
{
return NodeState.Failure;
}
if (result == NodeState.Running)
{
return NodeState.Running;
}
}
return NodeState.Success;
}
}
using System.Collections.Generic;
public class Selector : Node
{
List<Node> _childrenList;
public Selector(List<Node> childrenList)
{
_childrenList = childrenList;
}
public override NodeState Evaluate()
{
foreach (var node in _childrenList)
{
NodeState result = node.Evaluate();
if(result == NodeState.Success)
{
return NodeState.Success;
}
if(result == NodeState.Running)
{
return NodeState.Running;
}
}
return NodeState.Failure; // 기본 상태
}
}
using UnityEngine;
using System.Collections.Generic;
public class EnemyBT : MonoBehaviour
{
[SerializeField] Transform _target;
[SerializeField] float _attackRange = 1.5f;
[SerializeField] float _detectRange = 5f;
[SerializeField] float _moveSpeed = 3f;
Node _rootNode;
void Start()
{
Node attack = new Attack(transform, _target, _attackRange);
Node isNear = new IsTargetNear(transform, _target, _detectRange);
Node move = new MoveToTarget(transform, _target, _moveSpeed);
Sequence chase = new Sequence(new List<Node> { isNear, move });
_rootNode = new Selector(new List<Node> { attack, chase });
}
void Update()
{
_rootNode.Evaluate();
}
}
AttackAction: 공격이 가능한가? -> 가능하다면 공격 (Success/Failure)FSMState PatternBehaviour TreeBT Graph