├── .gitignore
├── LICENSE
├── README.md
├── Run.bat
├── haxelib.json
├── hxbt.hxproj
├── samples
├── json_loader_sample
│ ├── .gitignore
│ ├── Run.bat
│ ├── json_loader_sample.hxproj
│ └── src
│ │ └── Main.hx
└── luxe-sample
│ ├── assets
│ ├── .keep
│ └── behavior_trees.json
│ ├── config.json
│ ├── luxe-sample.hxproj
│ ├── project.flow
│ └── src
│ ├── Main.hx
│ └── sample
│ ├── BeLazyBehaviour.hx
│ ├── Blackboard.hx
│ ├── CloseDoorBehaviour.hx
│ ├── Door.hx
│ ├── OpenDoorBehaviour.hx
│ ├── Walk.hx
│ ├── WalkThroughDoorBehaviour.hx
│ └── WalkToDoorBehaviour.hx
├── src
├── Main.hx
└── hxbt
│ ├── Behavior.hx
│ ├── BehaviorTree.hx
│ ├── Composite.hx
│ ├── Decorator.hx
│ ├── composites
│ ├── Parallel.hx
│ ├── Selector.hx
│ └── Sequence.hx
│ ├── decorators
│ ├── AlwaysFail.hx
│ ├── AlwaysSucceed.hx
│ ├── Include.hx
│ ├── Invert.hx
│ ├── UntilFail.hx
│ └── UntilSuccess.hx
│ └── loaders
│ └── BehaviorTreeJSONLoader.hx
└── tests
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── behaviour.bat
├── behaviour.hxml
├── config.json
├── hxbt.hxproj
├── project.flow
└── src
├── Main.hx
└── behaviour
├── BehaviourMain.hx
└── hxbt
├── BehaviorBehaviour.hx
├── BehaviorTreeBehaviour.hx
├── MockBehaviour.hx
├── composites
├── ParallelBehaviour.hx
├── SelectorBehaviour.hx
└── SequenceBehaviour.hx
├── decorators
├── AlwaysFailBehaviour.hx
├── AlwaysSucceedBehaviour.hx
├── IncludeBehaviour.hx
├── InvertBehaviour.hx
├── UntilFailBehaviour.hx
└── UntilSucceedBehaviour.hx
└── loaders
└── BehaviorTreeJSONLoaderBehaviour.hx
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | behaviour.n
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Kristian Brodal
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # hxbt
2 |
3 | Behavior Tree implementation for Haxe.
4 |
5 | ### Installation Instructions
6 |
7 | #### Using haxelib ( Recommended )
8 | hxbt hasn't yet been added as a haxelib package, but will be in the future. You can however manually add it once you have cloned the repository. To add hxbt to haxelib simple use the following command:
9 |
10 | haxelib dev hxbt path/to/hxbt
11 |
12 | #### Not using haxelib
13 |
14 | Clone the repository or download as zip. Drag and drop src/hxbt folder to the src folder of your project.
15 |
16 | ### Usage
17 |
18 | The samples folder contains projects demonstrating how hxbt works and is a perfect place to get an idea of how the library can be used.
19 |
20 | All samples are located in the samples folder https://github.com/whuop/hxbt/tree/master/samples/.
21 |
22 | **Note** : This library's Parallel and Selector composites have two different updating alternatives: updating their logic one child per tick or doing a whole process per tick. Those alternatives are chosen
23 | with `allChildrenTick` at their construction.
24 |
25 | ### Are you using hxbt for a project?
26 |
27 | If you are, please let me know. I would love to see what people are creating using the library. And who knows, maybe I'll make a small showcase of games using hxbt in the future.
28 |
29 | ### License
30 |
31 | The MIT License (MIT)
32 |
33 | Copyright (c) 2015 Kristian Brodal
34 |
35 | Permission is hereby granted, free of charge, to any person obtaining a copy
36 | of this software and associated documentation files (the "Software"), to deal
37 | in the Software without restriction, including without limitation the rights
38 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39 | copies of the Software, and to permit persons to whom the Software is
40 | furnished to do so, subject to the following conditions:
41 |
42 | The above copyright notice and this permission notice shall be included in all
43 | copies or substantial portions of the Software.
44 |
45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
51 | SOFTWARE.
52 |
--------------------------------------------------------------------------------
/Run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if %1 == debug (
3 | bin\Main-debug.exe
4 | ) else (
5 | bin\Main.exe
6 | )
7 | pause
--------------------------------------------------------------------------------
/haxelib.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hxbt",
3 | "url": "https://github.com/whuop/hxbt",
4 | "license": "MIT",
5 | "tags": [],
6 | "description": "Haxe behavior tree implementation.",
7 | "version": "1.0.0",
8 | "releasenote": "",
9 | "contributors": [ "Whuop"],
10 | "dependencies": {},
11 | "classPath" : "src"
12 | }
13 |
--------------------------------------------------------------------------------
/hxbt.hxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/samples/json_loader_sample/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | behaviour.n
--------------------------------------------------------------------------------
/samples/json_loader_sample/Run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if %1 == debug (
3 | bin\Main-debug.exe
4 | ) else (
5 | bin\Main.exe
6 | )
7 | pause
--------------------------------------------------------------------------------
/samples/json_loader_sample/json_loader_sample.hxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/samples/json_loader_sample/src/Main.hx:
--------------------------------------------------------------------------------
1 | import hxbt.Behavior.Status;
2 | import hxbt.BehaviorTree;
3 | import hxbt.composites.Sequence;
4 | import hxbt.composites.Selector;
5 | import hxbt.loaders.BehaviorTreeJSONLoader;
6 | import hxbt.Behavior;
7 |
8 | class Blackboard
9 | {
10 | public var name : String;
11 |
12 | public function new(_name) : Void
13 | {
14 | this.name = _name;
15 | }
16 | }
17 |
18 | class SimpleBehavior extends Behavior
19 | {
20 | public function new() : Void
21 | {
22 | super();
23 | }
24 |
25 | override function update( context : Blackboard, dt : Float) : Status
26 | {
27 | trace('Updating ${context.name}');
28 | return Status.SUCCESS;
29 | }
30 |
31 | override function onInitialize( context : Blackboard) : Void
32 | {
33 | trace('Initializing ${context.name}');
34 | }
35 |
36 | override function onTerminate(context : Blackboard, status : Status) : Void
37 | {
38 | trace('Terminating ${context.name}');
39 | }
40 | }
41 |
42 | class Main
43 | {
44 |
45 |
46 | static function main()
47 | {
48 | // Construct the json string that defines the behavior tree.
49 | var json = '
50 | {
51 | "test_tree" :
52 | {
53 | "hxbt.selector" :
54 | {
55 | "hxbt.sequence":
56 | [
57 | {"SimpleBehavior" : {}}
58 | ]
59 | }
60 | }
61 | }
62 | ';
63 |
64 | var behaviorTree : BehaviorTree = BehaviorTreeJSONLoader.FromJSONString(json, "test_tree");
65 | var dt = 1.0 / 60.0;
66 | behaviorTree.period = dt;
67 | // Set which blackboard it should use.
68 | behaviorTree.setContext(new Blackboard('SimpleBehavior'));
69 |
70 | // Run the behavior tree 100 times just to test it.
71 | var i = 0;
72 | while (i < 100)
73 | {
74 | behaviorTree.update(dt);
75 | i++;
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/whuop/hxbt/a2a9ef0cdd30430efa675dbf3f51e850cd481094/samples/luxe-sample/assets/.keep
--------------------------------------------------------------------------------
/samples/luxe-sample/assets/behavior_trees.json:
--------------------------------------------------------------------------------
1 | {
2 | "WalkThroughDoorAndStop": [{
3 | "hxbt.sequence": [
4 | { "sample.WalkToDoorBehaviour": [] },
5 | { "sample.OpenDoorBehaviour": [] },
6 | { "sample.WalkThroughDoorBehaviour": [] },
7 | { "sample.CloseDoorBehaviour": [] },
8 | { "sample.BeLazyBehaviour": [] }
9 | ]
10 | }]
11 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/config.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/samples/luxe-sample/luxe-sample.hxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | "$(CompilerPath)/haxelib" run flow build $(TargetBuild) --$(BuildConfig)
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/samples/luxe-sample/project.flow:
--------------------------------------------------------------------------------
1 | {
2 | project : {
3 | name : 'empty',
4 | version : '1.0.0',
5 | author : 'whuop',
6 |
7 | app : {
8 | name : 'hxbt_luxe_sample',
9 | package : 'com.whuop.hxbt',
10 | main : 'Main'
11 | },
12 |
13 | build : {
14 | dependencies : {
15 | luxe : '*',
16 | hxbt : '*'
17 | }
18 | },
19 |
20 | files : {
21 | config : 'config.json',
22 | assets : 'assets/'
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/samples/luxe-sample/src/Main.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | import luxe.Game;
4 | import luxe.Sprite;
5 | import luxe.Vector;
6 | import luxe.Color;
7 |
8 | import hxbt.BehaviorTree;
9 | import hxbt.composites.Sequence;
10 |
11 | import sample.WalkToDoorBehaviour;
12 | import sample.OpenDoorBehaviour;
13 | import sample.WalkThroughDoorBehaviour;
14 | import sample.CloseDoorBehaviour;
15 | import sample.BeLazyBehaviour;
16 | import sample.Walk;
17 | import sample.Door;
18 | import sample.Blackboard;
19 |
20 |
21 | class Main extends Game
22 | {
23 | var actor:Sprite;
24 | var door:Sprite;
25 | var behaviorTree:BehaviorTree;
26 |
27 | public override function ready()
28 | {
29 | // create our dude who's going to walk around
30 | var actor:Sprite = new Sprite({
31 | pos: new Vector((Luxe.screen.w / 2) - 128, Luxe.screen.h / 2),
32 | size: new Vector(16, 16),
33 | color: new Color(0.9, 0.9, 0.9, 1),
34 | origin: new Vector(8, 16)
35 | });
36 | actor.add(new Walk(128));
37 |
38 | // create a door for him to walk through
39 | door = new Sprite({
40 | pos: new Vector((Luxe.screen.w / 2) + 128, Luxe.screen.h / 2),
41 | size: new Vector(16, 32),
42 | color: new Color(0.5, 0.25, 0, 1),
43 | origin: new Vector(8, 32)
44 | });
45 | door.add(new Door(16, 2));
46 |
47 | var bb = new Blackboard();
48 | bb.actor = actor;
49 | bb.door = door;
50 |
51 | behaviorTree = new BehaviorTree();
52 | var sequence = new Sequence();
53 | behaviorTree.setRoot(sequence);
54 | sequence.add(new WalkToDoorBehaviour( ));
55 | sequence.add(new OpenDoorBehaviour( ));
56 | sequence.add(new WalkThroughDoorBehaviour( ));
57 | sequence.add(new CloseDoorBehaviour( ));
58 | sequence.add(new BeLazyBehaviour( ));
59 |
60 | behaviorTree.setContext(bb);
61 | }
62 |
63 | public override function update( dt:Float )
64 | {
65 | behaviorTree.update(dt);
66 | } //update
67 |
68 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/src/sample/BeLazyBehaviour.hx:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import hxbt.Behavior;
4 |
5 | class BeLazyBehaviour extends Behavior
6 | {
7 | public function new()
8 | {
9 | super();
10 | }
11 |
12 | override function update(context : Blackboard, dt : Float) : Status
13 | {
14 | return Status.RUNNING;
15 | }
16 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/src/sample/Blackboard.hx:
--------------------------------------------------------------------------------
1 | package sample;
2 | import luxe.Sprite;
3 |
4 | /**
5 | * ...
6 | * @author Kristian Brodal
7 | */
8 | class Blackboard
9 | {
10 |
11 | public var actor : Sprite;
12 | public var door : Sprite;
13 |
14 | public function new()
15 | {
16 |
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/src/sample/CloseDoorBehaviour.hx:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import hxbt.Behavior;
4 | import sample.Door;
5 | import sample.Door.DoorState;
6 |
7 | class CloseDoorBehaviour extends Behavior
8 | {
9 | public function new()
10 | {
11 | super();
12 | }
13 |
14 | override function update(context : Blackboard, dt : Float) : Status
15 | {
16 | var door:Door = cast(context.door.get('Door'), Door);
17 | door.close();
18 | if(door.state == DoorState.CLOSED) {
19 | return Status.SUCCESS;
20 | }
21 | return Status.RUNNING;
22 | }
23 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/src/sample/Door.hx:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import luxe.Sprite;
4 | import luxe.Component;
5 | import luxe.Vector;
6 |
7 | enum DoorState {
8 | CLOSED;
9 | OPENING;
10 | OPEN;
11 | CLOSING;
12 | }
13 |
14 | /*
15 | * Animate the width of a sprite using "open()" and "close()" functions
16 | * @author Kenton Hamaluik
17 | */
18 | class Door extends Component {
19 | public var state:DoorState;
20 | var closedWidth:Float;
21 | var openWidth:Float;
22 | var spr:Sprite;
23 |
24 | var speed:Float = 32;
25 | var targetWidth:Float = 32;
26 |
27 | public function new(?closedWidth:Float, ?openWidth:Float) {
28 | super({ name: 'Door' });
29 | state = DoorState.CLOSED;
30 | this.closedWidth = closedWidth;
31 | this.openWidth = openWidth;
32 | }
33 |
34 | override function init() {
35 | spr = cast(entity, Sprite);
36 | }
37 |
38 | public function open() {
39 | if(state == DoorState.CLOSED) {
40 | state = DoorState.OPENING;
41 | targetWidth = openWidth;
42 | }
43 | }
44 |
45 | public function close() {
46 | if(state == DoorState.OPEN) {
47 | state = DoorState.CLOSING;
48 | targetWidth = closedWidth;
49 | }
50 | }
51 |
52 | override function update(dt:Float) {
53 | if(state == DoorState.OPENING) {
54 | spr.size.x -= speed * dt;
55 | if(spr.size.x <= openWidth) {
56 | spr.size.x = openWidth;
57 | state = DoorState.OPEN;
58 | }
59 | }
60 | else if(state == DoorState.CLOSING) {
61 | spr.size.x += speed * dt;
62 | if(spr.size.x >= closedWidth) {
63 | spr.size.x = closedWidth;
64 | state = DoorState.CLOSED;
65 | }
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/src/sample/OpenDoorBehaviour.hx:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import hxbt.Behavior;
4 | import sample.Door;
5 | import sample.Door.DoorState;
6 |
7 | class OpenDoorBehaviour extends Behavior
8 | {
9 | public function new()
10 | {
11 | super();
12 | }
13 |
14 | override function update(context : Blackboard, dt : Float) : Status
15 | {
16 | var door:Door = cast(context.door.get('Door'), Door);
17 | door.open();
18 | if(door.state == DoorState.OPEN) {
19 | return Status.SUCCESS;
20 | }
21 | return Status.RUNNING;
22 | }
23 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/src/sample/Walk.hx:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import luxe.Sprite;
4 | import luxe.Component;
5 | import luxe.Vector;
6 |
7 | /*
8 | * Will move towards a location with a given velocity
9 | * @author Kenton Hamaluik
10 | */
11 | class Walk extends Component {
12 | public var speed:Float = 32;
13 | public var target:Vector;
14 | public function new(?speed:Float) {
15 | super({ name: 'Walk' });
16 | if(speed != null) this.speed = speed;
17 | }
18 |
19 | override function update(dt:Float) {
20 | if(target != null) {
21 | entity.pos.add(target.clone().subtract(entity.pos).normalized.multiplyScalar(speed * dt));
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/src/sample/WalkThroughDoorBehaviour.hx:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import hxbt.Behavior;
4 | import luxe.Vector;
5 |
6 | class WalkThroughDoorBehaviour extends Behavior
7 | {
8 | var targetPosition:Vector;
9 |
10 | public function new()
11 | {
12 | super();
13 | }
14 |
15 | override function onInitialize(context : Blackboard) : Void
16 | {
17 | targetPosition = context.door.pos.clone().add_xyz(32);
18 | }
19 |
20 | override function update(context : Blackboard, dt : Float) : Status
21 | {
22 | if(targetPosition.clone().subtract(context.actor.pos).length <= 16) {
23 | cast(context.actor.get('Walk'), Walk).target = null;
24 | return Status.SUCCESS;
25 | }
26 | else {
27 | cast(context.actor.get('Walk'), Walk).target = targetPosition;
28 | }
29 | return Status.RUNNING;
30 | }
31 | }
--------------------------------------------------------------------------------
/samples/luxe-sample/src/sample/WalkToDoorBehaviour.hx:
--------------------------------------------------------------------------------
1 | package sample;
2 |
3 | import hxbt.Behavior;
4 | import luxe.Vector;
5 |
6 | class WalkToDoorBehaviour extends Behavior
7 | {
8 | var targetPosition:Vector;
9 |
10 | public function new()
11 | {
12 | super();
13 | }
14 |
15 | override function onInitialize(context : Blackboard) : Void
16 | {
17 | targetPosition = context.door.pos.clone();
18 | }
19 |
20 | override function update(context : Blackboard, dt : Float) : Status
21 | {
22 | if(targetPosition.clone().subtract(context.actor.pos).length <= 16) {
23 | cast(context.actor.get('Walk'), Walk).target = null;
24 | return Status.SUCCESS;
25 | }
26 | else {
27 | cast(context.actor.get('Walk'), Walk).target = targetPosition;
28 | }
29 | return Status.RUNNING;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Main.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | import hxbt.Behavior;
4 | import hxbt.Behavior.Status;
5 | import hxbt.BehaviorTree;
6 | import hxbt.composites.Sequence;
7 | import hxbt.composites.Selector;
8 | import hxbt.loaders.BehaviorTreeJSONLoader;
9 |
10 | class Blackboard
11 | {
12 | public var name : String;
13 |
14 | public function new(_name) : Void
15 | {
16 | this.name = _name;
17 | }
18 | }
19 |
20 | class SimpleBehavior extends Behavior
21 | {
22 | public function new() : Void
23 | {
24 | super();
25 | }
26 |
27 | override function update( context : Blackboard, dt : Float) : Status
28 | {
29 | trace('Updating ${context.name}');
30 | return Status.SUCCESS;
31 | }
32 |
33 | override function onInitialize( context : Blackboard) : Void
34 | {
35 | trace('Initializing ${context.name}');
36 | }
37 |
38 | override function onTerminate(context : Blackboard, status : Status) : Void
39 | {
40 | trace('Terminating ${context.name}');
41 | }
42 | }
43 |
44 | class Main
45 | {
46 |
47 |
48 | static function main()
49 | {
50 | jsonTree();
51 | //codeTree();
52 | }
53 |
54 | static function codeTree() : Void
55 | {
56 | // Create the behavior tree
57 | var behaviorTree = new BehaviorTree();
58 | // Make it update roughly every frame.
59 | var dt = 1.0 / 60.0;
60 | behaviorTree.period = dt;
61 | // Set which blackboard it should use.
62 | behaviorTree.setContext(new Blackboard('SimpleBehavior'));
63 |
64 | // Root element of the tree, is usually a selector.
65 | var root = new Selector();
66 | behaviorTree.setRoot(root);
67 | // After that, add a sequence that goes through a number of behaviors
68 | var sequence = new Sequence();
69 | root.add(sequence);
70 | // And lastly, add behaviors to that sequence
71 | sequence.add(new SimpleBehavior());
72 |
73 |
74 | // Run the behavior tree 100 times just to test it.
75 | var i = 0;
76 | while (i < 100)
77 | {
78 | behaviorTree.update(dt);
79 | i++;
80 | }
81 | }
82 |
83 | static function jsonTree() : Void
84 | {
85 | var json = '
86 | {
87 | "test_tree" :
88 | {
89 | "hxbt.selector" :
90 | {
91 | "hxbt.sequence":
92 | [
93 | {"SimpleBehavior" : {}}
94 | ]
95 | }
96 | }
97 | }
98 | ';
99 |
100 | var behaviorTree : BehaviorTree = BehaviorTreeJSONLoader.FromJSONString(json, "test_tree");
101 | var dt = 1.0 / 60.0;
102 | behaviorTree.period = dt;
103 | // Set which blackboard it should use.
104 | behaviorTree.setContext(new Blackboard('SimpleBehavior'));
105 |
106 | // Run the behavior tree 100 times just to test it.
107 | var i = 0;
108 | while (i < 100)
109 | {
110 | behaviorTree.update(dt);
111 | i++;
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/src/hxbt/Behavior.hx:
--------------------------------------------------------------------------------
1 | package hxbt;
2 | import hxbt.Behavior.Status;
3 |
4 | /*
5 | * Behavior states.
6 | */
7 | enum Status
8 | {
9 | INVALID;
10 | SUCCESS;
11 | FAILURE;
12 | RUNNING;
13 | }
14 |
15 | /**
16 | * Base class for all actions, conditons and composites.
17 | * A composite is a branch in the behavior tree.
18 | * @author Kristian Brodal
19 | */
20 | @:keepSub
21 | class Behavior
22 | {
23 |
24 | public var status(default, default) : Status = Status.INVALID;
25 |
26 | public function new()
27 | {
28 |
29 | }
30 |
31 | public function update( context : T, dt : Float) : Status { return Status.INVALID; }
32 | public function onInitialize( context : T) : Void { }
33 | public function onTerminate(context : T, status : Status) : Void { }
34 |
35 | public function tick(context : Dynamic, dt : Float) : Status
36 | {
37 | if (status == Status.INVALID)
38 | {
39 | onInitialize(context);
40 | }
41 |
42 | status = update(context, dt);
43 |
44 | if (status != Status.RUNNING)
45 | {
46 | onTerminate(context, status);
47 | }
48 |
49 | return status;
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/src/hxbt/BehaviorTree.hx:
--------------------------------------------------------------------------------
1 | package hxbt;
2 |
3 | /**
4 | * Base of a behavior tree, this is used to hold the rest of the tree for the AI
5 | * as this has extra functionality.
6 | * @author Kristian Brodal
7 | */
8 | class BehaviorTree
9 | {
10 | // The period decides how often the behavior tree should update.
11 | // Period of 0.2 will make the tree update 5 times a second.
12 | public var period(default, default) : Float;
13 |
14 | // Root of the behavior tree.
15 | private var m_tree : Behavior;
16 |
17 | // The context contains all the data needed to run
18 | // the different behaviors. Also called blackboard in some cases.
19 | private var m_context : T;
20 |
21 | // When counter reaches 0 tree is updated.
22 | private var m_counter : Float;
23 |
24 | public function new(?period:Float)
25 | {
26 | // 0.2 will make the tree run 5 times a second.
27 | // Used as default for now
28 | this.period = period == null ? 0.2 : period;
29 | m_counter = this.period;
30 | }
31 |
32 | public function setRoot(root : Behavior) : Void
33 | {
34 | m_tree = root;
35 | }
36 |
37 | public function setContext(context : T) : Void
38 | {
39 | m_context = context;
40 | }
41 |
42 | public function update(dt : Float) : Void
43 | {
44 | m_counter -= dt;
45 | while (m_counter <= 0)
46 | {
47 | m_counter += this.period;
48 | if (m_tree != null)
49 | {
50 | m_tree.tick(m_context, this.period);
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/src/hxbt/Composite.hx:
--------------------------------------------------------------------------------
1 | package hxbt;
2 |
3 | /**
4 | * ...
5 | * @author Kristian Brodal
6 | */
7 | class Composite extends Behavior
8 | {
9 | private var m_children : Array>;
10 |
11 |
12 | public function new()
13 | {
14 | super();
15 | m_children = new Array>();
16 | }
17 |
18 | /*
19 | * Add child to the end of the child array.
20 | */
21 | public function add(b : Behavior)
22 | {
23 | m_children.push(b);
24 | }
25 |
26 | /*
27 | * Remove given child from the array of children.
28 | */
29 | public function remove(b : Behavior)
30 | {
31 | m_children.remove(b);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/hxbt/Decorator.hx:
--------------------------------------------------------------------------------
1 | package hxbt;
2 |
3 | /**
4 | * ...
5 | * @author Kenton Hamaluik
6 | */
7 | class Decorator extends Behavior
8 | {
9 | private var m_child : Behavior;
10 |
11 |
12 | public function new(?child : Behavior)
13 | {
14 | super();
15 | m_child = child;
16 | }
17 |
18 | public function setChild(child : Behavior)
19 | {
20 | m_child = child;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/hxbt/composites/Parallel.hx:
--------------------------------------------------------------------------------
1 | package hxbt.composites;
2 | import hxbt.Behavior.Status;
3 |
4 | /**
5 | * A special branch that starts or resume all children every time it runs.
6 | * Will success if _all_ the children succeed.
7 | * Will fail if any one of the children fail.
8 | * @author Kenton Hamaluik
9 | */
10 | class Parallel extends Composite
11 | {
12 | public function new()
13 | {
14 | super();
15 | }
16 |
17 | override function onInitialize(context : T)
18 | {
19 | }
20 |
21 | override function onTerminate(context : T, status : Status)
22 | {
23 | for(_child in m_children)
24 | {
25 | _child.status = Status.INVALID;
26 | }
27 | }
28 |
29 | override function update(context : T, dt : Float) : Status
30 | {
31 | var allSucceeded:Bool = true;
32 | for(child in m_children)
33 | {
34 | child.tick(context, dt);
35 |
36 | // if any of the children fail,
37 | // fail the whole branch
38 | if(child.status == Status.FAILURE)
39 | {
40 | return Status.FAILURE;
41 | }
42 | // if all children didn't succeed, we're not done yet
43 | else if(child.status != Status.SUCCESS)
44 | {
45 | allSucceeded = false;
46 | }
47 | }
48 |
49 | if(allSucceeded)
50 | {
51 | return Status.SUCCESS;
52 | }
53 |
54 | return Status.RUNNING;
55 | }
56 | }
--------------------------------------------------------------------------------
/src/hxbt/composites/Selector.hx:
--------------------------------------------------------------------------------
1 | package hxbt.composites;
2 |
3 | import hxbt.Behavior.Status;
4 |
5 | /**
6 | * Similar to sequence, looks for one behavior that returns success and returns.
7 | * @author Kristian Brodal
8 | */
9 | class Selector extends Composite
10 | {
11 | private var m_currentChild : Behavior;
12 | private var m_currentIndex : Int;
13 | private var m_allChildrenTick:Bool;
14 |
15 | /**
16 | * @param allChildrenTick Should the Selector try to update a whole cycle
17 | * per tick or only one child.
18 | */
19 | public function new(allChildrenTick:Bool = false)
20 | {
21 | super();
22 | m_allChildrenTick = allChildrenTick;
23 | }
24 |
25 | override function onInitialize(context : T)
26 | {
27 | m_currentIndex = 0;
28 | }
29 |
30 | override function onTerminate(context : T, status : Status)
31 | {
32 | for(_child in m_children)
33 | {
34 | _child.status = Status.INVALID;
35 | }
36 | }
37 |
38 | function updateAllChildren(context : T, dt : Float) : Status
39 | {
40 | var status:Status;
41 | for(index in m_currentIndex...m_children.length)
42 | {
43 | m_currentIndex = index;
44 | m_currentChild = m_children[index];
45 | status = m_currentChild.tick(context, dt);
46 | if(status == SUCCESS)
47 | {
48 | m_currentIndex = 0;
49 | return SUCCESS;
50 | }
51 | else if(status == RUNNING)
52 | {
53 | return RUNNING;
54 | }
55 | }
56 | m_currentIndex = 0;
57 | return FAILURE;
58 | }
59 |
60 | function updateChildPerChild(context : T, dt : Float) : Status
61 | {
62 | m_currentChild = m_children[m_currentIndex];
63 | var s = m_currentChild.tick(context, dt);
64 |
65 | // If the child succeeds or is still running, early return.
66 | if (s != Status.FAILURE)
67 | {
68 | return s;
69 | }
70 | m_currentIndex++;
71 | // If the end of the children is hit, that means the whole thing fails.
72 | if (m_currentIndex == m_children.length)
73 | {
74 | // Reset index otherwise it will crash on next run through
75 | m_currentIndex = 0;
76 | return Status.FAILURE;
77 | }
78 |
79 | return Status.RUNNING;
80 | }
81 |
82 | override function update(context : T, dt : Float) : Status
83 | {
84 | if(m_allChildrenTick)
85 | {
86 | return updateAllChildren(context, dt);
87 | }
88 | else
89 | {
90 | return updateChildPerChild(context, dt);
91 | }
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/src/hxbt/composites/Sequence.hx:
--------------------------------------------------------------------------------
1 | package hxbt.composites;
2 | import hxbt.Behavior.Status;
3 |
4 | /**
5 | * A sequence goes through all behaviors attached to it from left to right.
6 | * Keeps going through behavior until all behaviors have returned SUCCESS.
7 | * If a single behavior returns FAILURE, the whole sequence fails.
8 | * @author Kristian Brodal
9 | * @author Kenton Hamaluik
10 | */
11 | class Sequence extends Composite
12 | {
13 | private var m_currentChild : Behavior;
14 | private var m_currentIndex : Int;
15 | private var m_allChildrenTick:Bool;
16 |
17 |
18 | /**
19 | * @param allChildrenTick Should the Sequence try to update a whole cycle
20 | * per tick or only one child.
21 | */
22 | public function new(allChildrenTick:Bool = false)
23 | {
24 | super();
25 | m_allChildrenTick = allChildrenTick;
26 | }
27 |
28 | override function onInitialize(context : T)
29 | {
30 | m_currentIndex = 0;
31 | }
32 |
33 | override function onTerminate(context : T, status : Status)
34 | {
35 | for(_child in m_children)
36 | {
37 | _child.status = Status.INVALID;
38 | }
39 | }
40 |
41 | function updateAllChildren(context : T, dt : Float) : Status
42 | {
43 | var status:Status;
44 | for(index in m_currentIndex...m_children.length)
45 | {
46 | m_currentIndex = index;
47 | m_currentChild = m_children[index];
48 | status = m_currentChild.tick(context, dt);
49 | if(status == Status.FAILURE)
50 | {
51 | m_currentIndex = 0;
52 | return Status.FAILURE;
53 |
54 | }
55 | else if(status == Status.RUNNING)
56 | {
57 | return Status.RUNNING;
58 | }
59 | }
60 | // If we got there, every children returned SUCCESS
61 | m_currentIndex = 0;
62 | return SUCCESS;
63 | }
64 |
65 | function updateChildPerChild(context : T, dt : Float) : Status
66 | {
67 | // get the current child which is being evaluated
68 | m_currentChild = m_children[m_currentIndex];
69 | var s = m_currentChild.tick(context, dt);
70 |
71 | // If the child failed or is still running, early return.
72 | if (s != Status.SUCCESS)
73 | {
74 | return s;
75 | }
76 | m_currentIndex++;
77 | // If end of array hit the whole sequence succeeded.
78 | if (m_currentIndex == m_children.length)
79 | {
80 | // Reset index otherwise it will crash on next run through
81 | m_currentIndex = 0;
82 | return Status.SUCCESS;
83 | }
84 |
85 | return Status.RUNNING;
86 | }
87 |
88 | override function update(context : T, dt : Float) : Status
89 | {
90 | if(m_allChildrenTick)
91 | {
92 | return updateAllChildren(context, dt);
93 | }
94 | else
95 | {
96 | return updateChildPerChild(context, dt);
97 | }
98 | }
99 | }
--------------------------------------------------------------------------------
/src/hxbt/decorators/AlwaysFail.hx:
--------------------------------------------------------------------------------
1 | package hxbt.decorators;
2 |
3 | import hxbt.Behavior.Status;
4 | import hxbt.Decorator;
5 |
6 | /**
7 | * Will always fail no matter whether the child succeeds or fails
8 | * @author Kenton Hamaluik
9 | */
10 | class AlwaysFail extends Decorator
11 | {
12 | public function new(?child : Behavior)
13 | {
14 | super(child);
15 | }
16 |
17 | override function update(context : T, dt : Float) : Status
18 | {
19 | return Status.FAILURE;
20 | }
21 | }
--------------------------------------------------------------------------------
/src/hxbt/decorators/AlwaysSucceed.hx:
--------------------------------------------------------------------------------
1 | package hxbt.decorators;
2 |
3 | import hxbt.Behavior.Status;
4 | import hxbt.Decorator;
5 |
6 | /**
7 | * Will always succeed no matter whether the child succeeds or fails
8 | * @author Kenton Hamaluik
9 | */
10 | class AlwaysSucceed extends Decorator
11 | {
12 | public function new(?child : Behavior)
13 | {
14 | super(child);
15 | }
16 |
17 | override function update(context : T, dt : Float) : Status
18 | {
19 | return Status.SUCCESS;
20 | }
21 | }
--------------------------------------------------------------------------------
/src/hxbt/decorators/Include.hx:
--------------------------------------------------------------------------------
1 | package hxbt.decorators;
2 |
3 | import hxbt.Behavior.Status;
4 | import hxbt.BehaviorTree;
5 | import hxbt.Decorator;
6 |
7 | /**
8 | * Includes an entire behaviour tree as a child and runs it
9 | * @author Kenton Hamaluik
10 | */
11 | class Include extends Decorator
12 | {
13 | var m_childTree : BehaviorTree;
14 |
15 | public function new(childTree : BehaviorTree)
16 | {
17 | super();
18 | m_childTree = childTree;
19 | }
20 |
21 | override function update(context : T, dt : Float) : Status
22 | {
23 | m_childTree.update(dt);
24 | return Status.SUCCESS;
25 | }
26 | }
--------------------------------------------------------------------------------
/src/hxbt/decorators/Invert.hx:
--------------------------------------------------------------------------------
1 | package hxbt.decorators;
2 |
3 | import hxbt.Behavior.Status;
4 | import hxbt.Decorator;
5 |
6 | /**
7 | * Returns success if the child returns failure,
8 | * returns failure if the child returns success,
9 | * otherwise returns what the child returns
10 | * @author Kenton Hamaluik
11 | */
12 | class Invert extends Decorator
13 | {
14 | public function new(?child : Behavior)
15 | {
16 | super(child);
17 | }
18 |
19 | override function update(context : T, dt : Float) : Status
20 | {
21 | if(m_child == null)
22 | {
23 | throw "Inverter requires child to not be null!";
24 | }
25 |
26 | var childResult : Status = m_child.tick(context, dt);
27 | if(childResult == Status.SUCCESS)
28 | {
29 | return Status.FAILURE;
30 | }
31 | else if(childResult == Status.FAILURE)
32 | {
33 | return Status.SUCCESS;
34 | }
35 | return childResult;
36 | }
37 | }
--------------------------------------------------------------------------------
/src/hxbt/decorators/UntilFail.hx:
--------------------------------------------------------------------------------
1 | package hxbt.decorators;
2 |
3 | import hxbt.Behavior.Status;
4 | import hxbt.Decorator;
5 |
6 | /**
7 | * will repeat the child until it fails
8 | * @author Kenton Hamaluik
9 | */
10 | class UntilFail extends Decorator
11 | {
12 | public function new(?child : Behavior)
13 | {
14 | super(child);
15 | }
16 |
17 | override function update(context : T, dt : Float) : Status
18 | {
19 | var childResult : Status = m_child.tick(context, dt);
20 | if(childResult != Status.FAILURE)
21 | {
22 | return Status.RUNNING;
23 | }
24 |
25 | return Status.SUCCESS;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/hxbt/decorators/UntilSuccess.hx:
--------------------------------------------------------------------------------
1 | package hxbt.decorators;
2 |
3 | import hxbt.Behavior.Status;
4 | import hxbt.Decorator;
5 |
6 | /**
7 | * will repeat the child until it succeeds
8 | * @author Kenton Hamaluik
9 | */
10 | class UntilSuccess extends Decorator
11 | {
12 | public function new(?child : Behavior)
13 | {
14 | super(child);
15 | }
16 |
17 | override function update(context : T, dt : Float) : Status
18 | {
19 | var childResult : Status = m_child.tick(context, dt);
20 | if(childResult != Status.SUCCESS)
21 | {
22 | return Status.RUNNING;
23 | }
24 |
25 | return Status.SUCCESS;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/hxbt/loaders/BehaviorTreeJSONLoader.hx:
--------------------------------------------------------------------------------
1 | package hxbt.loaders;
2 | import hxbt.Behavior;
3 | import hxbt.BehaviorTree;
4 | import hxbt.composites.Parallel;
5 | import hxbt.composites.Sequence;
6 | import hxbt.composites.Selector;
7 | import hxbt.decorators.AlwaysFail;
8 | import hxbt.decorators.AlwaysSucceed;
9 | import hxbt.decorators.Include;
10 | import hxbt.decorators.Invert;
11 | import hxbt.decorators.UntilFail;
12 | import hxbt.decorators.UntilSuccess;
13 |
14 | /**
15 | * The BehaviorTreeJSONLoader loads a behavior from a JSON file
16 | * with a very simple structure.
17 | * The structure of the behavior tree JSON is the following:
18 |
19 | {
20 | "test_tree": {
21 | "hxbt.sequence": [
22 | { "package.behavior0": [] },
23 | { "package.behavior1": [] },
24 | { "hxbt.selector": [
25 | { "package.behavior2": [] },
26 | { "package.behavior3": [] }
27 | ]}
28 | ]
29 | }
30 | }
31 |
32 | * @author Kristian Brodal
33 | * @author Kenton Hamaluik
34 | */
35 | class BehaviorTreeJSONLoader
36 | {
37 |
38 | private function new()
39 | {
40 |
41 | }
42 |
43 | // Constructs the behavior tree 'treeName' defined in the JSON file 'JSON'.
44 | // Returns constructed tree if successful, returns null if unsuccessful.
45 | public static function FromJSONString(JSON : String, treeName : String) : BehaviorTree
46 | {
47 | return FromJSONObject(haxe.Json.parse(JSON), treeName);
48 | }
49 |
50 | // Constructs the behavior tree 'treeName' defined in the JSON file 'JSON'.
51 | // Returns constructed tree if successful, returns null if unsuccessful.
52 | public static function FromJSONObject(JSON : Dynamic, treeName : String) : BehaviorTree
53 | {
54 | if(!Reflect.hasField(JSON, treeName)) {
55 | throw "Behaviour tree '" + treeName + "' doesn't exist in this JSON!";
56 | }
57 |
58 | var tree = Reflect.field(JSON, treeName);
59 | var roots:Array = Reflect.fields(tree);
60 | trace(roots);
61 | #if !behaviour
62 | if(roots.length != 1) {
63 | #else
64 | if(roots.length != 2) {
65 | #end
66 | throw "Behaviour tree '" + treeName + "' must have a singular root! It has " + roots.length + "!";
67 | }
68 | var root = Reflect.field(tree, roots[0]);
69 |
70 | var tree:BehaviorTree = new BehaviorTree();
71 | var rootBehaviour:Behavior = GetBehavior(root, treeName);
72 | tree.setRoot(rootBehaviour);
73 | return tree;
74 | }
75 |
76 | private static function GetBehavior(object : Dynamic, errorTrail : String):Behavior
77 | {
78 | // we should be recieving an array of fields
79 | // we actually only want there to be one field (JSON is kinda dumb that way)
80 | var rootFields:Array = Reflect.fields(object);
81 | if(rootFields.length != 1) {
82 | throw "Object '" + errorTrail + "' must have exactly 1 child! It has " + rootFields.length + "!";
83 | }
84 | var name:String = rootFields[0];
85 | var objects:Array = Reflect.field(object, name);
86 |
87 | switch(name) {
88 | case 'hxbt.sequence': {
89 | var sequence:Sequence = new Sequence();
90 | var arrI:Int = 0;
91 | for(object in objects) {
92 | sequence.add(GetBehavior(object, errorTrail + "." + name + ".[" + arrI + "]"));
93 | arrI++;
94 | }
95 | return sequence;
96 | }
97 |
98 | case 'hxbt.selector': {
99 | var selector:Selector = new Selector();
100 | var arrI:Int = 0;
101 | for(object in objects) {
102 | selector.add(GetBehavior(object, errorTrail + "." + name + ".[" + arrI + "]"));
103 | arrI++;
104 | }
105 | return selector;
106 | }
107 |
108 | case 'hxbt.parallel': {
109 | var parallel:Parallel = new Parallel();
110 | var arrI:Int = 0;
111 | for(object in objects) {
112 | parallel.add(GetBehavior(object, errorTrail + "." + name + ".[" + arrI + "]"));
113 | arrI++;
114 | }
115 | return parallel;
116 | }
117 |
118 | case 'hxbt.alwaysfail': {
119 | if(objects.length > 1) {
120 | throw "hxbt.alwaysfail can't have more than one child!";
121 | }
122 | var alwaysFail:AlwaysFail = new AlwaysFail();
123 | if(objects.length > 0) {
124 | alwaysFail.setChild(GetBehavior(objects[0], errorTrail + "." + name));
125 | }
126 | return alwaysFail;
127 | }
128 |
129 | case 'hxbt.alwayssucceed': {
130 | if(objects.length > 1) {
131 | throw "hxbt.alwayssucceed can't have more than one child!";
132 | }
133 | var alwaysSucceed:AlwaysSucceed = new AlwaysSucceed();
134 | if(objects.length > 0) {
135 | alwaysSucceed.setChild(GetBehavior(objects[0], errorTrail + "." + name));
136 | }
137 | return alwaysSucceed;
138 | }
139 |
140 | case 'hxbt.include': {
141 | throw "hxbt.include is not supported in JSON yet!";
142 | }
143 |
144 | case 'hxbt.invert': {
145 | if(objects.length != 1) {
146 | throw "hxbt.invert MUST have exactly one child!";
147 | }
148 | var invert:Invert = new Invert();
149 | invert.setChild(GetBehavior(objects[0], errorTrail + "." + name));
150 | return invert;
151 | }
152 |
153 | case 'hxbt.untilfail': {
154 | if(objects.length != 1) {
155 | throw "hxbt.untilfail MUST have exactly one child!";
156 | }
157 | var untilFail:UntilFail = new UntilFail();
158 | untilFail.setChild(GetBehavior(objects[0], errorTrail + "." + name));
159 | return untilFail;
160 | }
161 |
162 | case 'hxbt.untilsuccess': {
163 | if(objects.length != 1) {
164 | throw "hxbt.untilsuccess MUST have exactly one child!";
165 | }
166 | var untilSuccess:UntilSuccess = new UntilSuccess();
167 | untilSuccess.setChild(GetBehavior(objects[0], errorTrail + "." + name));
168 | return untilSuccess;
169 | }
170 |
171 | default: {
172 | var t = Type.resolveClass(name);
173 | if(t == null) {
174 | throw "Can't resolve behavior class '" + name + "' @ '" + errorTrail + "'!";
175 | }
176 |
177 | var inst = Type.createInstance(Type.resolveClass(name), []);
178 | if(!Std.is(inst, Behavior)) {
179 | throw "Error: class '" + name + "' isn't a behavior @ '" + errorTrail + "'!";
180 | }
181 | return inst;
182 | }
183 | }
184 | }
185 | }
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | behaviour.n
--------------------------------------------------------------------------------
/tests/.travis.yml:
--------------------------------------------------------------------------------
1 | # Travis now has haxe support, but something is going wrong. Try the old-school way
2 | language: python
3 |
4 | # Install Haxe before running the test.
5 | before_script:
6 | - sudo apt-get update > /dev/null # run update before installing anything
7 | - sudo apt-get install python-software-properties -y > /dev/null # for the next command
8 | - sudo add-apt-repository ppa:eyecreate/haxe -y > /dev/null # add the ubuntu ppa that contains haxe
9 | - sudo apt-get update > /dev/null # pull info from ppa
10 | - sudo apt-get install haxe -y > /dev/null # install haxe (and neko)
11 | - mkdir ~/haxelib > /dev/null # create a folder for installing haxelib
12 | - haxelib setup ~/haxelib > /dev/null
13 | - haxelib install buddy > /dev/null
14 |
15 | # Run the test!
16 | script:
17 | - haxe behaviour.hxml
18 | - neko behaviour.n
--------------------------------------------------------------------------------
/tests/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Kristian Brodal
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | # hxbt
2 |
3 | Behavior Tree implementation for Haxe.
4 |
5 | [](https://raw.githubusercontent.com/whuop/hxbt/master/LICENSE) [](https://github.com/whuop/hxbt) [](https://travis-ci.org/whuop/hxbt)
--------------------------------------------------------------------------------
/tests/behaviour.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cls
3 | haxe behaviour.hxml
4 | if %errorlevel% neq 0 exit /b %errorlevel%
5 | neko behaviour.n
--------------------------------------------------------------------------------
/tests/behaviour.hxml:
--------------------------------------------------------------------------------
1 | -D behaviour
2 | -lib buddy
3 | -main behaviour.BehaviourMain
4 | -cp src
5 | -neko behaviour.n
--------------------------------------------------------------------------------
/tests/config.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/tests/hxbt.hxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | "$(CompilerPath)/haxelib" run flow build $(TargetBuild) --$(BuildConfig)
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/tests/project.flow:
--------------------------------------------------------------------------------
1 | {
2 | project : {
3 | name : 'hxbt',
4 | version : '1.0.0',
5 | author : 'Kristian Brodal',
6 |
7 | app : {
8 | name : 'hxbt',
9 | package : 'com.luxeengine.empty',
10 | main : 'Main'
11 | },
12 |
13 | build : {
14 | dependencies : {
15 | luxe : '*',
16 | buddy : '*'
17 | }
18 | },
19 |
20 | files : {
21 | config : 'config.json',
22 | assets : 'assets/'
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/src/Main.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | #if !behaviour
4 | import hxbt.Behavior;
5 | import hxbt.Behavior.Status;
6 | import hxbt.BehaviorTree;
7 | import hxbt.composites.Sequence;
8 | import luxe.Component;
9 | import luxe.Input;
10 | import luxe.Sprite;
11 | import luxe.Vector;
12 | import luxe.Color;
13 | import luxe.resource.Resource;
14 |
15 | import sample.Walk;
16 | import sample.Door;
17 |
18 | import sample.WalkToDoorBehaviour;
19 | import sample.OpenDoorBehaviour;
20 | import sample.WalkThroughDoorBehaviour;
21 | import sample.CloseDoorBehaviour;
22 | import sample.BeLazyBehaviour;
23 |
24 | typedef ActorContext = {
25 | var actor:Sprite;
26 | var door:Sprite;
27 | }
28 |
29 | class Main extends luxe.Game
30 | {
31 | var actor:Sprite;
32 | var door:Sprite;
33 | var behaviourTree:BehaviorTree;
34 |
35 | override function ready()
36 | {
37 | // create our dude who's going to walk around
38 | var actor:Sprite = new Sprite({
39 | pos: new Vector((Luxe.screen.w / 2) - 128, Luxe.screen.h / 2),
40 | size: new Vector(16, 16),
41 | color: new Color(0.9, 0.9, 0.9, 1),
42 | origin: new Vector(8, 16)
43 | });
44 | actor.add(new Walk(128));
45 |
46 | // create a door for him to walk through
47 | door = new Sprite({
48 | pos: new Vector((Luxe.screen.w / 2) + 128, Luxe.screen.h / 2),
49 | size: new Vector(16, 32),
50 | color: new Color(0.5, 0.25, 0, 1),
51 | origin: new Vector(8, 32)
52 | });
53 | door.add(new Door(16, 2));
54 |
55 | var load = Luxe.resources.load_json("assets/behavior_trees.json");
56 |
57 | load.then(function(json:JSONResource){
58 | try {
59 | behaviourTree = hxbt.loaders.BehaviorTreeJSONLoader.FromJSONObject(json.asset.json, "WalkThroughDoorAndStop");
60 | behaviourTree.setContext({
61 | actor: actor,
62 | door: door
63 | });
64 | }
65 | catch(err:String) {
66 | trace("ERROR: " + err);
67 | }
68 | });
69 | }
70 |
71 | override function onkeyup(e:KeyEvent)
72 | {
73 | if(e.keycode == Key.escape)
74 | Luxe.shutdown();
75 | }
76 |
77 | override function update(dt:Float)
78 | {
79 | if(behaviourTree != null) {
80 | behaviourTree.period = dt; // make it update every frame
81 | behaviourTree.update(dt);
82 | }
83 | }
84 | }
85 |
86 | #end
--------------------------------------------------------------------------------
/tests/src/behaviour/BehaviourMain.hx:
--------------------------------------------------------------------------------
1 | package behaviour;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | class BehaviourMain extends BuddySuite implements Buddy {
7 | public function new() {
8 | // nothing here for now
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/BehaviorBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 |
9 | /**
10 | * ...
11 | * @author Kenton Hamaluik
12 | */
13 | class BehaviorBehaviour extends BuddySuite
14 | {
15 | public function new()
16 | {
17 | var behaviour = new MockBehaviour();
18 |
19 | describe("Using a subclassed behaviour", {
20 | it("should not have been initialized without ticking", {
21 | behaviour.m_initializedCalled.should.be(0);
22 | });
23 | it("should should auto-initialize on it's first tick", {
24 | behaviour.tick(null, 0.1);
25 | behaviour.m_initializedCalled.should.be(1);
26 | });
27 | it("should have updated on it's first tick", {
28 | behaviour.m_updateCalled.should.be(1);
29 | });
30 | it("should not have terminated without the leaf saying so", {
31 | behaviour.m_terminateCalled.should.be(0);
32 | behaviour.m_terminateStatus.should.be(null);
33 | });
34 | it("should not re-initialize after the first time without terminating first", {
35 | behaviour.tick(null, 0.1);
36 | behaviour.m_initializedCalled.should.be(1);
37 | behaviour.m_updateCalled.should.be(2);
38 | });
39 | it("should terminate whe the leaf says so", {
40 | behaviour.m_operationResult = Status.SUCCESS;
41 | behaviour.tick(null, 0.1);
42 | behaviour.m_updateCalled.should.be(3);
43 | behaviour.m_terminateCalled.should.be(1);
44 | behaviour.m_terminateStatus.should.be(Status.SUCCESS);
45 | });
46 | });
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/BehaviorTreeBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.BehaviorTree;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class BehaviorTreeBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var tree = new BehaviorTree();
19 | var behavior:MockBehaviour = new MockBehaviour();
20 | tree.setRoot(behavior);
21 |
22 | describe("Using a behavior tree", {
23 | it("should wait until the correct time to tick its root", {
24 | tree.update(tree.period / 2);
25 | behavior.m_initializedCalled.should.be(0);
26 | tree.update(tree.period / 2);
27 | behavior.m_initializedCalled.should.be(1);
28 | });
29 | });
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/MockBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt;
2 |
3 | import hxbt.Behavior;
4 |
5 | class MockBehaviour extends Behavior
6 | {
7 | public var m_initializedCalled = 0;
8 | public var m_terminateCalled = 0;
9 | public var m_updateCalled = 0;
10 |
11 | public var m_terminateStatus : Status;
12 |
13 | public var m_operationResult:Status = null;
14 |
15 | public function new()
16 | {
17 | super();
18 | }
19 |
20 | override function onInitialize(context : Dynamic) : Void
21 | {
22 | m_initializedCalled++;
23 | }
24 |
25 | override function onTerminate(context : Dynamic, status : Status) : Void
26 | {
27 | m_terminateCalled++;
28 | m_terminateStatus = status;
29 | }
30 |
31 | override function update(context : Dynamic, dt : Float) : Status
32 | {
33 | m_updateCalled++;
34 | if(m_operationResult != null)
35 | {
36 | return m_operationResult;
37 | }
38 | return Status.RUNNING;
39 | }
40 | }
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/composites/ParallelBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.composites;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.composites.Parallel;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class ParallelBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var parallel:Parallel = new Parallel();
19 |
20 | var behaviourA:MockBehaviour = new MockBehaviour();
21 | var behaviourB:MockBehaviour = new MockBehaviour();
22 |
23 | parallel.add(behaviourA);
24 | parallel.add(behaviourB);
25 |
26 | describe("Using a parallel", {
27 | it("should initialize every child on the first tick", {
28 | parallel.tick(null, 0.1);
29 | behaviourA.m_initializedCalled.should.be(1);
30 | behaviourB.m_initializedCalled.should.be(1);
31 | });
32 | it("should update every child only once on each tick", {
33 | behaviourA.m_updateCalled.should.be(1);
34 | behaviourB.m_updateCalled.should.be(1);
35 | });
36 | it("should keep running if not all of its children succeed", {
37 | behaviourA.m_operationResult = Status.SUCCESS;
38 | parallel.tick(null, 0.1);
39 | parallel.status.should.be(Status.RUNNING);
40 | });
41 | it("should succeed if all of its children succeed", {
42 | behaviourB.m_operationResult = Status.SUCCESS;
43 | parallel.tick(null, 0.1);
44 | parallel.status.should.be(Status.SUCCESS);
45 | });
46 | it("should fail if any of it's children fail", {
47 | behaviourA.m_operationResult = null;
48 | behaviourB.m_operationResult = null;
49 | parallel.tick(null, 0.1);
50 |
51 | behaviourB.m_operationResult = Status.FAILURE;
52 | parallel.tick(null, 0.1);
53 | parallel.status.should.be(Status.FAILURE);
54 | });
55 | it("should invalidate all its children when it terminates", {
56 | behaviourA.status.should.be(Status.INVALID);
57 | behaviourB.status.should.be(Status.INVALID);
58 | });
59 | });
60 | }
61 | }
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/composites/SelectorBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.composites;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.composites.Selector;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class SelectorBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var selector:Selector = new Selector();
19 |
20 | var behaviourA:MockBehaviour = new MockBehaviour();
21 | var behaviourB:MockBehaviour = new MockBehaviour();
22 |
23 | selector.add(behaviourA);
24 | selector.add(behaviourB);
25 |
26 | describe("Using a selector", {
27 | it("should initialize only the first child on the first tick", {
28 | selector.tick(null, 0.1);
29 | behaviourA.m_initializedCalled.should.be(1);
30 | behaviourB.m_initializedCalled.should.be(0);
31 | });
32 | it("should not move on to the next child if the first one hasn't finished", {
33 | selector.tick(null, 0.1);
34 | behaviourA.m_updateCalled.should.be(2);
35 | behaviourB.m_initializedCalled.should.be(0);
36 | });
37 | it("should move on to the next child if the first fails", {
38 | behaviourA.m_operationResult = Status.FAILURE;
39 | selector.tick(null, 0.1);
40 | selector.tick(null, 0.1);
41 | behaviourB.m_initializedCalled.should.be(1);
42 | });
43 | it("should succeed if a child succeeds", {
44 | behaviourB.m_operationResult = Status.SUCCESS;
45 | selector.tick(null, 0.1);
46 | selector.status.should.be(Status.SUCCESS);
47 | });
48 | it("should report as running if its children are running", {
49 | behaviourA.m_operationResult = null;
50 | behaviourB.m_operationResult = null;
51 | selector.tick(null, 0.1);
52 | selector.status.should.be(Status.RUNNING);
53 | });
54 | it("should fail if all children report failure", {
55 | selector = new Selector();
56 | selector.add(behaviourA);
57 | selector.add(behaviourB);
58 | behaviourA.m_operationResult = Status.FAILURE;
59 | behaviourB.m_operationResult = Status.FAILURE;
60 |
61 | selector.tick(null, 0.1);
62 | selector.tick(null, 0.1);
63 |
64 | selector.status.should.be(Status.FAILURE);
65 | });
66 | it("should invalidate all its children when it terminates", {
67 | behaviourA.status.should.be(Status.INVALID);
68 | behaviourB.status.should.be(Status.INVALID);
69 | });
70 | });
71 | }
72 | }
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/composites/SequenceBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.composites;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.composites.Sequence;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class SequenceBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var sequence:Sequence = new Sequence();
19 |
20 | var behaviourA:MockBehaviour = new MockBehaviour();
21 | var behaviourB:MockBehaviour = new MockBehaviour();
22 |
23 | sequence.add(behaviourA);
24 | sequence.add(behaviourB);
25 |
26 | describe("Using a sequence", {
27 | it("should initialize only the first child on the first tick", {
28 | sequence.tick(null, 0.1);
29 | behaviourA.m_initializedCalled.should.be(1);
30 | behaviourB.m_initializedCalled.should.be(0);
31 | });
32 | it("should not move on to the next child if the first one hasn't finished", {
33 | sequence.tick(null, 0.1);
34 | behaviourA.m_updateCalled.should.be(2);
35 | behaviourB.m_initializedCalled.should.be(0);
36 | });
37 | it("should move on to the next child if the first finishes", {
38 | behaviourA.m_operationResult = Status.SUCCESS;
39 | sequence.tick(null, 0.1);
40 | sequence.tick(null, 0.1);
41 | behaviourA.m_updateCalled.should.be(3);
42 | behaviourB.m_initializedCalled.should.be(1);
43 | });
44 | it("should succeed if all children succeed", {
45 | behaviourB.m_operationResult = Status.SUCCESS;
46 | sequence.tick(null, 0.1);
47 | sequence.status.should.be(Status.SUCCESS);
48 | });
49 | it("should report as running if its children are running", {
50 | behaviourA.m_operationResult = null;
51 | behaviourB.m_operationResult = null;
52 | sequence.tick(null, 0.1);
53 | sequence.status.should.be(Status.RUNNING);
54 | });
55 | it("should fail if a child returns failure", {
56 | behaviourA.m_operationResult = Status.FAILURE;
57 | sequence.tick(null, 0.1);
58 | sequence.status.should.be(Status.FAILURE);
59 | });
60 | it("should invalidate all its children when it terminates", {
61 | behaviourA.status.should.be(Status.INVALID);
62 | behaviourB.status.should.be(Status.INVALID);
63 | });
64 | });
65 | }
66 | }
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/decorators/AlwaysFailBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.decorators;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.decorators.AlwaysFail;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class AlwaysFailBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var decorator:AlwaysFail = new AlwaysFail();
19 | var behaviour = new MockBehaviour();
20 | decorator.setChild(behaviour);
21 |
22 | describe("Using an always fail decorator", {
23 | it("should respond with failure if the child succeeds", {
24 | behaviour.m_operationResult = Status.SUCCESS;
25 | decorator.tick(null, 0.1);
26 | decorator.status.should.be(Status.FAILURE);
27 | });
28 | it("should respond with failure if the child fails", {
29 | behaviour.m_operationResult = Status.FAILURE;
30 | decorator.tick(null, 0.1);
31 | decorator.status.should.be(Status.FAILURE);
32 | });
33 | it("should respond with failure if the child is running", {
34 | behaviour.m_operationResult = null;
35 | decorator.tick(null, 0.1);
36 | decorator.status.should.be(Status.FAILURE);
37 | });
38 | it("should respond with failure if the child is invalid", {
39 | behaviour.m_operationResult = Status.INVALID;
40 | decorator.tick(null, 0.1);
41 | decorator.status.should.be(Status.FAILURE);
42 | });
43 | });
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/decorators/AlwaysSucceedBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.decorators;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.decorators.AlwaysSucceed;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class AlwaysSucceedBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var decorator:AlwaysSucceed = new AlwaysSucceed();
19 | var behaviour = new MockBehaviour();
20 | decorator.setChild(behaviour);
21 |
22 | describe("Using an always succeed decorator", {
23 | it("should respond with success if the child succeeds", {
24 | behaviour.m_operationResult = Status.SUCCESS;
25 | decorator.tick(null, 0.1);
26 | decorator.status.should.be(Status.SUCCESS);
27 | });
28 | it("should respond with success if the child fails", {
29 | behaviour.m_operationResult = Status.FAILURE;
30 | decorator.tick(null, 0.1);
31 | decorator.status.should.be(Status.SUCCESS);
32 | });
33 | it("should respond with success if the child is running", {
34 | behaviour.m_operationResult = null;
35 | decorator.tick(null, 0.1);
36 | decorator.status.should.be(Status.SUCCESS);
37 | });
38 | it("should respond with success if the child is invalid", {
39 | behaviour.m_operationResult = Status.INVALID;
40 | decorator.tick(null, 0.1);
41 | decorator.status.should.be(Status.SUCCESS);
42 | });
43 | });
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/decorators/IncludeBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.decorators;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import hxbt.BehaviorTree;
8 | import behaviour.hxbt.MockBehaviour;
9 | import hxbt.decorators.Include;
10 |
11 | /**
12 | * ...
13 | * @author Kenton Hamaluik
14 | */
15 | class IncludeBehaviour extends BuddySuite
16 | {
17 | public function new()
18 | {
19 | var tree:BehaviorTree = new BehaviorTree();
20 | var behaviour = new MockBehaviour();
21 | tree.setRoot(behaviour);
22 | var decorator:Include = new Include(tree);
23 |
24 | describe("Using an include decorator", {
25 | it("should update the tree on ticks", {
26 | decorator.tick(null, 5);
27 | behaviour.m_initializedCalled.should.be(1);
28 | });
29 | it("should always respond with success", {
30 | decorator.status.should.be(Status.SUCCESS);
31 | });
32 | });
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/decorators/InvertBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.decorators;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.decorators.Invert;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class InvertBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var decorator:Invert = new Invert();
19 | var behaviour = new MockBehaviour();
20 | decorator.setChild(behaviour);
21 |
22 | describe("Using an inverter decorator", {
23 | it("should respond with failure if the child succeeds", {
24 | behaviour.m_operationResult = Status.SUCCESS;
25 | decorator.tick(null, 0.1);
26 | decorator.status.should.be(Status.FAILURE);
27 | });
28 | it("should respond with success if the child fails", {
29 | behaviour.m_operationResult = Status.FAILURE;
30 | decorator.tick(null, 0.1);
31 | decorator.status.should.be(Status.SUCCESS);
32 | });
33 | it("should respond with running if the child is running", {
34 | behaviour.m_operationResult = null;
35 | decorator.tick(null, 0.1);
36 | decorator.status.should.be(Status.RUNNING);
37 | });
38 | it("should respond with invalid if the child is invalid", {
39 | behaviour.m_operationResult = Status.INVALID;
40 | decorator.tick(null, 0.1);
41 | decorator.status.should.be(Status.INVALID);
42 | });
43 | });
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/decorators/UntilFailBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.decorators;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.decorators.UntilFail;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class UntilFailBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var decorator:UntilFail = new UntilFail();
19 | var behaviour = new MockBehaviour();
20 | decorator.setChild(behaviour);
21 |
22 | describe("Using an until fail decorator", {
23 | it("should report as running if the child is running", {
24 | behaviour.m_operationResult = null;
25 | decorator.tick(null, 0.1);
26 | decorator.status.should.be(Status.RUNNING);
27 | });
28 | it("should report as running if the child succeeds", {
29 | behaviour.m_operationResult = Status.SUCCESS;
30 | decorator.tick(null, 0.1);
31 | decorator.status.should.be(Status.RUNNING);
32 | });
33 | it("should report as success if the child fails", {
34 | behaviour.m_operationResult = Status.FAILURE;
35 | decorator.tick(null, 0.1);
36 | decorator.status.should.be(Status.SUCCESS);
37 | });
38 | });
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/decorators/UntilSucceedBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.decorators;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import behaviour.hxbt.MockBehaviour;
8 | import hxbt.decorators.UntilSuccess;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class UntilSucceedBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | var decorator:UntilSuccess = new UntilSuccess();
19 | var behaviour = new MockBehaviour();
20 | decorator.setChild(behaviour);
21 |
22 | describe("Using an until success decorator", {
23 | it("should report as running if the child is running", {
24 | behaviour.m_operationResult = null;
25 | decorator.tick(null, 0.1);
26 | decorator.status.should.be(Status.RUNNING);
27 | });
28 | it("should report as running if the child fails", {
29 | behaviour.m_operationResult = Status.FAILURE;
30 | decorator.tick(null, 0.1);
31 | decorator.status.should.be(Status.RUNNING);
32 | });
33 | it("should report as success if the child succeeds", {
34 | behaviour.m_operationResult = Status.SUCCESS;
35 | decorator.tick(null, 0.1);
36 | decorator.status.should.be(Status.SUCCESS);
37 | });
38 | });
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/tests/src/behaviour/hxbt/loaders/BehaviorTreeJSONLoaderBehaviour.hx:
--------------------------------------------------------------------------------
1 | package behaviour.hxbt.loaders;
2 |
3 | import buddy.*;
4 | using buddy.Should;
5 |
6 | import hxbt.Behavior.Status;
7 | import hxbt.BehaviorTree;
8 | import hxbt.loaders.BehaviorTreeJSONLoader;
9 |
10 | /**
11 | * ...
12 | * @author Kenton Hamaluik
13 | */
14 | class BehaviorTreeJSONLoaderBehaviour extends BuddySuite
15 | {
16 | public function new()
17 | {
18 | describe("Using a JSON loader", {
19 | it("should be able to instantiate a tree from a JSON object", {});
20 | it("should be able to instantiate a tree containing HXBT nodes", {});
21 | it("should be able to instantiate a tree containing custom leaf behaviours", {});
22 | it("should be able to instantiate a tree from a JSON string", {
23 | // for some reason this fails in the tests but works normally ?
24 | /*var jsonString = '
25 | {
26 | "WalkThroughDoorAndStop": [{
27 | "hxbt.sequence": [
28 | { "sample.WalkToDoorBehaviour": [] },
29 | { "sample.OpenDoorBehaviour": [] },
30 | { "sample.WalkThroughDoorBehaviour": [] },
31 | { "sample.CloseDoorBehaviour": [] },
32 | { "sample.BeLazyBehaviour": [] }
33 | ]
34 | }]
35 | }
36 | ';
37 | hxbt.loaders.BehaviorTreeJSONLoader.FromJSONString.bind(jsonString, "TestTree").should.not.throwType(String);
38 | var tree:BehaviorTree = hxbt.loaders.BehaviorTreeJSONLoader.FromJSONString(jsonString, "WalkThroughDoorAndStop");
39 | tree.should.not.be(null);*/
40 | });
41 | });
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------