#概要
ゲームのAIに使われるBehaviourTreeの勉強についてのメモです。
BehaviourTreeをUniRxで実装するところまで書きます。
#Behavior Tree
参考文献は幻塔戦記グリフォンの AI で使っている Behaviour Treeです。
ゲームのAIを実装をステートマシンで実装すると下記の問題があります。
- 状態の数が増えると管理すべき遷移の数が膨大に増える。
- 再利用性が低い。
一方、BehaviourTreeで実装すると下記の利点があります。
- AIが複雑になっても管理しやすい。
- 各Nodeの使い回しが可能。
##Nodeについて
下記4つの代表的なNodeを組み合わせるとAIが実装できます。
###ActionNode
- 特徴
- 実行するのみ
- 子は持てない
###DecoratorNode
- ある条件のチェックを行う
- 条件が通ったら子を実行して、子が返すステータスを返す。
- 通らなかったらFailureを返す。
- 子は一つだけ。
###SelectorNode
- 成功する子が見つかるまで、子を一つ一つ実行する。
- 見つかったら処理を止めて、Successを返す。
- 見つからなかったらFailureを返す。
###SequencerNode
- 順番に子を実行するノード。
- 子が成功したら次の子の処理に移る。
- 子が失敗したらすぐにFailureを返す。
- 全てのノードが成功したらSuccessを返す。
今回実装するのは下記のようなAIです。動きの流れとしてはこうなります。
- 自分のHPが10より大きい場合は、5m以内の敵を攻撃
- 10以下の場合はいちばん近くのタワーに移動して待機
- 行動が完了したら1から同じ行動を繰り返す。
下記が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();
}
}
}