Implementasi JavaScript dari Pohon Perilaku. Mereka berguna untuk mengimplementasikan AI. Jika Anda memerlukan informasi lebih lanjut tentang Pohon Perilaku, lihat di GameDevAIPro, ada artikel bagus tentang Pohon Perilaku dari Alex Champandard dan Philip Dunstan.
Jika Anda memerlukan dukungan TypeScript, silakan periksa cabang typescript
yang akan menjadi rilis 3.0 mendatang dan jika Anda memiliki beberapa pertanyaan dan komentar, silakan sampaikan.
Jika Anda menggunakan npm:
npm install behaviortree
atau menggunakan benang:
yarn add behaviortree
Paket ini tidak memiliki ketergantungan sendiri.
Pertama, saya harus menyebutkan bahwa perpustakaan ini juga dapat digunakan di lingkungan common-js seperti node v8. Agar ini berfungsi, Anda harus mengganti semua pernyataan import
dengan pernyataan require()
.
Jadi, bukannya
import { BehaviorTree , Sequence , Task , SUCCESS , FAILURE } from 'behaviortree'
gunakan saja
const { BehaviorTree , Sequence , Task , SUCCESS , FAILURE } = require ( 'behaviortree' )
Saya menggunakan sintaks modul ES yang baru, karena menurut saya sangat mudah dibaca. Jadi semua kodenya ditulis seperti ini. Untuk melihat contoh kerja kedua versi, kunjungi/kloning repo contoh.
Tugas adalah Node
sederhana (tepatnya node daun), yang menangani semua pekerjaan kotor dalam metode run
, yang mengembalikan true
, false
, atau "running"
. Agar lebih jelas dan fleksibel, harap gunakan konstanta ekspor yang disediakan untuk nilai kembalian tersebut ( SUCCESS
, FAILURE
, RUNNING
).
Setiap metode tugas Anda menerima papan tulis, yang Anda tetapkan saat membuat instance BehaviorTree. Papan tulis pada dasarnya adalah sebuah objek, yang menyimpan data dan metode semua tugas yang diperlukan untuk melakukan pekerjaannya dan untuk berkomunikasi dengan dunia.
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
}
} )
Metodenya:
start
- Dipanggil sebelum run dipanggil. Namun tidak jika tugas dilanjutkan setelah diakhiri dengan this.running()end
- Dipanggil setelah run dipanggil. Namun tidak jika tugas selesai dengan this.running()run
- Berisi hal-hal utama yang ingin dilakukan tugas tersebut Sequence
akan memanggil setiap sub nodenya satu demi satu hingga satu node gagal (mengembalikan FAILURE
) atau semua node dipanggil. Jika satu panggilan node gagal, Sequence
akan mengembalikan FAILURE
sendiri, selain itu ia akan memanggil 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
memanggil setiap node dalam daftarnya hingga satu node mengembalikan SUCCESS
, lalu node itu sendiri kembali sebagai sukses. Jika tidak ada sub node yang memanggil SUCCESS
pemilih akan mengembalikan 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
]
} )
Pemilih Random
hanya memanggil salah satu subnodenya secara acak, jika subnode tersebut mengembalikan RUNNING
, subnode tersebut akan dipanggil lagi pada proses berikutnya.
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
]
} )
Membuat sebuah instance dari pohon perilaku cukup sederhana. Cukup buat instance kelas BehaviorTree
dan tentukan bentuk pohonnya, menggunakan node yang disebutkan di atas dan papan tulis yang dapat digunakan node tersebut.
import { BehaviorTree } from 'behaviortree'
var bTree = new BehaviorTree ( {
tree : mySelector ,
blackboard : { }
} )
blackboard
yang Anda tentukan akan diteruskan ke setiap metode start()
, end()
dan run()
sebagai argumen pertama. Anda dapat menggunakannya, untuk memberi tahu pohon perilaku, objek mana (misalnya pemain buatan) yang dijalankannya, membiarkannya berinteraksi dengan dunia, atau menyimpan bit status jika diperlukan. Untuk menjalankan pohon, Anda dapat memanggil step()
kapan pun Anda punya waktu untuk beberapa penghitungan AI di game loop Anda.
bTree . step ( )
BehaviorTree hadir dengan registri internal tempat Anda dapat mendaftarkan tugas dan kemudian mereferensikannya di node Anda berdasarkan namanya, yang Anda pilih. Ini sangat berguna, jika Anda memerlukan perilaku yang sama di beberapa pohon, atau ingin memisahkan pendefinisian tugas dan konstruksi pohon.
// Register a task:
BehaviorTree . register ( 'testtask' , myTask )
// Or register a sequence or priority:
BehaviorTree . register ( 'test sequence' , mySequence )
Yang sekarang dapat Anda rujuk dengan mudah di node Anda, seperti:
import { Selector } from 'behaviortree'
const mySelector = new Selector ( {
nodes : [ 'my awesome task' , 'another awe# task to do' ]
} )
Menggunakan registri memiliki satu manfaat lagi, untuk Tugas sederhana dengan hanya satu metode run
, ada cara singkat untuk menulisnya:
BehaviorTree . register ( 'testtask' , ( blackboard ) => {
console . log ( 'I am doing stuff' )
return SUCCESS
} )
Dan sekarang sebuah contoh bagaimana semua orang bisa bekerja sama.
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 )
Dalam contoh ini hal berikut terjadi: setiap kali melewati setInterval
(perputaran permainan kita), anjing menggonggong – kita mengimplementasikan ini dengan node yang terdaftar, karena kita melakukan ini dua kali – kemudian ia berjalan secara acak, kemudian menggonggong lagi dan kemudian jika ia menemukan dirinya berdiri di samping pohon, ia kencing di pohon.
Setiap node juga bisa menjadi Decorator
, yang membungkus node biasa (atau node lain yang dihias) dan mengontrol nilai atau pemanggilannya, menambahkan beberapa kondisi, atau melakukan sesuatu dengan status yang dikembalikan. Di direktori src/decorators
Anda akan menemukan beberapa dekorator yang sudah diterapkan untuk inspirasi atau penggunaan, seperti InvertDecorator
yang meniadakan nilai kembalian dari node yang dihias atau CooldownDecorator
yang memastikan node hanya dipanggil sekali dalam periode waktu henti yang tenang.
const decoratedSequence = new InvertDecorator ( {
node : 'awesome sequence doing stuff'
} )
Untuk membuat dekorator sendiri. Anda hanya memerlukan kelas yang memperluas kelas Decorator
dan mengganti metode dekorasi. Cukup lihat di dalam sub folder src/decorators
untuk memeriksa beberapa implementasi referensi.
Berhati-hatilah karena Anda tidak bisa begitu saja membuat instance kelas Decorator
dan meneruskan metode decorate
sebagai cetak biru sebagai dekorator dinamis, karena cara kerjanya saat ini.
Ada beberapa dekorator "sederhana" yang telah dibuat untuk kenyamanan Anda. Periksa direktori src/decorators
untuk detail lebih lanjut (dan spesifikasi apa yang mereka lakukan). Menggunakannya sesederhana:
import { BehaviorTree , Sequence , Task , SUCCESS , FAILURE , decorators } from 'behaviortree'
const { AlwaysSucceedDecorator } = decorators
Ada kelas BehaviorTreeImporter
yang ditentukan yang dapat digunakan untuk mengisi instance BehaviorTree
dari definisi JSON untuk sebuah pohon. Struktur definisi terlihat seperti ini:
{
"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 " }
]
}
Melalui properti type
, pengimpor mencari Decorators
, Selectors
, Sequences
dan kelas yang Anda tentukan sendiri dari definisi tipe internal serta tugas dari registri BehaviorTree
, dan mengembalikan objek, yang dapat digunakan sebagai tree
dalam konstruktor BehaviorTree
.
Jika Anda tidak menyukai pernyataan import
- yang baru, Anda masih dapat menggunakan pernyataan require
- yang tradisional:
const {
BehaviorTree ,
Sequence ,
Task ,
SUCCESS ,
FAILURE ,
decorators : { AlwaysSucceedDecorator }
} = require ( 'behaviortree' )
Anda dapat menambahkan parameter introspector
ke metode step
yang berisi instance kelas Introspector
atau kelas lain yang mengimplementasikan antarmuka serupa. Melakukan hal itu memungkinkan Anda mengumpulkan statistik/data berguna tentang setiap proses pohon perilaku Anda dan menunjukkan kepada Anda, tugas mana yang dijalankan dan mengembalikan hasil apa. Berguna dalam memperoleh pemahaman tentang kebenaran pohon tersebut.
Namun jangan lakukan ini di lingkungan produksi, karena pekerjaan yang dilakukan di sana tidak memerlukan evaluasi rutin.
const { Introspector } = require ( 'behaviortree' )
const introspector = new Introspector ( )
bTree . step ( { introspector } )
console . log ( introspector . lastResult )
Itu akan menghasilkan sesuatu seperti:
{
name : 'select' ,
result : Symbol ( running ) ,
children : [
{
name : 'targeting' ,
result : false
} ,
{
name : 'jump' ,
result : Symbol ( running )
}
]
}
Anda ingin berkontribusi? Jika Anda mempunyai ide atau kritik, buka saja terbitannya, di sini di GitHub. Jika Anda ingin mengotori tangan Anda, Anda dapat membagi repositori ini. Namun catatan: Jika Anda menulis kode, jangan lupa untuk menulis tes. Dan kemudian buat permintaan tarik. Saya akan senang melihat apa yang akan terjadi.
Pengujian dilakukan dengan bercanda, dan saya menggunakan benang untuk mengelola paket dan mengunci versi.
yarn
yarn test
RUNNING
Hak Cipta (C) 2013-2020 Georg Tavonius
Izin dengan ini diberikan, secara gratis, kepada siapa pun yang memperoleh salinan perangkat lunak ini dan file dokumentasi terkait ("Perangkat Lunak"), untuk menggunakan Perangkat Lunak tanpa batasan, termasuk tanpa batasan hak untuk menggunakan, menyalin, memodifikasi, menggabungkan , mempublikasikan, mendistribusikan, mensublisensikan, dan/atau menjual salinan Perangkat Lunak, dan mengizinkan orang yang menerima Perangkat Lunak untuk melakukan hal tersebut, dengan tunduk pada ketentuan berikut:
Pemberitahuan hak cipta di atas dan pemberitahuan izin ini akan disertakan dalam semua salinan atau sebagian besar Perangkat Lunak.
PERANGKAT LUNAK INI DISEDIAKAN "APA ADANYA", TANPA JAMINAN APA PUN, TERSURAT MAUPUN TERSIRAT, TERMASUK NAMUN TIDAK TERBATAS PADA JAMINAN KELAYAKAN UNTUK DIPERDAGANGKAN, KESESUAIAN UNTUK TUJUAN TERTENTU, DAN TIDAK ADA PELANGGARAN. DALAM KEADAAN APA PUN PENULIS ATAU PEMEGANG HAK CIPTA TIDAK BERTANGGUNG JAWAB ATAS KLAIM, KERUSAKAN, ATAU TANGGUNG JAWAB LAINNYA, BAIK DALAM TINDAKAN KONTRAK, HUKUM ATAU LAINNYA, YANG TIMBUL DARI, ATAU SEHUBUNGAN DENGAN PERANGKAT LUNAK ATAU PENGGUNAAN ATAU HAL-HAL LAIN DALAM PERANGKAT LUNAK.