Game AI

Behavior Tree (1) - 기초

tsyang 2022. 8. 21. 14:57

2021.12.27 - [Game AI] - 의사 결정 - State machines

 

의사 결정 - State machines

2021.10.21 - [Game AI] - 의사 결정 - Decision Tree 의사 결정 - Decision Tree Decision Tree Decision Tree는 Decision Making 구현법 중 하나이다. 빠르고, 쉽게 구현 가능하고 이해하기 쉽다는 장점을 가지..

tsyang.tistory.com

 

 

개요


Behavior Tree는 AI 캐릭터를 만들 때 가장 많이 사용되는 방식 중 하나이다.

 

Behavior Tree의 예

 

Hierarchical State Machine과 유사한 점이 많은데, Behavior Tree에서는 State 대신 Task라는 개념을 쓴다.

 

Task는 sub-tree로 구성되어 있어 복잡한 행동을 표현할 수 있다. 그리고 얘네를 조합해서 더 높은 레벨의 행동을 구현할 수 있다. 

 

GUI를 가진 툴과 함께 사용하기 좋다. 

 

 

 

 

Task


 

Task는 CPU time을 받아서 작업을 수행한다. 그리고 작업의 성공 여부를 리턴한다. 리턴 값에는 '시간이 더 필요해', '에러가 발생했어' 같은 에러 코드가 올 수도 있다.

 

Task는 3가지 종류가 있다. 

  1. Condition
  2. Action
  3. Composite

 

-Condition

Condition은 말 그대로 게임 내에서 조건을 체크한다. 예를 들면 '캐릭터의 10m이내에 적이 있는가?', '남은 체력이 적은가?' 와 같은 조건이다.  보통 매개변수를 통해 쉽게 재사용할 수 있도록 구현된다.

 

조건 체크의 성공여부에 대한 status를 반환한다.

 

-Action

Action은 게임의 상태를 바꾼다. 예를 들면 '캐릭터 이동' , '캐릭터의 상태 변경' 등이 있다. 

 

Action은 Conditon과 달리 대부분 성공한다. 만약 자주 실패한다면 차라리 행동하기 전에 더 나은 Condition을 구현하여 쓰는게 낫다. 

 

 

-Composite

Composite은 Tree에서 자신의 자식 노드들을 실행시킨다. 

 

Composite은 다시 두 개로 나뉜다.

 

  1. Selector : 자식 노드 중 하나라도 성공하면 즉시 리턴. 성공하는 자식 노드가 있을 때 까지 자식 노드들을 순차적으로 실행하며, 모두 실패한다면 실패 상태를 리턴한다.
  2. Sequence : 자식 중 하나가 실패하면 즉시 리턴. 자식 노드들이 수행에 성공하는 한 계속 자식 노드들을 실행시킨다. 만약 모든 자식 노드를 실행시켰다면 성공 상태를 리턴한다.

 

Selector

 

Selector를 이용하여 (숨기, 도망치기, 지원요청) 중 한 가지의 행동만 수행하는 경우.

 

 

Sequence

Sequence를 이용하여 근처에 적이 있다면 엄폐하고 공격하는 경우.

 

 

둘의 적절한 조합

Selector와 Sequence를 조합하여 적이 있는 경우 체력이 많으면 공격하고 아니면 도망친뒤 지원 요청을 하는 경우.

 

 

 

 

구현


모든 Task는 하나의 인터페이스를 상속받는다. 

 

public interface Task
{
    //성공 혹은 실패를 반환
    public bool Run();
}

 

근처에 적이 있는지 체크하는 Condition Task는 다음과 같이 구현할 수 있다.

 

public class EnemyNearCondition : Task
{
    public float TargetDistance = 10;
    public bool Run()
    {
        //정해진 거리 이내에 적이 있다면 true 반환
        return Enemies.DistanceToClosestEnemy <= TargetDistance;
    }
}

 

Selector와 Sequence는 다음과 같이 구현할 수 있다.

public class Selector : Task
{
    public Task[] children;
    public bool Run()
    {
        foreach (var child in children)
            if (child.Run())
                return true;

        return false;
    }
}

public class Sequence : Task
{
    public Task[] children;
    public bool Run()
    {
        foreach (var child in children)
            if (false == child.Run())
                return false;

        return true;
    }
}

 

실제로 사용을 할 때는 리턴 값을 boolean이 아닌 enum같은 더 유연한 값을 쓰는게 좋다.

 


참고 : Ian Millington, AI for GAMES 3rd edition, CRC press, [338~347p]