Behaviour Tree

행동 트리(Behaviour Tree)에 대해서 알아보자.

1. Composite Task
노드를 합성해서 흐름을 제어한다.
기본적으로 node 의 실행 순서는 위에서 아래로, 왼쪽에서 오른쪽이다.
주요 Composite은 다음과 같은것이 있다.

Selector :  노드가 하나라도 성공하면 부모노드에게 성공을 반환
               OR 연산자
Sequencer : 모든 자식 노드가 성공하면 부모노드로 성공 반환
                 노드가 실패하면 부모노드로 실패 반환
                 AND 연산자

2. Action Task
실제 행동하는 단말 노드

주의할점 :
단말 액션 노드가 true나 false를 반환하지 않으면 계속해서 해당 노드에 머물게 된다.
이럴때는 내부에서 종료 조건을 주던지 외부에서 강제적으로 액션을 중단 할필요가 있다.

3. Decoration Task
하나의 자식을 가지며, 조건이 만족하면 자식을 실행하고 조건을 만족하지 않으면 false를 반환한다.

[ Selector와 Sequencer를 기초 테스트 코드  ]
using System;
using System.Collections.Generic;

namespace testCSharp
{
    class BehaviourTreeTest
    {
        List<Func<bool>> selector = new List<Func<bool>>();
        List<Func<bool>> sequencer = new List<Func<bool>>();

        void Print(string str)
        {
            Console.WriteLine(str);
        }

        public void Init()
        {
            selector.Add(() => { Print("eat"); return false; });
            selector.Add(() => { Print("bathe"); return true; });
            selector.Add(() => { Print("bed"); return true; });

            sequencer.Add(() => { Print("eat"); return true; });
            sequencer.Add(() => { Print("bathe"); return true; });
            sequencer.Add(() => { Print("bed"); return false; });
        }

        public void RunSelector()
        {
            foreach(var node in selector)
            {
                if (node() == true)
                {
                    Print("success");
                    break;
                }


                Print("-------------------------------------------------------");
            }
        }

        public void RunSequencer()
        {
            foreach (var node in sequencer)
            {
                if (node() == false)
                    break;

                Print("success");
                Print("-------------------------------------------------------");
            }
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            BehaviourTreeTest tree = new BehaviourTreeTest();
            tree.Init();
            Console.WriteLine("<< Selector >>");
            tree.RunSelector();
            Console.WriteLine("\n\n\n<< Sequencer >>");
            tree.RunSequencer();

            Console.ReadKey();
        }
    }
}

출력
<< Selector >>
eat
-------------------------------------------------------
bathe
success



<< Sequencer >>
eat
success
-------------------------------------------------------
bathe
success
-------------------------------------------------------
bed



다음 그림과 같은 행동을 가지는 Behaviour Tree를 코드로 간략하게 만들어 보자.



using System;
using System.Collections.Generic;

namespace BT
{
    public abstract class Node
    {
        public abstract bool Run();
    }

    public abstract class CompositeTask : Node
    {
        List<Node> childList = new List<Node>();
        protected List<Node> GetChild() { return childList; }
        public void AddChild (Node node)
        {
            childList.Add(node);
        }
    }

    public abstract class ActionTask : Node
    {
    }

    public class Selector : CompositeTask
    {
        public override bool Run()
        {
            foreach(Node child in GetChild())
            {
                if (child.Run())
                    return true;
            }
            
            return false; 
        }
    }

    public class Sequence : CompositeTask
    {
        public override bool Run()
        {
            foreach(Node child in GetChild())
            {
                if (child.Run() == false)
                    return false;
            }
           
            return true;
        }
    }

    public class CheckMonster : ActionTask
    {
        public override bool Run()
        {
            Console.WriteLine("CheckMonster true");
            return true;
        }
    }

    public class AttackMonster : ActionTask
    {
        public override bool Run()
        {
            Console.WriteLine("AttackMonster false");
            return false;
        }
    }

    public class Rest : ActionTask
    {
        public override bool Run()
        {
            Console.WriteLine("Rest false");
            return false;
        }
    }

    public class Wander : ActionTask
    {
        public override bool Run()
        {
            Console.WriteLine("Wander true");
            return true;
        }
    }

}

namespace testCSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            BT.Selector rootSelect = new BT.Selector();
           
            BT.Sequence seq = new BT.Sequence();
            seq.AddChild(new BT.CheckMonster());
            seq.AddChild(new BT.AttackMonster());
            seq.AddChild(new BT.Rest());

            rootSelect.AddChild(seq);
            rootSelect.AddChild(new BT.Wander());

            Console.WriteLine("===========> Behaviour Tree = {0}", rootSelect.Run());

            Console.ReadKey();
        }
    }

출력
CheckMonster true
AttackMonster false
Wander true
===========> Behaviour Tree = True


참조)
smileeagle님이 작성한 PDF 문서와 링크입니다.
BTs를 이용한 몬스터 서버 AI 개선.pdf
게임코드 - [AI] BehaviorTree를 이용해 몬스터 AI를 구성해 보자.

http://aigamedev.com/open/article/bt-overview/
http://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php

http://labs.gree.jp/blog/2015/06/14026/
http://m.blog.naver.com/aaasssddd25/220795379268
http://pgrblues.egloos.com/m/4338077

기본 코드 깔끔
http://kindtis.tistory.com/590

용어 설명
http://lifeisforu.tistory.com/327

유니티 C#에서 Behaviour Tree를 시리얼라이즈 하기
http://davidlegare.ghost.io/behavior-trees-1/
http://davidlegare.ghost.io/unity-behavior-trees-pt-2/
http://davidlegare.ghost.io/unity-behavior-trees-pt-3-better-serialization/

유니티  Behave 플러그인
http: //e-hacking.org/2016/03/21/behavior-tree-행동트리/

코드만
http://www.cplusplus.com/forum/general/141582/