LoginSignup
44
39

More than 5 years have passed since last update.

Behaviour TreeをUniRxで実装してみた

Last updated at Posted at 2016-03-13

概要

ゲームのAIに使われるBehaviourTreeの勉強についてのメモです。
BehaviourTreeをUniRxで実装するところまで書きます。

Behavior Tree

参考文献は幻塔戦記グリフォンの AI で使っている Behaviour Treeです。
ゲームのAIを実装をステートマシンで実装すると下記の問題があります。

  • 状態の数が増えると管理すべき遷移の数が膨大に増える。
  • 再利用性が低い。

一方、BehaviourTreeで実装すると下記の利点があります。

  • AIが複雑になっても管理しやすい。
  • 各Nodeの使い回しが可能。

Nodeについて

下記4つの代表的なNodeを組み合わせるとAIが実装できます。

ActionNode

  • 特徴
    • 実行するのみ
    • 子は持てない

アクション.png

DecoratorNode

  • ある条件のチェックを行う
    • 条件が通ったら子を実行して、子が返すステータスを返す。
    • 通らなかったらFailureを返す。
  • 子は一つだけ。

DecoratorNode.png

SelectorNode

  • 成功する子が見つかるまで、子を一つ一つ実行する。
    • 見つかったら処理を止めて、Successを返す。
    • 見つからなかったらFailureを返す。

SelectorNode.png

SequencerNode

  • 順番に子を実行するノード。
    • 子が成功したら次の子の処理に移る。
    • 子が失敗したらすぐにFailureを返す。
    • 全てのノードが成功したらSuccessを返す。

SequencerNode.png

サンプル

今回実装するのは下記のようなAIです。動きの流れとしてはこうなります。
1. 自分のHPが10より大きい場合は、5m以内の敵を攻撃
2. 10以下の場合はいちばん近くのタワーに移動して待機
3. 行動が完了したら1から同じ行動を繰り返す。

BehaviorTree.png

下記がUnityのサンプルコードの一部です。
全ソースコードはGitHub上にあります。
BehaviourtreeSample

BehaviourTreeSample.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;

namespace BehaviourTrees
{
    /// <summary>
    /// BehaviourTreeのサンプル。
    /// </summary>
    public class BehaviourTreeSample : MonoBehaviour {

        BehaviourTreeInstance node;

        void Start(){

            var attackNode = new DecoratorNode(IsHpLessThan, new ActionNode(AtackEmeny));

            var moveTowerNode = new SequencerNode(new BehaviourTreeBase[]{
                new ActionNode(MoveNearTower),
                new ActionNode(Wait)}
                );

            var rootNode = new SelectorNode(new BehaviourTreeBase[]{
                attackNode,
                moveTowerNode}
            );


            node = new BehaviourTreeInstance(rootNode);
            node.finishRP.Where(p=>p!=BehaviourTreeInstance.NodeState.READY).Subscribe(p=>ResetCoroutineStart());
            node.Excute();
        }


        private ExecutionResult IsHpLessThan(BehaviourTreeInstance instance){
            var enemyhp = Random.Range(0,10);
            if(enemyhp <= 4){
                Debug.Log("敵のHPが4以下。チャンス。");
                return new ExecutionResult(true);
            }
            Debug.Log("敵のHPが4より大きい。まだ慌てる時間じゃない。");
            return new ExecutionResult(false);
        }


        private ExecutionResult AtackEmeny(BehaviourTreeInstance instance)
        {

            Debug.Log("敵を攻撃する");

            return new ExecutionResult(true);
        }


        private ExecutionResult MoveNearTower(BehaviourTreeInstance instance)
        {

            Debug.Log("一番近くのタワーにいく。");

            return new ExecutionResult(true);
        }


        private ExecutionResult Wait(BehaviourTreeInstance instance)
        {

            Debug.Log("待機。");

            return new ExecutionResult(true);
        }


        void ResetCoroutineStart(){
            StartCoroutine(WaitCoroutine());
        }


        IEnumerator WaitCoroutine(){
            yield return new WaitForSeconds(1.5f);
            node.Reset();
        }
    }
}
44
39
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
39