├── LICENSE.md.meta ├── README.md.meta ├── Core.meta ├── Ex.meta ├── Ex ├── Actions.meta ├── Editor.meta ├── Conditionals.meta ├── Actions │ ├── BTActionMove.cs.meta │ ├── BTActionPlayAnimation.cs.meta │ ├── BTActionPlayAnimation.cs │ └── BTActionMove.cs ├── Editor │ ├── BTTreeWindow.cs.meta │ └── BTTreeWindow.cs └── Conditionals │ ├── BTCheckWithinDistance.cs.meta │ └── BTCheckWithinDistance.cs ├── Utilities.meta ├── Core ├── Actions.meta ├── Composites.meta ├── Conditional.meta ├── Decorator.meta ├── BTDatabase.cs.meta ├── BTNode.cs.meta ├── BTTree.cs.meta ├── Actions │ ├── BTAction.cs.meta │ ├── BTActionLog.cs.meta │ ├── BTActionWait.cs.meta │ ├── BTActionCallback.cs.meta │ ├── BTActionResetData.cs.meta │ ├── BTActionWaitRandom.cs.meta │ ├── BTActionBlank.cs │ ├── BTActionBlank.cs.meta │ ├── BTActionLog.cs │ ├── BTActionCallback.cs │ ├── BTActionWait.cs │ ├── BTActionWaitRandom.cs │ ├── BTActionResetData.cs │ └── BTAction.cs ├── Decorator │ ├── BTInverter.cs.meta │ ├── BTRepeater.cs.meta │ ├── BTTimer.cs.meta │ ├── BTDecorator.cs.meta │ ├── BTConditionEvaluator.cs.meta │ ├── BTInverter.cs │ ├── BTDecorator.cs │ ├── BTTimer.cs │ ├── BTRepeater.cs │ └── BTConditionEvaluator.cs ├── Composites │ ├── BTComposite.cs.meta │ ├── BTSelector.cs.meta │ ├── BTSequence.cs.meta │ ├── BTSimpleParallel.cs.meta │ ├── BTComposite.cs │ ├── BTSequence.cs │ ├── BTSelector.cs │ └── BTSimpleParallel.cs ├── Conditional │ ├── BTCompareData.cs.meta │ ├── BTConditional.cs.meta │ ├── BTConditional.cs │ └── BTCompareData.cs ├── BTTree.cs ├── BTNode.cs └── BTDatabase.cs ├── Utilities ├── BTConfiguration.cs ├── BTConfiguration.cs.meta ├── BTConstants.cs.meta └── BTConstants.cs ├── README.md └── LICENSE.md /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ab7ac9ef995174155ae4f68939b9f839 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: efc2306f880444f8a8740a990351231e 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Core.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 58a1938cc571344e88f47d8e48583822 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Ex.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b3a24aae389874e089ec4a9614e93ff9 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Ex/Actions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d54891fae93474b8e8eabd89cdc43aad 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Ex/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 019de30b31a654f5bb481e441925cb57 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Utilities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e45e2f7b94c1b43319882cfa553a1805 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Core/Actions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ee380ce154f86495dbf7bcf8b389284b 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Core/Composites.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c00666672d0184fa391a90070ca31ee5 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Core/Conditional.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 64f63228f2ded45709e0c6e1b2d8a244 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Core/Decorator.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99c39cc6be4a64629b7036f828c2756f 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Ex/Conditionals.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1425df5a624024f6ca124722e3fd97b8 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Utilities/BTConfiguration.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | public static class BTConfiguration { 7 | 8 | public static bool ENABLE_DATABASE_LOG; 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /Core/BTDatabase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c9b6319543e544e18be5f821b87d5c4a 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/BTNode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ac0fbb8cc017a49b197277ec04d11585 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/BTTree.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 935793290862e466b8b0a1ec3f967cdb 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Actions/BTAction.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f549884ff898d4972bcb29c74285a576 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Actions/BTActionLog.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5921696601bf34024a99db06652de863 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Actions/BTActionWait.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7c1487b92a8544c48bfaf12957dca828 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Decorator/BTInverter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f414f1bd267540f69d791dea978ae7d 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Decorator/BTRepeater.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 98d4592a2a13b43429ec1b60c31d23cf 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Decorator/BTTimer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 62a0fd374282f4c4787e53bc736b7407 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Ex/Actions/BTActionMove.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a9eed8a25d1904ff5b0831614a201460 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Ex/Editor/BTTreeWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 612e2245a2080457ba4cc84657e1ba2f 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Utilities/BTConfiguration.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 347de96cc8e654c58b501e3e43b788b3 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Utilities/BTConstants.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ff50dd6cec454ff68e2ddfe15f305df 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Actions/BTActionCallback.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6ed71d8cbbc6e4320b27f692c166c119 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Actions/BTActionResetData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7b64d41536db740cdbd4939ffa3b8f77 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Composites/BTComposite.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e0821614481334c26a2665cd1b9468ef 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Composites/BTSelector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7bdeafdfe6f1b492fb1b84044588169f 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Composites/BTSequence.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad293c4dabcac431f9c6e4d9d3e2f2f4 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Conditional/BTCompareData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e53a29c28e092404fb99478df6c5b10f 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Conditional/BTConditional.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f6a19ff51f0b149d0bdc14d89a6949c4 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Decorator/BTDecorator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 45a2c8614f6944b1584a4a269deaf350 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Actions/BTActionWaitRandom.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 65cd102e5f01f4827a075d7209e6a112 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Composites/BTSimpleParallel.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bbe7b99436a2b4276b7263bef1fbd538 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Decorator/BTConditionEvaluator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d1206adc2ec764f13bca5009869532e4 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Ex/Actions/BTActionPlayAnimation.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 88633afd7fe5b4892a91e06853c8ffda 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Ex/Conditionals/BTCheckWithinDistance.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e1a6b8c6fe12a4eed91e2ebb3a46011b 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Core/Actions/BTActionBlank.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | public class BTActionBlank : BTAction { 7 | 8 | protected override BTResult Execute () { 9 | return BTResult.Running; 10 | } 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /Core/Actions/BTActionBlank.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aa4047777aec647adbcc1078ee3196bd 3 | timeCreated: 1427188040 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Utilities/BTConstants.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | public enum BTResult { 7 | Success, 8 | Failed, 9 | Running, 10 | } 11 | 12 | public enum BTAbortOpt { 13 | None, 14 | Self, 15 | LowerPriority, 16 | Both, 17 | } 18 | 19 | public enum BTClearOpt { 20 | Default, 21 | Selected, 22 | DefaultAndSelected, 23 | All, 24 | } 25 | 26 | public enum BTLogic { 27 | And, 28 | Or, 29 | } 30 | 31 | public enum BTExecuteOpt { 32 | OnTick, 33 | OnClear, 34 | Both, 35 | } 36 | 37 | public enum BTDataReadOpt { 38 | ReadAtBeginning, 39 | ReadEveryTick, 40 | } 41 | } -------------------------------------------------------------------------------- /Core/Decorator/BTInverter.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTInverter is a decorator node that inverts what its child returns. 8 | /// 9 | public class BTInverter : BTDecorator { 10 | 11 | public BTInverter (BTNode child = null) : base (child) {} 12 | 13 | public override BTResult Tick () { 14 | switch (child.Tick()) { 15 | case BTResult.Running: 16 | return BTResult.Running; 17 | case BTResult.Success: 18 | return BTResult.Failed; 19 | case BTResult.Failed: 20 | return BTResult.Success; 21 | } 22 | return BTResult.Failed; 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /Core/Actions/BTActionLog.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTActionLog is an action node that performs a Unity log. 8 | /// It returns success after logging once. 9 | /// 10 | public class BTActionLog : BTAction { 11 | 12 | private string _text; 13 | private bool _isError; 14 | 15 | public BTActionLog (string text, bool isError = false) { 16 | _text = text; 17 | _isError = isError; 18 | } 19 | 20 | protected override BTResult Execute () { 21 | if (_isError) { 22 | Debug.LogError(_text); 23 | } 24 | else { 25 | Debug.Log(_text); 26 | } 27 | 28 | return BTResult.Success; 29 | } 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /Core/Decorator/BTDecorator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTDecorator is the base class for decorator nodes. 8 | /// It has and only has one child. 9 | /// Usually it is used to alter the behavior of its child. 10 | /// 11 | public class BTDecorator : BTNode { 12 | 13 | public BTNode child {get; set;} 14 | 15 | 16 | public BTDecorator (BTNode node) { 17 | this.child = node; 18 | } 19 | 20 | public override void Activate (BTDatabase database) { 21 | base.Activate (database); 22 | 23 | child.Activate(database); 24 | } 25 | 26 | public override void Clear () { 27 | base.Clear (); 28 | child.Clear(); 29 | } 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /Core/Conditional/BTConditional.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTConditional is the base class for conditional nodes. 8 | /// It is usually used to check conditions. 9 | /// 10 | /// Concrete conditional classes inheriting from this class should override the Check method. 11 | /// 12 | public abstract class BTConditional : BTNode { 13 | 14 | sealed public override BTResult Tick () { 15 | if (Check()) { 16 | return BTResult.Success; 17 | } 18 | else { 19 | return BTResult.Failed; 20 | } 21 | } 22 | 23 | /// 24 | /// This is where the condition check happens. 25 | /// 26 | public virtual bool Check () { 27 | return false; 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /Core/Actions/BTActionCallback.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.Collections; 4 | 5 | namespace BT { 6 | 7 | public class BTActionCallback : BTAction { 8 | 9 | private Action _callback; 10 | private BTExecuteOpt _executeOpt; 11 | 12 | public BTActionCallback (Action callback, BTExecuteOpt executeOpt) { 13 | _callback = callback; 14 | _executeOpt = executeOpt; 15 | } 16 | 17 | protected override BTResult Execute () { 18 | if (_executeOpt != BTExecuteOpt.OnClear) { 19 | _callback(_database); 20 | } 21 | 22 | return BTResult.Success; 23 | } 24 | 25 | public override void Clear () { 26 | base.Clear (); 27 | 28 | if (_executeOpt != BTExecuteOpt.OnTick) { 29 | _callback(_database); 30 | } 31 | } 32 | 33 | 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /Core/Actions/BTActionWait.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTActionWait is an action node that waits for a specified time. 8 | /// It returns running during the wait, and returns success after that. 9 | /// 10 | public class BTActionWait : BTAction { 11 | private float _startTime; 12 | 13 | public float seconds {get; set;} 14 | 15 | 16 | public BTActionWait (float seconds) { 17 | this.seconds = seconds; 18 | } 19 | 20 | protected override void Enter () { 21 | base.Enter (); 22 | 23 | _startTime = Time.time; 24 | } 25 | 26 | protected override BTResult Execute () { 27 | if (Time.time - _startTime >= seconds) { 28 | return BTResult.Success; 29 | } 30 | return BTResult.Running; 31 | } 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /Core/Decorator/BTTimer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTTimer is a child node that ticks the child with interval. 8 | /// During interval, it returns running. 9 | /// If ticking the child, it returns what the child returns. 10 | /// 11 | public class BTTimer : BTDecorator { 12 | 13 | private float _timer; 14 | 15 | public float interval {get; set;} 16 | 17 | 18 | public BTTimer (float interval, BTNode child = null) : base (child) { 19 | this.interval = interval; 20 | } 21 | 22 | public override BTResult Tick () { 23 | _timer += Time.deltaTime; 24 | 25 | if (_timer >= interval) { 26 | _timer = 0; 27 | BTResult result = child.Tick(); 28 | return result; 29 | } 30 | else { 31 | return BTResult.Running; 32 | } 33 | } 34 | 35 | public override void Clear () { 36 | base.Clear (); 37 | _timer = 0; 38 | } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /Core/BTTree.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTTree is where the behavior tree should be constructed. 8 | /// 9 | public abstract class BTTree : MonoBehaviour { 10 | private BTNode _root; 11 | protected BTDatabase _database; 12 | 13 | public BTNode root {get {return _root;}} 14 | 15 | 16 | void Start () { 17 | _root = Init(); 18 | 19 | if (_root.name == null) { 20 | _root.name = "Root"; 21 | } 22 | _root.Activate(_database); 23 | } 24 | 25 | 26 | void Update () { 27 | _root.Tick(); 28 | } 29 | 30 | /// 31 | /// Init this tree by constructing the behavior tree. 32 | /// Root node should be returned. 33 | /// 34 | public virtual BTNode Init () { 35 | _database = GetComponent(); 36 | if (_database == null) { 37 | _database = gameObject.AddComponent(); 38 | } 39 | 40 | return null; 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BT Framework 2 2 | ============ 3 | BT Framework 2 is the next version of [BT Framework] (https://github.com/f15gdsy/BT-Framework), a code-centric behavior tree framework for Unity3d.
4 | It is for those who like to do most of the things in code. 5 | 6 | ### Examples 7 | TBD 8 | 9 | ### Features 10 | 1. Code-centric and minimum dependencies on Unity3d, so it won't pollute your inspector. 11 | 2. Familiar APIs for you to build customized action nodes & conditional nodes. If you come from a state machine background, you should find it familiar. 12 | 3. More stable than previous version as important logic nodes are covered by unit tests; 13 | 4. A GUI tool for visulize the behavior tree built, which helps debuging process. 14 | 15 | ### How to Use It 16 | TBD 17 | 18 | ### Tips & Tricks 19 | TBD 20 | 21 | ### Unit Tests 22 | TBD 23 | 24 | ### TODO 25 | 1. Improve the visulization tool; 26 | 2. Choose an approach for better subtree reuse, factory pattern is what I'm considering right now; 27 | -------------------------------------------------------------------------------- /Core/Conditional/BTCompareData.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTCompareData is a conditional inheriting from BTConditional. 8 | /// 9 | /// It performs comparison between the provided data with what's found in BTDatabase. 10 | /// It returns true if they are equal, false otherwise. 11 | /// 12 | public class BTCompareData : BTConditional { 13 | private string _readDataName; 14 | private int _readDataId; 15 | private T _rhs; 16 | 17 | 18 | public BTCompareData (string readDataName, T rhs) { 19 | _readDataName = readDataName; 20 | _rhs = rhs; 21 | } 22 | 23 | public override void Activate (BTDatabase database) { 24 | base.Activate (database); 25 | 26 | _readDataId = database.GetDataId(_readDataName); 27 | } 28 | 29 | public override bool Check () { 30 | if (_rhs == null) { 31 | return _database.CheckDataNull(_readDataId); 32 | } 33 | return _database.GetData(_readDataId).Equals(_rhs); 34 | } 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /Core/Actions/BTActionWaitRandom.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTActionWaitRandom is almost the same as BTActionWait, 8 | /// except every time it is about to execute, the waiting time is randomly generated in a specified range. 9 | /// 10 | public class BTActionWaitRandom : BTAction { 11 | private float _startTime; 12 | private float _interval; 13 | 14 | public float secondsMin {get; set;} 15 | public float secondsMax {get; set;} 16 | 17 | public BTActionWaitRandom (float secondsMin, float secondsMax) { 18 | this.secondsMin = secondsMin; 19 | this.secondsMax = secondsMax; 20 | } 21 | 22 | protected override void Enter () { 23 | base.Enter (); 24 | 25 | _startTime = Time.time; 26 | _interval = Random.Range(secondsMin, secondsMax); 27 | } 28 | 29 | protected override BTResult Execute () { 30 | if (Time.time - _startTime >= _interval) { 31 | return BTResult.Success; 32 | } 33 | return BTResult.Running; 34 | } 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /Ex/Actions/BTActionPlayAnimation.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT.Ex { 5 | 6 | public class BTActionPlayAnimation : BTAction { 7 | private Animator _animator; 8 | private int _stateHash; 9 | private bool _justEntered; 10 | 11 | 12 | 13 | public BTActionPlayAnimation (Animator animator, string stateName, string layerName = "Base Layer") { 14 | _animator = animator; 15 | _stateHash = Animator.StringToHash(layerName + "." + stateName); 16 | } 17 | 18 | protected override void Enter () { 19 | base.Enter (); 20 | 21 | _animator.Play(_stateHash); 22 | _justEntered = true; 23 | } 24 | 25 | protected override BTResult Execute () { 26 | // If an animation is not loop, and to make it return success after one play, 27 | // you needs to set the transition in animator controller. 28 | if (_justEntered || _animator.GetCurrentAnimatorStateInfo(0).fullPathHash == _stateHash) { 29 | if (_justEntered) { 30 | _justEntered = false; 31 | } 32 | 33 | return BTResult.Running; 34 | } 35 | return BTResult.Success; 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yifeng 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 | -------------------------------------------------------------------------------- /Core/Actions/BTActionResetData.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTActionResetData is an action node that reset a data in BTDatabase. 8 | /// It provides three options: 9 | /// - OnTick: reset the data on tick; 10 | /// - OnClear: reset the data on clear; 11 | /// - Both: reset the data on tick and on clear. 12 | /// It is useful with the use of different decorators. 13 | /// 14 | public class BTActionResetData : BTAction { 15 | private string _setDataName; 16 | private int _setDataId; 17 | private T _rhs; 18 | private BTExecuteOpt _resetOpt; 19 | 20 | 21 | public BTActionResetData (string setDataName, T rhs, BTExecuteOpt resetOpt) { 22 | _setDataName = setDataName; 23 | _rhs = rhs; 24 | _resetOpt = resetOpt; 25 | } 26 | 27 | public override void Activate (BTDatabase database) { 28 | base.Activate (database); 29 | 30 | _setDataId = _database.GetDataId(_setDataName); 31 | } 32 | 33 | protected override BTResult Execute () { 34 | if (_resetOpt != BTExecuteOpt.OnClear) { 35 | _database.SetData(_setDataId, _rhs); 36 | } 37 | return BTResult.Success; 38 | } 39 | 40 | public override void Clear () { 41 | base.Clear (); 42 | 43 | if (_resetOpt != BTExecuteOpt.OnTick) { 44 | _database.SetData(_setDataId, _rhs); 45 | } 46 | } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /Core/BTNode.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | 6 | namespace BT { 7 | 8 | /// 9 | /// BT node is the base class for any other nodes in BT framework. 10 | /// Usually, you don't need to override this node to utilize the framework, unless you know what you are doing. 11 | /// 12 | public abstract class BTNode { 13 | 14 | protected BTDatabase _database; 15 | 16 | public string name {get; set;} 17 | public string details {get; set;} 18 | public bool isRunning {get; set;} 19 | public BTNode clearTick {get; set;} 20 | 21 | /// 22 | /// Activate the node, which is called on Start of a BTTree. 23 | /// Can be seen as initialization method. 24 | /// 25 | /// Database. 26 | public virtual void Activate (BTDatabase database) { 27 | _database = database; 28 | 29 | if (clearTick != null) { 30 | clearTick.Activate(database); 31 | } 32 | } 33 | 34 | /// 35 | /// Tick is where logics happens every frame (not all nodes will be ticked in a frame). 36 | /// 37 | public virtual BTResult Tick () {return BTResult.Failed;} 38 | 39 | /// 40 | /// Clear the node. 41 | /// 42 | public virtual void Clear () { 43 | isRunning = false; 44 | 45 | if (clearTick != null) { 46 | clearTick.Tick(); 47 | } 48 | } 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /Core/Actions/BTAction.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTAction is the base class for action nodes. 8 | /// It is where the actual gameplay logic happens. 9 | /// 10 | public class BTAction : BTNode { 11 | 12 | private BTActionStatus _status = BTActionStatus.Ready; 13 | 14 | 15 | sealed public override BTResult Tick () { 16 | BTResult tickResult = BTResult.Success; 17 | 18 | if (_status == BTActionStatus.Ready) { 19 | Enter(); 20 | _status = BTActionStatus.Running; 21 | } 22 | if (_status == BTActionStatus.Running) { 23 | tickResult = Execute(); 24 | if (tickResult != BTResult.Running) { 25 | Exit(); 26 | _status = BTActionStatus.Ready; 27 | isRunning = false; 28 | } 29 | else { 30 | isRunning = true; 31 | } 32 | } 33 | return tickResult; 34 | } 35 | 36 | public override void Clear () { 37 | base.Clear(); 38 | 39 | if (_status != BTActionStatus.Ready) { // not cleared yet 40 | Exit(); 41 | _status = BTActionStatus.Ready; 42 | } 43 | } 44 | 45 | /// 46 | /// Called when the action node is about to execute. 47 | /// 48 | protected virtual void Enter () {} 49 | 50 | /// 51 | /// Called every frame if the action node is active. 52 | /// 53 | protected virtual BTResult Execute () {return BTResult.Failed;} 54 | 55 | /// 56 | /// Called when the action node finishes. 57 | /// 58 | protected virtual void Exit () {} 59 | 60 | 61 | 62 | private enum BTActionStatus { 63 | Ready, 64 | Running, 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /Ex/Conditionals/BTCheckWithinDistance.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT.Ex { 5 | 6 | public class BTCheckWithinDistance : BTConditional { 7 | private string _readDataName; 8 | private int _readDataId; 9 | private DataOpt _dataOpt; 10 | private float _distance; 11 | private Transform _targetTrans; 12 | private Transform _trans; 13 | 14 | 15 | public BTCheckWithinDistance (Transform trans, float distance, string readDataName, DataOpt dataOpt) { 16 | _trans = trans; 17 | _distance = distance; 18 | _readDataName = readDataName; 19 | _dataOpt = dataOpt; 20 | } 21 | 22 | public BTCheckWithinDistance (Transform trans, float distance, Transform targetTrans) : this ( trans, distance, null, DataOpt.ProvidedTrans) { 23 | _targetTrans = targetTrans; 24 | } 25 | 26 | public override void Activate (BTDatabase database) { 27 | base.Activate (database); 28 | 29 | if (_dataOpt != DataOpt.ProvidedTrans) { 30 | _readDataId = _database.GetDataId(_readDataName); 31 | } 32 | } 33 | 34 | public override bool Check () { 35 | Vector3 dataPosition = Vector3.zero; 36 | 37 | switch (_dataOpt) { 38 | case DataOpt.Vec3: 39 | dataPosition = _database.GetData(_readDataId); 40 | break; 41 | case DataOpt.Trans: 42 | Transform trans = _database.GetData(_readDataId); 43 | dataPosition = trans.position; 44 | break; 45 | case DataOpt.ProvidedTrans: 46 | dataPosition = _targetTrans.position; 47 | break; 48 | } 49 | 50 | Vector3 offset = dataPosition - _trans.position; 51 | return offset.sqrMagnitude <= _distance * _distance; 52 | } 53 | 54 | public enum DataOpt { 55 | Vec3, 56 | Trans, 57 | ProvidedTrans, 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Core/Composites/BTComposite.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace BT { 6 | 7 | /// 8 | /// BTComposite is the base class for composite nodes. 9 | /// 10 | public class BTComposite : BTNode { 11 | 12 | private List _children; 13 | private List _selectedChildrenForClear; // Used for BTClearOpt.Selected & BTClearOpt.DefaultAndSelected 14 | 15 | /// 16 | /// There are different options for Clear method. 17 | /// The actually implementation depends on different types of composite nodes. 18 | /// 19 | public BTClearOpt clearOpt {get; set;} 20 | // public BTAbortOpt abortOpt {get; set;} 21 | public List children { 22 | get { 23 | if (_children == null) { 24 | _children = new List(); 25 | } 26 | return _children; 27 | } 28 | } 29 | protected List selectedChildrenForClear { 30 | get { 31 | if (_selectedChildrenForClear == null) { 32 | _selectedChildrenForClear = new List(); 33 | } 34 | return _selectedChildrenForClear; 35 | } 36 | } 37 | 38 | 39 | public override void Activate (BTDatabase database) { 40 | base.Activate (database); 41 | 42 | foreach (BTNode child in children) { 43 | child.Activate(database); 44 | } 45 | } 46 | 47 | public void AddChild (BTNode node) { 48 | if (node != null) { 49 | children.Add(node); 50 | } 51 | } 52 | 53 | public void AddChild (BTNode node, bool selectForClear) { 54 | AddChild(node); 55 | if (selectForClear) { 56 | selectedChildrenForClear.Add(node); 57 | } 58 | } 59 | 60 | public void RemoveChild (BTNode node) { 61 | children.Remove(node); 62 | selectedChildrenForClear.Remove(node); 63 | } 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /Core/Decorator/BTRepeater.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTRepeater is a decorator node that keeps the child tick for a number of times, or forever. 8 | /// 9 | public class BTRepeater : BTDecorator { 10 | /// 11 | /// How many times can the child tick. 12 | /// 13 | public int count {get; set;} 14 | 15 | /// 16 | /// Tick the child forever. 17 | /// 18 | public bool repeatForever {get; set;} 19 | 20 | /// 21 | /// Should end the repetition if the child return failure. 22 | /// 23 | public bool endOnFailure {get; set;} 24 | 25 | private int _currentCount; 26 | 27 | 28 | private BTRepeater (int count, bool repeatForever, bool endOnFailure, BTNode child = null) : base (child) { 29 | this.count = count; 30 | this.repeatForever = repeatForever; 31 | this.endOnFailure = endOnFailure; 32 | } 33 | 34 | public BTRepeater (int count, bool endOnFailure, BTNode child = null) : this (count, false, endOnFailure, child) {} 35 | public BTRepeater (bool endOnFailure, BTNode child = null) : this (0, true, endOnFailure, child) {} 36 | 37 | public override BTResult Tick () { 38 | if (repeatForever) { 39 | BTResult result = child.Tick(); 40 | 41 | if (endOnFailure && result == BTResult.Failed) { 42 | return BTResult.Failed; 43 | } 44 | 45 | isRunning = true; 46 | return BTResult.Running; 47 | } 48 | else if (_currentCount < count) { 49 | BTResult result = child.Tick(); 50 | _currentCount++; 51 | 52 | if (_currentCount >= count) { 53 | if (result == BTResult.Running) { 54 | result = BTResult.Success; 55 | } 56 | return result; 57 | } 58 | 59 | if (endOnFailure && result == BTResult.Failed) { 60 | return BTResult.Failed; 61 | } 62 | isRunning = true; 63 | return BTResult.Running; 64 | } 65 | 66 | return BTResult.Failed; 67 | } 68 | 69 | public override void Clear () { 70 | base.Clear (); 71 | 72 | _currentCount = 0; 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /Core/BTDatabase.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | 6 | namespace BT { 7 | 8 | /// 9 | /// BTDatabase is the blackboard in a classic blackboard system. 10 | /// (I found the name "blackboard" a bit hard to understand so I call it database ;p) 11 | /// 12 | /// It is the place to store data from local nodes, cross-tree nodes, and even other scripts. 13 | 14 | /// Nodes can read the data inside a database by the use of a string, or an int id of the data. 15 | /// The latter one is prefered for efficiency's sake. 16 | /// 17 | public class BTDatabase : MonoBehaviour { 18 | 19 | // _database & _dataNames are 1 to 1 relationship 20 | private List _dataList = new List(); 21 | private List _dataNames = new List(); 22 | 23 | 24 | // Should use dataId as parameter to get data instead of this 25 | public T GetData (string dataName) { 26 | int dataId = IndexOfDataId(dataName); 27 | if (dataId == -1) Debug.LogError("BTDatabase: Data for " + dataName + " does not exist!"); 28 | 29 | return (T) _dataList[dataId]; 30 | } 31 | 32 | // Should use this function to get data! 33 | public T GetData (int dataId) { 34 | if (BT.BTConfiguration.ENABLE_DATABASE_LOG) { 35 | Debug.Log("BTDatabase: getting data for " + _dataNames[dataId]); 36 | } 37 | return (T) _dataList[dataId]; 38 | } 39 | 40 | public void SetData (string dataName, T data) { 41 | int dataId = GetDataId(dataName); 42 | _dataList[dataId] = (object) data; 43 | } 44 | 45 | public void SetData (int dataId, T data) { 46 | _dataList[dataId] = (object) data; 47 | } 48 | 49 | public bool CheckDataNull (string dataName) { 50 | int dataId = IndexOfDataId(dataName); 51 | if (dataId == -1) return true; 52 | 53 | return CheckDataNull(dataId); 54 | } 55 | 56 | public bool CheckDataNull (int dataId) { 57 | // Despite == test, Equal test helps the case that the reference is Monobahvior and is destroyed. 58 | return _dataList[dataId] == null || _dataList[dataId].Equals(null); 59 | } 60 | 61 | public int GetDataId (string dataName) { 62 | int dataId = IndexOfDataId(dataName); 63 | if (dataId == -1) { 64 | _dataNames.Add(dataName); 65 | _dataList.Add(null); 66 | dataId = _dataNames.Count - 1; 67 | } 68 | 69 | return dataId; 70 | } 71 | 72 | private int IndexOfDataId (string dataName) { 73 | for (int i=0; i<_dataNames.Count; i++) { 74 | if (_dataNames[i].Equals(dataName)) return i; 75 | } 76 | 77 | return -1; 78 | } 79 | 80 | public bool ContainsData (string dataName) { 81 | return IndexOfDataId(dataName) != -1; 82 | } 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /Ex/Actions/BTActionMove.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT.Ex { 5 | 6 | public class BTActionMove : BTAction { 7 | 8 | private string _readDataName; 9 | private int _readDataId; 10 | private float _speed; 11 | private float _tolerance; 12 | private DataOpt _dataOpt; 13 | private UsageOpt _usageOpt; 14 | private BTDataReadOpt _dataReadOpt; 15 | private Vector3 _vec3Data; 16 | private Transform _targetTrans; 17 | private Transform _trans; 18 | 19 | public BTActionMove (Transform trans, float speed, float tolerance, string readDataName, DataOpt dataOpt, UsageOpt usageOpt, BTDataReadOpt dataReadOpt) { 20 | _trans = trans; 21 | _speed = speed; 22 | _tolerance = tolerance; 23 | _readDataName = readDataName; 24 | _dataOpt = dataOpt; 25 | _usageOpt = usageOpt; 26 | _dataReadOpt = dataReadOpt; 27 | } 28 | 29 | public BTActionMove (Transform trans, float speed, float tolerance, Transform targetTrans, BTDataReadOpt dataReadOpt) : 30 | this (trans, speed, tolerance, null, DataOpt.ProvidedTrans, UsageOpt.Position, dataReadOpt) { 31 | _targetTrans = targetTrans; 32 | } 33 | 34 | public override void Activate (BTDatabase database) { 35 | base.Activate (database); 36 | 37 | if (_dataOpt != DataOpt.ProvidedTrans) { 38 | _readDataId = _database.GetDataId(_readDataName); 39 | } 40 | } 41 | 42 | protected override void Enter () { 43 | base.Enter (); 44 | 45 | if (_dataReadOpt == BTDataReadOpt.ReadAtBeginning) { 46 | ReadVec3Data(); 47 | } 48 | } 49 | 50 | protected override BTResult Execute () { 51 | if (_dataReadOpt == BTDataReadOpt.ReadEveryTick) { 52 | ReadVec3Data(); 53 | } 54 | 55 | Vector3 direction = Vector3.zero; 56 | 57 | switch (_usageOpt) { 58 | case UsageOpt.Direction: 59 | direction = _vec3Data; 60 | break; 61 | case UsageOpt.Position: 62 | direction = _vec3Data - _trans.position; 63 | break; 64 | } 65 | 66 | if (direction.sqrMagnitude <= _tolerance * _tolerance) { 67 | return BTResult.Success; 68 | } 69 | else { 70 | Vector3 position = _trans.position; 71 | position += direction.normalized * _speed * Time.deltaTime; 72 | _trans.position = position; 73 | } 74 | return BTResult.Running; 75 | } 76 | 77 | private void ReadVec3Data () { 78 | _vec3Data = Vector3.zero; 79 | 80 | switch (_dataOpt) { 81 | case DataOpt.Vec3: 82 | _vec3Data = _database.GetData(_readDataId); 83 | break; 84 | case DataOpt.Trans: 85 | _vec3Data = _database.GetData(_readDataId).position; 86 | break; 87 | case DataOpt.ProvidedTrans: 88 | _vec3Data = _targetTrans.position; 89 | break; 90 | } 91 | } 92 | 93 | public enum DataOpt { 94 | Vec3, 95 | Trans, 96 | ProvidedTrans, 97 | } 98 | 99 | public enum UsageOpt { 100 | Position, 101 | Direction, 102 | } 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /Core/Composites/BTSequence.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace BT { 5 | 6 | /// 7 | /// BTSequence is a composite node that ticks children from left to right. 8 | /// 9 | /// It will return failure as soon as one of its child node return failure. 10 | /// If a child node returns success then it will sequentially run the next task. 11 | /// If all child nodes return success then it will return success. 12 | /// If a child returns running it will return running. 13 | /// 14 | /// Default clear option is to clear the current active child. 15 | /// 16 | public class BTSequence : BTComposite { 17 | 18 | private int _activeChildIndex; 19 | 20 | 21 | public int activeChildIndex {get {return _activeChildIndex;}} 22 | 23 | 24 | public override BTResult Tick () { 25 | return TickFromActiveChild(); 26 | } 27 | 28 | public override void Clear () { 29 | base.Clear(); 30 | 31 | switch (clearOpt) { 32 | case BTClearOpt.Default: 33 | if (_activeChildIndex != -1) { 34 | children[_activeChildIndex].Clear(); 35 | } 36 | break; 37 | 38 | case BTClearOpt.Selected: 39 | foreach (BTNode child in selectedChildrenForClear) { 40 | int index = children.IndexOf(child); 41 | // greater than active child index means they are not cleared yet 42 | // also if _activeChildIndex is -1, then all selected children will be cleared 43 | if (index >= _activeChildIndex) { 44 | child.Clear(); 45 | } 46 | } 47 | break; 48 | 49 | case BTClearOpt.DefaultAndSelected: 50 | if (_activeChildIndex != -1) { 51 | BTNode activeChild = children[_activeChildIndex]; 52 | if (!selectedChildrenForClear.Contains(activeChild)) { 53 | activeChild.Clear(); 54 | } 55 | } 56 | foreach (BTNode child in selectedChildrenForClear) { 57 | int index = children.IndexOf(child); 58 | if (index >= _activeChildIndex) { 59 | child.Clear(); 60 | } 61 | } 62 | break; 63 | 64 | case BTClearOpt.All: 65 | if (_activeChildIndex > -1) { 66 | for (int i=_activeChildIndex; i 7 | /// BTSelector is a composite that: 8 | /// 9 | /// It will return success as soon as one of its child tasks return success. 10 | /// If a child task returns failure then it will sequentially run the next task. 11 | /// If no child task returns success then it will return failure. 12 | /// If a child node return running it will return running. 13 | /// 14 | /// Default clear option is to clear the current active child. 15 | /// 16 | public class BTSelector : BTComposite { 17 | 18 | private int _activeChildIndex = -1; 19 | private int _previousSuccessChildIndex = -1; 20 | 21 | 22 | public int activeChildIndex {get {return _activeChildIndex;}} 23 | 24 | 25 | public override BTResult Tick () { 26 | for (int i=0; i _previousSuccessChildIndex) { 75 | child.Clear(); 76 | } 77 | } 78 | break; 79 | 80 | case BTClearOpt.DefaultAndSelected: 81 | if (_activeChildIndex != -1) { 82 | BTNode activeChild = children[_activeChildIndex]; 83 | if (!selectedChildrenForClear.Contains(activeChild)) { 84 | activeChild.Clear(); 85 | } 86 | } 87 | int split = Mathf.Max(_activeChildIndex, _previousSuccessChildIndex); 88 | foreach (BTNode child in selectedChildrenForClear) { 89 | int index = children.IndexOf(child); 90 | if (index > split) { 91 | child.Clear(); 92 | } 93 | } 94 | break; 95 | 96 | case BTClearOpt.All: 97 | split = Mathf.Max(_activeChildIndex-1, _previousSuccessChildIndex); 98 | foreach (BTNode child in children) { 99 | int index = children.IndexOf(child); 100 | if (index > split) { 101 | child.Clear(); 102 | } 103 | } 104 | break; 105 | } 106 | 107 | _activeChildIndex = -1; 108 | _previousSuccessChildIndex = -1; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /Core/Composites/BTSimpleParallel.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace BT { 6 | 7 | /// 8 | /// BTSimpleParallel is a composite node that has a primary child and other children (background). 9 | /// 10 | /// If primary child returns running, the background children nodes will run, and it returns running. 11 | /// If primary child returns failure or success, the background children will not run and it returns failure or success accordingly. 12 | /// 13 | /// Default clear option is to clear the primary child & any running background children. 14 | /// 15 | public class BTSimpleParallel : BTComposite { 16 | 17 | private BTNode _primaryChild; 18 | private List _runningChildren; 19 | private bool _shouldClearPrimaryChild = true; 20 | 21 | public BTNode primaryChild {get {return _primaryChild;}} 22 | 23 | 24 | public void SetPrimaryChild (BTNode node, bool selectForClear = false) { 25 | if (_primaryChild != null) { 26 | selectedChildrenForClear.Remove(_primaryChild); 27 | } 28 | 29 | _primaryChild = node; 30 | if (selectForClear) { 31 | selectedChildrenForClear.Add(_primaryChild); 32 | } 33 | } 34 | 35 | public override void Activate (BTDatabase database) { 36 | base.Activate (database); 37 | 38 | _primaryChild.Activate(database); 39 | ResetRuningChildren(); 40 | } 41 | 42 | public override BTResult Tick () { 43 | if (_primaryChild == null) { 44 | Debug.LogError("Primary Child not set!"); 45 | } 46 | 47 | BTResult primaryChildResult = _primaryChild.Tick(); 48 | 49 | if (primaryChildResult == BTResult.Running) { 50 | RunBackground(); 51 | isRunning = true; 52 | return BTResult.Running; 53 | } 54 | else { 55 | _shouldClearPrimaryChild = false; 56 | _primaryChild.Clear(); 57 | isRunning = false; 58 | return primaryChildResult; 59 | } 60 | } 61 | 62 | public override void Clear () { 63 | base.Clear (); 64 | 65 | switch (clearOpt) { 66 | case BTClearOpt.Default: 67 | case BTClearOpt.DefaultAndSelected: 68 | case BTClearOpt.All: 69 | if (_shouldClearPrimaryChild) { 70 | _primaryChild.Clear(); 71 | } 72 | foreach (BTNode child in _runningChildren) { 73 | child.Clear(); 74 | } 75 | break; 76 | 77 | case BTClearOpt.Selected: 78 | foreach (BTNode child in selectedChildrenForClear) { 79 | if ((_shouldClearPrimaryChild && child == _primaryChild) || _runningChildren.Contains(child)) { 80 | child.Clear(); 81 | } 82 | } 83 | break; 84 | } 85 | 86 | _shouldClearPrimaryChild = true; 87 | ResetRuningChildren(); 88 | } 89 | 90 | private void ResetRuningChildren () { 91 | _runningChildren = new List(); 92 | foreach (BTNode child in children) { 93 | _runningChildren.Add(child); 94 | } 95 | } 96 | 97 | private void RunBackground () { 98 | for (int i=_runningChildren.Count-1; i>=0; i--) { 99 | BTNode child = _runningChildren[i]; 100 | BTResult result = child.Tick(); 101 | 102 | if (result != BTResult.Running) { 103 | child.Clear(); 104 | _runningChildren.RemoveAt(i); 105 | } 106 | } 107 | } 108 | 109 | 110 | // private FinishOpt _finishOpt; 111 | 112 | // public enum FinishOpt { 113 | // AbortBackground, 114 | // WaitForBackground, 115 | // } 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /Core/Decorator/BTConditionEvaluator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace BT { 6 | 7 | /// 8 | /// BTConditionEvaluator is a decorator node. 9 | /// It performs conditional check to determine if the child should be Tick. 10 | /// 11 | /// It allows the child to tick if the conditional check return true, and returns what the child returns. 12 | /// 13 | /// It has two clear options 14 | /// - OnAbortRunning: if Clear is called, it will only clear its child, if it was running. 15 | /// - OnNotRunning: if Clear is called, it will clear its child no matter what. 16 | /// 17 | public class BTConditionEvaluator : BTDecorator { 18 | 19 | private List _conditionals; 20 | private List _conditionalInverts; 21 | public BTLogic logicOpt; 22 | public bool reevaludateEveryTick; 23 | public ClearChildOpt clearOpt; 24 | 25 | private BTResult _previousResult = BTResult.Failed; 26 | 27 | 28 | 29 | public BTConditionEvaluator (List conditionals, BTLogic logicOpt, bool reevaluateEveryTick, ClearChildOpt clearOpt, BTNode child = null) : base (child) { 30 | this._conditionals = conditionals; 31 | this.logicOpt = logicOpt; 32 | this.reevaludateEveryTick = reevaluateEveryTick; 33 | this.clearOpt = clearOpt; 34 | } 35 | 36 | public BTConditionEvaluator (BTLogic logicOpt, bool reevaluateEveryTick, ClearChildOpt clearOpt, BTNode child = null) : base (child) { 37 | this._conditionals = new List(); 38 | this._conditionalInverts = new List(); 39 | this.logicOpt = logicOpt; 40 | this.reevaludateEveryTick = reevaluateEveryTick; 41 | this.clearOpt = clearOpt; 42 | } 43 | 44 | public override void Activate (BTDatabase database) { 45 | base.Activate (database); 46 | 47 | foreach (BTConditional conditional in _conditionals) { 48 | conditional.Activate(database); 49 | } 50 | } 51 | 52 | public override BTResult Tick () { 53 | if (_previousResult != BTResult.Running || reevaludateEveryTick) { 54 | switch (logicOpt) { 55 | case BTLogic.And: 56 | int i = 0; 57 | foreach (BTConditional conditional in _conditionals) { 58 | bool invert = _conditionalInverts[i++]; 59 | if ((invert && conditional.Check()) || 60 | (!invert && !conditional.Check())) { 61 | return BTResult.Failed; 62 | } 63 | } 64 | break; 65 | 66 | case BTLogic.Or: 67 | bool anySuccess = false; 68 | i = 0; 69 | foreach (BTConditional conditional in _conditionals) { 70 | bool invert = _conditionalInverts[i++]; 71 | if ((invert && !conditional.Check()) || 72 | (!invert && conditional.Check())) { 73 | anySuccess = true; 74 | break; 75 | } 76 | } 77 | if (!anySuccess) { 78 | return BTResult.Failed; 79 | } 80 | break; 81 | } 82 | } 83 | 84 | _previousResult = child.Tick(); 85 | if (_previousResult == BTResult.Running) { 86 | isRunning = true; 87 | } 88 | 89 | return _previousResult; 90 | } 91 | 92 | public override void Clear () { 93 | if ((isRunning && clearOpt == ClearChildOpt.OnAbortRunning) || 94 | (_previousResult == BTResult.Success && clearOpt == ClearChildOpt.OnStopRunning) || 95 | clearOpt == ClearChildOpt.OnNotRunning) { 96 | isRunning = false; 97 | child.Clear(); 98 | } 99 | 100 | if (clearTick != null) { 101 | clearTick.Tick(); 102 | } 103 | _previousResult = BTResult.Failed; 104 | } 105 | 106 | public void AddConditional (BTConditional conditional, bool invertResult = false) { 107 | if (!_conditionals.Contains(conditional)) { 108 | _conditionals.Add(conditional); 109 | _conditionalInverts.Add(invertResult); 110 | } 111 | } 112 | 113 | public void RemoveConditional (BTConditional conditional) { 114 | int index = _conditionals.IndexOf(conditional); 115 | _conditionals.Remove(conditional); 116 | _conditionalInverts.RemoveAt(index); 117 | } 118 | 119 | 120 | public enum ClearChildOpt { 121 | OnAbortRunning, 122 | OnStopRunning, 123 | OnNotRunning, 124 | } 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /Ex/Editor/BTTreeWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.Collections.Generic; 4 | using BT; 5 | 6 | public class BTTreeWindow: EditorWindow { 7 | 8 | private GameObject _go; 9 | 10 | private BTTree _previousTree; 11 | private BTNodeInfo _info; 12 | private Vector2 _size = new Vector2(200, 50); 13 | private Vector2 _offset = new Vector2(250, 100); 14 | private int _currentWindowId; 15 | private Dictionary _levelToCount; 16 | 17 | private Vector2 _scrollPosition = Vector2.zero; 18 | 19 | 20 | [MenuItem("Window/BTTree")] 21 | static void ShowEditor() { 22 | EditorWindow.GetWindow(); 23 | } 24 | 25 | void OnInspectorUpdate () { 26 | Repaint(); 27 | } 28 | 29 | void OnEnable () { 30 | _previousTree = null; 31 | } 32 | 33 | void OnGUI() { 34 | _go = (GameObject) EditorGUI.ObjectField(new Rect(position.xMax - 350, position.yMin-40, 300, 20), "BTTree GameObject", _go, typeof(GameObject), true); 35 | 36 | BTTree tree; 37 | if (_go == null || (tree = _go.GetComponent()) == null) { 38 | EditorGUI.LabelField(new Rect(position.xMax - 350, position.y-17, 500, 20), "Find a GameObject with component inherited from BTTree."); 39 | return ; 40 | } 41 | 42 | if (_previousTree == null || _previousTree.GetType() != tree.GetType()) { 43 | BTNode root = tree.root == null ? tree.Init() : tree.root; 44 | 45 | _levelToCount = new Dictionary(); 46 | _info = ParseNodeInfo(root, 0, 0); 47 | 48 | _previousTree = tree; 49 | } 50 | 51 | int maxCount = 0; 52 | foreach (int count in _levelToCount.Values) { 53 | if (count > maxCount) { 54 | maxCount = count; 55 | } 56 | } 57 | 58 | _scrollPosition = GUI.BeginScrollView(new Rect(0, 0, position.width - 1, position.height - 1), 59 | _scrollPosition, 60 | new Rect(0, 0, maxCount * _offset.x + 50, _levelToCount.Keys.Count * _offset.y + 50)); 61 | 62 | BeginWindows(); 63 | 64 | _currentWindowId = 0; 65 | DrawNodeInfo(_info, null); 66 | 67 | EndWindows(); 68 | 69 | GUI.EndScrollView(); 70 | } 71 | 72 | private BTNodeInfo ParseNodeInfo (BTNode node, int level, int indexInParent) { 73 | int maxNodeSize = 1; 74 | List infos = new List(); 75 | 76 | if (node is BTSimpleParallel) { // simple parallel has a primary child 77 | BTSimpleParallel simpleParallel = (BTSimpleParallel) node; 78 | List children = simpleParallel.children; 79 | children.Insert(0, simpleParallel.primaryChild); 80 | 81 | if (children.Count > 0) { 82 | maxNodeSize = 0; 83 | } 84 | 85 | int i=0; 86 | foreach (BTNode child in children) { 87 | BTNodeInfo info = ParseNodeInfo(child, level+1, i++); 88 | maxNodeSize += info.maxNodeSize; 89 | infos.Add(info); 90 | } 91 | } 92 | else if (node is BTComposite) { 93 | BTComposite composite = (BTComposite) node; 94 | List children = composite.children; 95 | 96 | if (children.Count > 0) { 97 | maxNodeSize = 0; 98 | } 99 | 100 | int i=0; 101 | foreach (BTNode child in children) { 102 | BTNodeInfo info = ParseNodeInfo(child, level+1, i++); 103 | maxNodeSize += info.maxNodeSize; 104 | infos.Add(info); 105 | } 106 | } 107 | else if (node is BTDecorator) { 108 | BTDecorator decorator = (BTDecorator) node; 109 | 110 | if (decorator.child != null) { 111 | BTNodeInfo info = ParseNodeInfo(decorator.child, level+1, 0); 112 | if (info.maxNodeSize > maxNodeSize) { 113 | maxNodeSize = info.maxNodeSize; 114 | } 115 | infos.Add(info); 116 | } 117 | } 118 | 119 | int countInLevel; 120 | _levelToCount.TryGetValue(level, out countInLevel); 121 | 122 | _levelToCount[level] = countInLevel + maxNodeSize; 123 | 124 | return new BTNodeInfo(node, infos, maxNodeSize, level, countInLevel, indexInParent); 125 | } 126 | 127 | private Rect DrawNodeInfo (BTNodeInfo info, BTNodeInfo parentInfo) { 128 | float selfX = info.indexInParent * _offset.x; 129 | float parentX = 0; 130 | 131 | if (info.indexInParent > 0) { 132 | selfX += (parentInfo.childrenInfo[info.indexInParent - 1].maxNodeSize - 1) * _offset.x; 133 | } 134 | if (parentInfo != null) { 135 | parentX = parentInfo.positionX; 136 | } 137 | else { 138 | selfX += 50; 139 | } 140 | 141 | Rect rect = new Rect(selfX + parentX, 142 | info.level * _offset.y, 143 | _size.x, 144 | _size.y); 145 | 146 | info.positionX = selfX + parentX; 147 | 148 | foreach (BTNodeInfo childInfo in info.childrenInfo) { 149 | Color color = Color.white; 150 | if (childInfo.node.isRunning) { 151 | color = Color.green; 152 | } 153 | Rect childRect = DrawNodeInfo(childInfo, info); 154 | DrawPolygonLine(rect, childRect, color); 155 | } 156 | 157 | string name = info.node.name != null ? info.node.name : info.node.GetType().ToString(); 158 | string[] nameParts = name.Split('.'); 159 | 160 | GUI.Window(_currentWindowId++, rect, DoWindow, nameParts[nameParts.Length-1]); 161 | 162 | return rect; 163 | } 164 | 165 | 166 | private static void DrawPolygonLine (Rect rect1, Rect rect2, Color color) { 167 | float midY = Mathf.Min(rect1.center.y, rect2.center.y) + Mathf.Abs(rect1.center.y - rect2.center.y) / 2; 168 | Vector3 rect1Point = new Vector3(rect1.center.x, rect1.center.y); 169 | Vector3 midRect1Point = new Vector3(rect1.center.x, midY); 170 | Vector3 midRect2Point = new Vector3(rect2.center.x, midY); 171 | Vector3 rect2Point = new Vector3(rect2.center.x, rect2.center.y); 172 | 173 | Handles.color = color; 174 | 175 | Handles.DrawPolyLine(new Vector3[] { 176 | rect1Point, 177 | midRect1Point, 178 | midRect2Point, 179 | rect2Point 180 | }); 181 | } 182 | 183 | private void DoWindow (int id) { 184 | 185 | } 186 | 187 | 188 | public class BTNodeInfo { 189 | public BTNode node; 190 | public List childrenInfo; 191 | public int maxNodeSize; 192 | public int level; 193 | public int countInLevel; 194 | public float positionX; 195 | public int indexInParent; 196 | 197 | public BTNodeInfo (BTNode node, List childrenInfo, int maxNodeSize, int level, int countInLevel, int indexInParent) { 198 | this.node = node; 199 | this.childrenInfo = childrenInfo; 200 | this.maxNodeSize = maxNodeSize; 201 | this.level = level; 202 | this.countInLevel = countInLevel; 203 | this.indexInParent = indexInParent; 204 | } 205 | } 206 | 207 | 208 | private enum NodeType { 209 | Invalid, 210 | 211 | Sequence, 212 | Selector, 213 | SimpleParallel, 214 | 215 | Inverter, 216 | Repeater, 217 | ConditionalEvaluator, 218 | Timer, 219 | 220 | Conditional, 221 | 222 | Action, 223 | } 224 | } --------------------------------------------------------------------------------