비헤이비어 트리의 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 모듈 구문을 사용합니다. 왜냐하면 그것이 매우 읽기 쉽다고 생각하기 때문입니다. 그래서 모든 코드는 이렇게 작성됩니다. 두 버전의 작업 예제를 보려면 예제 저장소를 방문/복제하세요.
작업은 true
, false
또는 "running"
반환하는 run
메서드의 모든 더러운 작업을 처리하는 간단한 Node
(정확히 말하면 리프 노드)입니다. 명확성과 유연성을 위해 해당 반환 값( 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
한 노드가 실패하거나( FAILURE
반환) 모든 노드가 호출될 때까지 모든 하위 노드를 차례로 호출합니다. 한 노드 호출이 실패하면 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
한 노드가 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
선택기는 하위 노드 중 하나를 무작위로 호출합니다. 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' ]
} )
레지스트리를 사용하면 한 가지 더 많은 이점이 있습니다. run
메서드가 하나만 있는 간단한 작업의 경우 이를 작성하는 간단한 방법이 있습니다.
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
(게임 루프)을 패스할 때마다 개가 짖습니다. 이 작업을 두 번 수행하기 때문에 등록된 노드로 구현했습니다. 그런 다음 무작위로 돌아다니다가 다시 짖고, 개가 짖습니다. 나무 옆에 서서 나무에 오줌을 싸는 자신을 발견합니다.
모든 노드는 일반(또는 다른 장식된) 노드를 래핑하고 값이나 호출을 제어하고 일부 조건을 추가하거나 반환된 상태로 작업을 수행 Decorator
가 될 수도 있습니다. src/decorators
디렉터리에는 데코레이팅된 노드의 반환 값을 무효화하는 InvertDecorator
또는 노드가 쿨 다운타임 기간 내에 한 번만 호출되도록 보장하는 CooldownDecorator
와 같이 영감을 얻거나 사용하기 위해 이미 구현된 데코레이터가 있습니다.
const decoratedSequence = new InvertDecorator ( {
node : 'awesome sequence doing stuff'
} )
자신만의 데코레이터를 생성합니다. Decorator
클래스를 확장하고 Decorator 메서드를 재정의하는 클래스만 있으면 됩니다. 일부 참조 구현을 확인하려면 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
클래스의 인스턴스나 유사한 인터페이스를 구현하는 다른 클래스를 포함하는 step
메서드에 introspector
매개 변수를 추가할 수 있습니다. 이렇게 하면 행동 트리의 모든 실행에 대한 유용한 통계/데이터를 수집할 수 있으며 어떤 작업이 실행되었고 어떤 결과가 반환되었는지 확인할 수 있습니다. 트리의 정확성을 이해하는 데 유용합니다.
하지만 프로덕션 환경에서는 이 작업을 수행하지 마세요. 왜냐하면 그곳에서 수행되는 작업은 정기적인 평가에 필요하지 않기 때문입니다.
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 Georg Tavonius
본 소프트웨어 및 관련 문서 파일("소프트웨어")의 사본을 취득한 모든 사람에게 사용, 복사, 수정, 병합에 대한 권리를 포함하되 이에 국한되지 않고 제한 없이 소프트웨어를 취급할 수 있는 권한이 무료로 부여됩니다. , 소프트웨어 사본을 게시, 배포, 재라이센스 부여 및/또는 판매하고, 소프트웨어를 제공받은 사람에게 다음 조건에 따라 그렇게 하도록 허용합니다.
위의 저작권 고지와 본 허가 고지는 소프트웨어의 모든 사본 또는 상당 부분에 포함됩니다.
소프트웨어는 상품성, 특정 목적에의 적합성 및 비침해에 대한 보증을 포함하되 이에 국한되지 않고 명시적이든 묵시적이든 어떠한 종류의 보증 없이 "있는 그대로" 제공됩니다. 어떠한 경우에도 작성자나 저작권 보유자는 계약, 불법 행위 또는 기타 행위로 인해 소프트웨어나 사용 또는 기타 거래와 관련하여 발생하는 모든 청구, 손해 또는 기타 책임에 대해 책임을 지지 않습니다. 소프트웨어.