Game AI

Behavior Tree (2) - 확률, Decorator, 리소스 보호

tsyang 2022. 8. 27. 17:00

이전 글 : 2022.08.21 - [Game AI] - Behavior Tree (1) - 기초

 

Behavior Tree (1) - 기초

2021.12.27 - [Game AI] - 의사 결정 - State machines 의사 결정 - State machines 2021.10.21 - [Game AI] - 의사 결정 - Decision Tree 의사 결정 - Decision Tree Decision Tree Decision Tree는 Decision Mak..

tsyang.tistory.com

 

확률적으로 하위 Task 수행


당연하게도, Selector나 Sequence에서는 Child Task를 랜덤한 순서로 실행시킬 필요가 있다. 이런 경우 Selector나 Sequecne가 수행되기 전에 자식 Task들을 한 번 셔플 해주면 된다.

 

public class RandomSelector : Selector
{
    public override bool Run()
    {
        children.Suffle();
        return base.Run();
    }
}

 

 

 

Decorator


이전 글에서 Behavior tree의 세 가지 요소인 Condition, Action, Composite이 있다고 했다. 이 외에도 중요한 Decorator라는 개념이 있다.

 

Decorator 역시 Task의 한 타입이다. Decorator는 하나의 child task를 가지고 있으며 child task의 기능을 수정한다.

 

대표적인 예로는 child task를 수행할지 말지 정하는 'filter'가 있다. filter는 child task를 '실패할 때까지 반복'하는 등의 조건을 정해줄 수 있다.

 

 

 

public abstract class Decorator : Task
{
    public Task child;
}

//실패할때까지 반복
public class UntilFail : Decorator
{
    public override bool Run()
    {
        while (child.Run()) { }

        return false;
    }
}

 

 

 

 

리소스 보호


Behavior Tree의 Task들이 기능을 수행할 때 한정된 리소스에 접근해야 할 때가 있다. 가장 대표적인 예는 캐릭터의 애니메이션 상태 같은 것이다.

 

 이런 경우 Sequence와 Condition을 통해 리소스 접근을 보호할 수 있다.

 

 

 

그러나, 이런 방식은 Behavior Tree를 제작하는 사람에게 의존하기 때문에 에러가 발생할 확률이 높다.

 

그래서 세마포어(Semaphore) 같은 Lock을 사용하는 게 좋다.

 

우리가 사용할 세마포어는 다음과 같은 인터페이스를 가지고 있다고 가정하자. 

 

class Semaphore
{
    //동시에 리소스를 취득할 수 있는 오브젝트 수
    private int maxConcurrent;

    //리소스 취득에 성공하면 true를 반환한다.
    public bool Aquire();
    
    //리소스를 반납한다.
    public void Release();
}

언어마다 세마포어 라이브러리를 지원하니 그걸 사용하는 게 좋다. 그러나 위의 세마포어 인터페이스는 스레드를 블로킹하지는 않는다는 것에 유의하자. (그냥 false 리턴임)

 

 

그렇다면 리소스 취득을 제한하는 클래스는 다음과 같이 만들 수 있다.

public class SemaphoreGuard : Decorator
{
    private Semaphore _semaphore;
    
    public override bool Run()
    {
        if (_semaphore.Aquire())
        {
            bool result = child.Run();
            _semaphore.Release();
            return result;
        }

        return false;
    }
}

 

또한 여러 개의 Decorator에게 세마포어를 제공하기 위해서 일종의 세마포어 Factory를 만드는 것이 보편적이다. 

class SemaphoreFactory
{
    Dictionary<string, Semaphore> _semaphoreHashTable;
    
    public Semaphore GetSemaphore(string name, int maxConcurrent)
    {
        if (false == _semaphoreHashTable.ContainsKey(name))
            _semaphoreHashTable[name] = new Semaphore(maxConcurrent);

        return _semaphoreHashTable[name];
    }
}

 


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