├── .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 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 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 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 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 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 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 | 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 | [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://raw.githubusercontent.com/whuop/hxbt/master/LICENSE) [![Version](http://img.shields.io/github/tag/whuop/hxbt.svg?style=flag&label=version)](https://github.com/whuop/hxbt) [![Build Status](https://travis-ci.org/whuop/hxbt.svg?branch=master)](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 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 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 | 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 | --------------------------------------------------------------------------------