ビヘイビア ツリーの JavaScript 実装。 AI の実装に役立ちます。ビヘイビア ツリーについてさらに詳しい情報が必要な場合は、GameDevAIPro を参照してください。Alex Champandard と Philip Dunstan によるビヘイビア ツリーに関する素晴らしい記事があります。
TypeScript のサポートが必要な場合は、今後の 3.0 リリースとなるtypescript
ブランチをチェックしてください。質問やコメントがある場合はお知らせください。
npm を使用する場合:
npm install behaviortree
または糸を使用して:
yarn add behaviortree
このパッケージには独自の依存関係はありません。
まず、このライブラリは、node v8 のような common-js 環境でも使用できることを述べておきます。これを機能させるには、すべてのimport
ステートメントをrequire()
ステートメントに切り替える必要があります。
だから代わりに
import { BehaviorTree , Sequence , Task , SUCCESS , FAILURE } from 'behaviortree'
ただ使ってください
const { BehaviorTree , Sequence , Task , SUCCESS , FAILURE } = require ( 'behaviortree' )
新しい ES モジュール構文は非常に読みやすいと思うので、それを使用します。したがって、すべてのコードは次のように記述されます。両方のバージョンの動作例を確認するには、例のリポジトリにアクセス/クローンを作成します。
タスクは単純なNode
(正確にはリーフ ノード) であり、 run
メソッド内のすべてのダーティな作業を処理し、 true
、 false
、または"running"
のいずれかを返します。明確にし、柔軟性を高めるために、これらの戻り値 ( SUCCESS
、 FAILURE
、 RUNNING
) には、提供されているエクスポートされた定数を使用してください。
タスクの各メソッドは、BehaviorTree をインスタンス化するときに割り当てる黒板を受け取ります。黒板は基本的にオブジェクトであり、すべてのタスクが作業を実行し、世界と通信するために必要なデータとメソッドを保持します。
import { Task , SUCCESS } from 'behaviortree'
const myTask = new Task ( {
// (optional) this function is called directly before the run method
// is called. It allows you to setup things before starting to run
start : function ( blackboard ) {
blackboard . isStarted = true
} ,
// (optional) this function is called directly after the run method
// is completed with either this.success() or this.fail(). It allows you to clean up
// things, after you run the task.
end : function ( blackboard ) {
blackboard . isStarted = false
} ,
// This is the meat of your task. The run method does everything you want it to do.
run : function ( blackboard ) {
return SUCCESS
}
} )
方法:
start
- run が呼び出される前に呼び出されます。ただし、タスクが this.running() で終了した後に再開する場合はそうではありません。end
- run が呼び出された後に呼び出されます。ただし、タスクが this.running() で終了した場合はそうではありません。run
- タスクに実行させたい主な内容が含まれますSequence
1 つのノードが失敗する ( FAILURE
返す) か、すべてのノードが呼び出されるまで、そのすべてのサブノードを順番に呼び出します。 1 つのノードの呼び出しが失敗した場合、 Sequence
FAILURE
自体を返します。それ以外の場合は、 SUCCESS
を呼び出します。
import { Sequence } from 'behaviortree'
const mySequence = new Sequence ( {
nodes : [
// here comes in a list of nodes (Tasks, Sequences or Priorities)
// as objects or as registered strings
]
} )
Selector
1 つのノードがSUCCESS
返すまでリスト内のすべてのノードを呼び出し、セレクター自体が成功を返します。そのサブノードのいずれもSUCCESS
呼び出さない場合、セレクターはFAILURE
を返します。
import { Selector } from 'behaviortree'
const mySelector = new Selector ( {
nodes : [
// here comes in a list of nodes (Tasks, Sequences or Priorities)
// as objects or as registered strings
]
} )
Random
セレクターはサブノードの 1 つをランダムに呼び出すだけで、それがRUNNING
返した場合、次回の実行時に再度呼び出されます。
import { Random } from 'behaviortree'
const mySelector = new Random ( {
nodes : [
// here comes in a list of nodes (Tasks, Sequences or Priorities)
// as objects or as registered strings
]
} )
ビヘイビア ツリーのインスタンスの作成は非常に簡単です。 BehaviorTree
クラスをインスタンス化し、上記のノードとノードが使用できる黒板を使用してツリーの形状を指定するだけです。
import { BehaviorTree } from 'behaviortree'
var bTree = new BehaviorTree ( {
tree : mySelector ,
blackboard : { }
} )
指定したblackboard
、すべてのstart()
、 end()
、およびrun()
メソッドに最初の引数として渡されます。これを使用すると、ビヘイビアー ツリーにどのオブジェクト (人工プレーヤーなど) で実行されているかを知らせたり、世界と対話させたり、必要に応じて状態のビットを保持させたりすることができます。ツリーを実行するには、ゲーム ループ内で AI 計算を行う時間があればいつでもstep()
を呼び出すことができます。
bTree . step ( )
BehaviorTree には、タスクを登録し、後で選択した名前でノード内でタスクを参照できる内部レジストリが付属しています。これは、複数のツリーで同じ動作が必要な場合、またはタスクの定義とツリーの構築を分離したい場合に非常に便利です。
// Register a task:
BehaviorTree . register ( 'testtask' , myTask )
// Or register a sequence or priority:
BehaviorTree . register ( 'test sequence' , mySequence )
これは、次のようにノード内で簡単に参照できるようになりました。
import { Selector } from 'behaviortree'
const mySelector = new Selector ( {
nodes : [ 'my awesome task' , 'another awe# task to do' ]
} )
レジストリを使用することにはもう 1 つの利点があります。 run
メソッドが 1 つだけの単純なタスクの場合、それらを記述する短い方法があります。
BehaviorTree . register ( 'testtask' , ( blackboard ) => {
console . log ( 'I am doing stuff' )
return SUCCESS
} )
そして今度は、全員が協力する方法の一例です。
import { BehaviorTree , Sequence , Task , SUCCESS , FAILURE } from 'behaviortree'
BehaviorTree . register (
'bark' ,
new Task ( {
run : function ( dog ) {
dog . bark ( )
return SUCCESS
}
} )
)
const tree = new Sequence ( {
nodes : [
'bark' ,
new Task ( {
run : function ( dog ) {
dog . randomlyWalk ( )
return SUCCESS
}
} ) ,
'bark' ,
new Task ( {
run : function ( dog ) {
if ( dog . standBesideATree ( ) ) {
dog . liftALeg ( )
dog . pee ( )
return SUCCESS
} else {
return FAILURE
}
}
} )
]
} )
const dog = new Dog ( /*...*/ ) // the nasty details of a dog are omitted
const bTree = new BehaviorTree ( {
tree : tree ,
blackboard : dog
} )
// The "game" loop:
setInterval ( function ( ) {
bTree . step ( )
} , 1000 / 60 )
この例では、次のことが起こりますsetInterval
(ゲーム ループ) を渡すたびに、犬が吠えます。これを 2 回行うため、登録済みのノードを使用して実装しました。その後、犬はランダムに歩き回り、次に吠えます。気がつくと木の横に立っていて、木の上でおしっこをしています。
すべてのノードはDecorator
にすることもできます。 Decorator は、通常の (または別の装飾された) ノードをラップし、その値または呼び出しを制御したり、条件を追加したり、返された状態に対して何らかの処理を実行したりすることができます。 src/decorators
ディレクトリには、装飾されたノードの戻り値を無効にするInvertDecorator
や、クール ダウンタイム期間内にノードが 1 回だけ呼び出されることを保証するCooldownDecorator
など、インスピレーションや使用のためにすでに実装されているデコレータがいくつかあります。
const decoratedSequence = new InvertDecorator ( {
node : 'awesome sequence doing stuff'
} )
独自のデコレータを作成するには。 Decorator
クラスを拡張し、decorate メソッドをオーバーライドするクラスが必要なだけです。 src/decorators
サブフォルダー内を調べて、いくつかのリファレンス実装を確認してください。
現時点では、単にDecorator
クラスをインスタンス化し、 decorate
メソッドを動的デコレータとしてブループリントとして渡すことはできないことに注意してください。
便利なように、いくつかの「単純な」デコレータがすでに構築されています。詳細 (およびその動作の仕様) については、 src/decorators
ディレクトリを確認してください。使用方法は次のように簡単です。
import { BehaviorTree , Sequence , Task , SUCCESS , FAILURE , decorators } from 'behaviortree'
const { AlwaysSucceedDecorator } = decorators
ツリーの JSON 定義からBehaviorTree
インスタンスを埋めるために使用できるBehaviorTreeImporter
クラスが定義されています。定義構造は次のようになります。
{
"type" : " selector " ,
"name" : " the root " ,
"nodes" : [
{
"type" : " ifEnemyInSight " ,
"name" : " handling enemies " ,
"node" : { "type" : " walk " , "name" : " go to enemy " }
},
{
"type" : " cooldown " ,
"name" : " jumping around " ,
"cooldown" : 1 ,
"node" : { "type" : " jump " , "name" : " jump up " }
},
{ "type" : " idle " , "name" : " doing nothing " }
]
}
インポーターはtype
プロパティを通じてDecorators
、 Selectors
、 Sequences
および内部型定義から独自に定義したクラスとBehaviorTree
レジストリからのタスクを検索し、 BehaviorTree
コンストラクター内でtree
として使用できるオブジェクトを返します。
新しいimport
ステートメントが気に入らない場合でも、従来のrequire
ステートメントを使用できるはずです。
const {
BehaviorTree ,
Sequence ,
Task ,
SUCCESS ,
FAILURE ,
decorators : { AlwaysSucceedDecorator }
} = require ( 'behaviortree' )
Introspector
クラスのインスタンスまたは同様のインターフェイスを実装する別のクラスを含むintrospector
パラメーターをstep
メソッドに追加できます。これにより、ビヘイビアー ツリーのすべての実行に関する有用な統計/データを収集し、どのタスクが実行され、どの結果が返されたかを示すことができます。ツリーの正確性を理解するのに役立ちます。
ただし、運用環境ではこれを実行しないでください。そこで行われる作業は定期的な評価にはまったく必要ないからです。
const { Introspector } = require ( 'behaviortree' )
const introspector = new Introspector ( )
bTree . step ( { introspector } )
console . log ( introspector . lastResult )
その結果、次のような結果になります。
{
name : 'select' ,
result : Symbol ( running ) ,
children : [
{
name : 'targeting' ,
result : false
} ,
{
name : 'jump' ,
result : Symbol ( running )
}
]
}
貢献したいですか?アイデアや批判がある場合は、GitHub で問題を開いてください。手を汚したい場合は、このリポジトリをフォークすることができます。ただし、注意: コードを作成する場合は、テストを作成することを忘れないでください。そして、プルリクエストを作成します。何が起こるか楽しみにしています。
テストは jest で行われ、パッケージの管理とバージョンのロックには Yarn を使用します。
yarn
yarn test
RUNNING
分岐ノードでの開始と終了の呼び出しに関する問題を修正著作権 (C) 2013-2020 ゲオルグ・タボニウス
本ソフトウェアおよび関連ドキュメント ファイル (以下「ソフトウェア」) のコピーを入手した人には、使用、コピー、変更、マージする権利を含むがこれらに限定されない、制限なくソフトウェアを取り扱う許可が、ここに無償で与えられます。 、以下の条件を条件として、本ソフトウェアのコピーを出版、配布、サブライセンス、および/または販売すること、および本ソフトウェアが提供される人物にそれを許可すること。
上記の著作権表示およびこの許可通知は、ソフトウェアのすべてのコピーまたは主要部分に含まれるものとします。
ソフトウェアは「現状のまま」提供され、明示的か黙示的かを問わず、商品性、特定目的への適合性、および非侵害の保証を含むがこれらに限定されない、いかなる種類の保証も行われません。いかなる場合においても、作者または著作権所有者は、契約行為、不法行為、またはその他の行為であるかどうかにかかわらず、ソフトウェアまたはソフトウェアの使用またはその他の取引に起因または関連して生じる、いかなる請求、損害、またはその他の責任に対しても責任を負わないものとします。ソフトウェア。