[Unity] 몬스터 BT 노드 데이터

잘생김·2024년 2월 20일

어째서..

몬스터 BT는 DFS(Depth First Search : 깊이우선탐색) 구조로 구성되었다.
현재 기본동작으로 idle, die, track, attack을 구현했지만 이후 플레이어의 패링시 피격(hit)이라던지 attack 모션도 조건문으로 구성되는 다양한 공격들이 존재한다.

INode SettingBT()
{
    return new SelectorNode
    (
        new List<INode>()
        {
            new SequenceNode
            (
                new List<INode>()
                {
                    new ConditionNode(() => bossHP ==0, BossDie),
                    new ActionNode(BossTrack),
                    new ActionNode(BossNornalAttack1),
                }
            ),
            new ActionNode(BossIdle)
        }
    );
}

조건에 조건으로 계속 들어간다면 이 구조는 한눈에 보기가 어려워진다.
그러면 어떻게 하죠..?
노드를 데이터구조화 시키면 어떨까?
노드를 세팅하고 각 노드에 데이터를 빌드하는 구조라면 앞으로 더 추가될 조건들과 구성 노드를 구성하기 편리할거같다(?)

그렇다면 각 노드를 어떻게 데이터화 시킬것인가?

JSON으로 관리시..

Json
"키" : {"값"}

"selector": { "type", "child" }
"sequence": { "child" }
"if": { "trueCondition", "action"}

attack의 "selector"는 무작위로 구현된다는 조건이 있어 "selector"는 타입이 존재해야한다.

SelectorType

  • Normal(0) => 우선 순위 높은 것부터 차례대로 노드를 평가한다.
  • Random(1) => 자식 중에서 무작위로 선택하여 평가한다.
{
    "selector" : {
        "type": 0,
        "child": {
            "selector": {
                "type": 0,
                "child": {
                    "if": {
                        "trueCondition": "IsEmptyHp",
                        "action": "BossDie"
                        .
                        .
                        .
                    },
                    "BossAttack",
                    "BossTrack"
                }
            }
        } 
        
    }
}

보기 어렵다.

다른 프로젝트의 BT는 어떤식으로 데이터구성을 했을까?
배열에 모든 노드를 저장해서 쓴다.

디자인 패턴
빌더 패턴

  • 객체를 단계별로 생성할 수 있다.
    빌더패턴 적용시 파라미터를 메소드 체인징으로 구성
enum MinotaurosActionType
{
    Die,
    Attack,
    Track,
    Idle
}

[Die][Attack][Track][Idle]

class MinotaurosBuilder : Builder
{
    void Init()
    {
        MinotaurosActionType을 기반으로 배열에 액션 노드를 저장한다.
    }

    override void Build()
    {
        selector5 = Selector.
                    AddChild((GetNode((int)MinotaurosActionType.NormalAttack1))).
                    AddChild((GetNode((int)MinotaurosActionType.NormalAttack2))).
                    AddChild((GetNode((int)MinotaurosActionType.ComboAttack1))).
                    AddChild((GetNode((int)MinotaurosActionType.ComboAttack2))).
                    SetType(SelectorType.Random).
                    Build();
        
        sequence = Sequence.
                    AddChild((GetNode((int)MinotaurosActionType.Backstep))).
                    AddChild((GetNode((int)MinotaurosActionType.Stumping))).
                    Build();

        selector4 = Selector.
                    AddChild(sequence.SetCondition(player is behind me)).
                    AddChild(GetNode((int)MinotaurosActionType.KickAttack).SetCondition(player is defense)).
                    AddChild(selector5).
                    Build();
    
        selector3 = ControlNodeBuilder.
                    AddChild(selctor4.SetCondition(player is in attack range)).
                    AddChild(GetNode((int)MinotaurosActionType.JumpAttack).SetCondition(player is out of attack range)).
                    Build<Selector>();
        
        selector2 = Selector.
                    AddChild(GetNode(Die).SetCondition(hp == 0)).
                    AddChild(GetNode(Hit).SetCondition(방어 상태)).
                    AddChild(selector3).
                    AddChild(GetNode(BossTrack).SetCondition(공격 범위 내에 없다면)).
                    Build();
        
        selector1 = Selector.
                    AddChild(selector2.SetCondition(보스룸 진입)).
                    AddCConhild(GetNode(Idle)).
                    Build();

        Root.child = selector1;           
    }
}

노드 생성 - 노드 빌드 - 데이터 입력으로 빌드한다.

profile
비전공자가 개발자로 취업하기 위해

0개의 댓글