├── .github └── workflows │ └── maven.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.MD ├── pom.xml └── src ├── main └── java │ └── com │ └── github │ └── silencesu │ └── behavior3java │ ├── B3Loader.java │ ├── actions │ ├── Error.java │ ├── Failer.java │ ├── Runner.java │ ├── Succeeder.java │ └── Wait.java │ ├── composites │ ├── MemPriority.java │ ├── MemSequence.java │ ├── Priority.java │ └── Sequence.java │ ├── config │ ├── BTNodeCfg.java │ ├── BTTreeCfg.java │ ├── BTTreeProjectCfg.java │ ├── BTTreeProjectDataCfg.java │ ├── BevTreeConfig.java │ └── DefaultNodes.java │ ├── constant │ ├── B3Const.java │ └── B3Status.java │ ├── core │ ├── Action.java │ ├── BaseNode.java │ ├── BehaviorTree.java │ ├── BehaviorTreeProject.java │ ├── Blackboard.java │ ├── Composite.java │ ├── Condition.java │ ├── Decorator.java │ ├── IAction.java │ ├── IBaseWrapper.java │ ├── IComposite.java │ ├── ICondition.java │ ├── IDecorator.java │ ├── INode.java │ ├── INodeWorker.java │ ├── SubTree.java │ └── Tick.java │ ├── decorators │ ├── Inverter.java │ ├── Limiter.java │ ├── MaxTime.java │ ├── RepeatUntilFailure.java │ ├── RepeatUntilSuccess.java │ └── Repeater.java │ └── util │ └── FileUtil.java └── test ├── java └── com │ └── github │ └── silencesu │ └── behavior3java │ ├── Loader.java │ └── actions │ └── Log.java └── resources ├── project.b3 ├── tree.json ├── tree1.png └── tree2.png /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up JDK 1.8 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 1.8 16 | - name: Build with Maven 17 | run: mvn -B package --file pom.xml 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | *.iml 4 | .settings 5 | logs/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | script: 5 | - mvn clean package -DskipTests=true 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # behavior3java [![Build Status](https://travis-ci.org/SilenceSu/behavior3java.svg?branch=master)](https://travis-ci.org/SilenceSu/behavior3java) [![maven](https://maven-badges.herokuapp.com/maven-central/com.github.silencesu/behavior3java/badge.svg)](https://search.maven.org/search?q=behavior3java) 2 | 3 | 4 | ### Overview 5 | behavior3java是一套与behavior3editor编辑器相配套的行为树框架。 6 | 7 | ### 编辑器 8 | - https://github.com/behavior3/behavior3editor 源码 9 | - https://github.com/magicsea/behavior3editor/releases 桌面版本 10 | 11 | 12 | 13 | 14 | ### 开始使用 15 | 16 | 17 | ##### 注意点 18 | 使用桌面版本时候会出现b3文件,代表整个工程文件。 19 | 但是此时工程文件与导出project json 格式是不一样的。 20 | 所以导致报错不兼容,后期会区分加载b3文件与工程json 21 | 22 | ##### 引用框架 23 | ``` 24 | 25 | com.github.silencesu 26 | behavior3java 27 | 1.1.0 28 | 29 | 30 | ``` 31 | 32 | ##### 独立子树加载 33 | 34 | ``` 35 | 36 | //不包含子树的独立树 37 | BehaviorTree behaviorTree = B3Loader.loadB3Tree(confJson, extendNodes); 38 | ``` 39 | 40 | ##### 含有子树的工程加载 41 | 42 | ``` 43 | //加载整个工程文件 44 | BehaviorTreeProject behaviorTreeProject = B3Loader.loadB3Project(confJson, extendNodes); 45 | BehaviorTree behaviorTree = behaviorTreeProject.findBTTreeByTitle("b1");//获取名字为b1的树 46 | 47 | ``` 48 | 49 | ##### 开始运行 50 | ``` 51 | //该行为树所有数据都存储在blackboard中。 52 | Blackboard blackboard = new Blackboard(); 53 | //驱动tick 54 | behaviorTree.tick(new Object(), blackboard); 55 | 56 | ``` 57 | 58 | 59 | ### 测试用例行为树 60 | 61 | - ### 行为树b1 62 | 63 | ![行为树图](src/test/resources/tree1.png) 64 | 65 | - ### 行为树b2 66 | 67 | ![行为树图](src/test/resources/tree2.png) 68 | 69 | 70 | 71 | 72 | 73 | # 行为树介绍 74 | ### 行为树 4大基本类型节点 75 | 76 | - Composite Node 组合节点 77 | - Decorator Node 修饰节点 78 | - Condition Node 条件节点(叶节点) 79 | - Action Node 动作节点(叶节点) 80 | 81 | 82 | ### 行为树节点状态 83 | 84 | - SUCCESS 节点执行成功 85 | - FAILED 节点失败 86 | - RUNNING 节点正在运行 87 | - ERROR 程序错误 88 | 89 | 90 | #### Composite 节点子类 91 | 92 | - Priorty(Selector) Node 选择节点 93 | 94 | >当执行本类型Node时,它将从begin到end迭代执行自己的Child Node: 95 | 如遇到一个Child Node执行后返回True,那停止迭代, 96 | 本Node向自己的Parent Node也返回True;否则所有Child Node都返回False, 97 | 那本Node向自己的Parent Node返回False。 98 | 99 | 100 | - Sequence Node 顺序节点 101 | 102 | >当执行本类型Node时,它将从begin到end迭代执行自己的Child Node: 103 | 如遇到一个Child Node执行后返回False,那停止迭代, 104 | 本Node向自己的Parent Node也返回False;否则所有Child Node都返回True, 105 | 那本Node向自己的Parent Node返回True。 106 | 107 | 108 | 109 | - Parallel Node (计划) 110 | 111 | >并发执行它的所有Child Node。 112 | 而向Parent Node返回的值和Parallel Node所采取的具体策略相关: 113 | Parallel Selector Node: 一False则返回False,全True才返回True。 114 | Parallel Sequence Node: 一True则返回True,全False才返回False。 115 | Parallel Hybird Node: 指定数量的Child Node返回True或False后才决定结果。 116 | 117 | - Random 随机节点 (计划) 118 | 119 | >随机的执行节点 120 | 121 | 122 | #### Decorator 节点常用 123 | - Inverter 124 | - Limiter 125 | - MaxTime 126 | - Repeater 127 | - RepeatUntilFailure 128 | - RepeatUntilSuccess 129 | 130 | #### Action 节点 131 | 132 | - Succeeder 133 | - Failer 134 | - Error 135 | - Runner 136 | - Wait 137 | 138 | 139 | 140 | 141 | 142 | # 143 | 144 | ### 行为树相关学习 145 | - http://www.aisharing.com/archives/90#comment-238610 146 | - https://www.behaviac.com/concepts/ 147 | - https://note.youdao.com/ynoteshare1/index.html?id=4f46dc2144ea62b55f597630f5e666b4&type=note 148 | 149 | ### 相关项目 150 | - https://github.com/magicsea/behavior3go 151 | - https://github.com/behavior3/behavior3js 152 | - https://github.com/behavior3/behavior3py 153 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | behavior3java 8 | com.github.silencesu 9 | behavior3java 10 | 1.1.0 11 | jar 12 | 13 | 基于behavior3edit行为树的java版 14 | https://github.com/SilenceSu/behavior3java 15 | 16 | 17 | 18 | Silence 19 | Silence.sx@gmail.com 20 | 21 | 22 | 23 | 24 | 25 | Apache License 2.0 26 | http://www.apache.org/licenses/LICENSE-2.0 27 | 28 | 29 | 30 | 31 | 32 | scm:git:git@github.com:SilenceSu/behavior3java.git 33 | scm:git:git@github.com:SilenceSu/behavior3java.git 34 | https://github.com/SilenceSu/behavior3java 35 | 36 | 37 | 38 | 39 | UTF-8 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-compiler-plugin 48 | 3.3 49 | 50 | 8 51 | 8 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-source-plugin 59 | 3.1.0 60 | 61 | 62 | attach-sources 63 | 64 | jar-no-fork 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-javadoc-plugin 73 | 3.1.1 74 | 75 | -Xdoclint:none 76 | 77 | 78 | 79 | 80 | attach-javadocs 81 | 82 | jar 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | org.sonatype.plugins 91 | nexus-staging-maven-plugin 92 | 1.6.7 93 | true 94 | 95 | nexus-release 96 | https://oss.sonatype.org/ 97 | true 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-jar-plugin 105 | 2.4 106 | 107 | 108 | 109 | 110 | org.sonatype.plugins 111 | nexus-staging-maven-plugin 112 | 1.6.7 113 | true 114 | 115 | oss 116 | https://oss.sonatype.org/ 117 | true 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-release-plugin 124 | 2.4.2 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | com.alibaba 133 | fastjson 134 | 1.2.83 135 | 136 | 137 | org.projectlombok 138 | lombok 139 | 1.18.6 140 | provided 141 | 142 | 143 | org.slf4j 144 | slf4j-api 145 | 1.7.26 146 | 147 | 148 | 149 | ch.qos.logback 150 | logback-classic 151 | 1.2.3 152 | test 153 | 154 | 155 | ch.qos.logback 156 | logback-core 157 | 1.2.3 158 | 159 | 160 | junit 161 | junit 162 | test 163 | 4.13.1 164 | 165 | 166 | 167 | 168 | 169 | deployMvnCenter 170 | 171 | 172 | 173 | 174 | org.apache.maven.plugins 175 | maven-gpg-plugin 176 | 1.6 177 | 178 | 179 | sign-artifacts 180 | verify 181 | 182 | sign 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | oss 193 | https://oss.sonatype.org/content/repositories/snapshots/ 194 | 195 | 196 | 197 | oss 198 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/B3Loader.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java; 2 | 3 | import com.github.silencesu.behavior3java.config.BTTreeCfg; 4 | import com.github.silencesu.behavior3java.config.BTTreeProjectCfg; 5 | import com.github.silencesu.behavior3java.config.BevTreeConfig; 6 | import com.github.silencesu.behavior3java.core.BaseNode; 7 | import com.github.silencesu.behavior3java.core.BehaviorTree; 8 | import com.github.silencesu.behavior3java.core.BehaviorTreeProject; 9 | 10 | import java.util.Map; 11 | 12 | /** 13 | * 行为树加载器 14 | * 15 | * @author SilenceSu 16 | * @Email Silence.Sx@Gmail.com 17 | * Created by Silence on 2019/9/20. 18 | */ 19 | public class B3Loader { 20 | 21 | 22 | /** 23 | * @param treeJson 行为树配置文件 24 | * @param extendNodes 自定义扩展结点 25 | */ 26 | public static BehaviorTree loadB3Tree(String treeJson, Map> extendNodes) { 27 | 28 | //init tree config 29 | BTTreeCfg btTreeCfg = BevTreeConfig.LoadTreeCfg(treeJson); 30 | 31 | //load treecfg 32 | BehaviorTree tree = new BehaviorTree(); 33 | if (extendNodes != null && !extendNodes.isEmpty()) { 34 | tree.load(btTreeCfg, extendNodes); 35 | } else { 36 | tree.load(btTreeCfg); 37 | } 38 | 39 | return tree; 40 | 41 | } 42 | 43 | /** 44 | * 加载工程 45 | * 46 | * @param projectJson 47 | * @param extendNodes 48 | * @return 49 | */ 50 | public static BehaviorTreeProject loadB3Project(String projectJson, Map> extendNodes) { 51 | BTTreeProjectCfg projectCfg = BevTreeConfig.LoadBTTreePorjectCfg(projectJson); 52 | BehaviorTreeProject project = new BehaviorTreeProject(); 53 | project.initProject(projectCfg, extendNodes); 54 | return project; 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/actions/Error.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.actions; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | import com.github.silencesu.behavior3java.core.Action; 5 | import com.github.silencesu.behavior3java.core.Tick; 6 | 7 | /** 8 | * @author SilenceSu 9 | * @Email Silence.Sx@Gmail.com 10 | * Created by Silence on 2019/3/2. 11 | */ 12 | public class Error extends Action { 13 | 14 | @Override 15 | public B3Status onTick(Tick tick) { 16 | return B3Status.ERROR; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/actions/Failer.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.actions; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | import com.github.silencesu.behavior3java.core.Tick; 5 | import com.github.silencesu.behavior3java.core.Action; 6 | 7 | /** 8 | * @author SilenceSu 9 | * @Email Silence.Sx@Gmail.com 10 | * Created by Silence on 2019/3/2. 11 | */ 12 | public class Failer extends Action { 13 | @Override 14 | public B3Status onTick(Tick tick) { 15 | return B3Status.FAILURE; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/actions/Runner.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.actions; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | import com.github.silencesu.behavior3java.core.Tick; 5 | import com.github.silencesu.behavior3java.core.Action; 6 | 7 | /** 8 | * @author SilenceSu 9 | * @Email Silence.Sx@Gmail.com 10 | * Created by Silence on 2019/3/2. 11 | */ 12 | public class Runner extends Action { 13 | @Override 14 | public B3Status onTick(Tick tick) { 15 | return B3Status.SUCCESS; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/actions/Succeeder.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.actions; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | import com.github.silencesu.behavior3java.core.Tick; 5 | import com.github.silencesu.behavior3java.core.Action; 6 | 7 | /** 8 | * @author SilenceSu 9 | * @Email Silence.Sx@Gmail.com 10 | * Created by Silence on 2019/3/2. 11 | */ 12 | public class Succeeder extends Action { 13 | @Override 14 | public B3Status onTick(Tick tick) { 15 | return B3Status.SUCCESS; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/actions/Wait.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.actions; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Const; 5 | import com.github.silencesu.behavior3java.constant.B3Status; 6 | import com.github.silencesu.behavior3java.core.Tick; 7 | import com.github.silencesu.behavior3java.core.Action; 8 | 9 | /** 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | public class Wait extends Action { 15 | 16 | private long endTime; 17 | 18 | @Override 19 | public void initialize(BTNodeCfg nodeCfg) { 20 | super.initialize(nodeCfg); 21 | String ml = nodeCfg.getProperties().get(B3Const.END_TIME); 22 | endTime = Long.valueOf(ml); 23 | 24 | } 25 | 26 | @Override 27 | public void onOpen(Tick tick) { 28 | super.onOpen(tick); 29 | 30 | long startTime = System.currentTimeMillis(); 31 | tick.getBlackboard().setParam(B3Const.START_TIME, startTime, tick.getTree().getId(), this.getId()); 32 | 33 | } 34 | 35 | @Override 36 | public B3Status onTick(Tick tick) { 37 | 38 | long currentTime = System.currentTimeMillis(); 39 | Long startTime = tick.getBlackboard().getParam(B3Const.START_TIME, tick.getTree().getId(), this.getId()); 40 | 41 | if (currentTime - startTime > this.endTime) { 42 | return B3Status.SUCCESS; 43 | } 44 | 45 | 46 | return B3Status.RUNNING; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/composites/MemPriority.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.composites; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Const; 4 | import com.github.silencesu.behavior3java.constant.B3Status; 5 | import com.github.silencesu.behavior3java.core.Tick; 6 | import com.github.silencesu.behavior3java.core.Composite; 7 | 8 | /** 9 | * 记忆选择节点 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | public class MemPriority extends Composite { 15 | @Override 16 | public void onOpen(Tick tick) { 17 | super.onOpen(tick); 18 | tick.getBlackboard().setParam(B3Const.RUNNING_CHILD, 0, tick.getTree().getId(), this.getId()); 19 | } 20 | 21 | @Override 22 | public B3Status onTick(Tick tick) { 23 | 24 | Integer childId = tick.getBlackboard().getParam(B3Const.RUNNING_CHILD, tick.getTree().getId(), this.getId()); 25 | 26 | for (int i = childId; i < this.getChildCount(); i++) { 27 | B3Status status = this.getChild(i).execute(tick); 28 | 29 | if (status != B3Status.FAILURE) { 30 | 31 | if (status == B3Status.RUNNING) { 32 | tick.getBlackboard().setParam(B3Const.RUNNING_CHILD, i, tick.getTree().getId(), this.getId()); 33 | } 34 | 35 | return status; 36 | 37 | } 38 | 39 | 40 | 41 | } 42 | 43 | return B3Status.FAILURE; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/composites/MemSequence.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.composites; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Const; 4 | import com.github.silencesu.behavior3java.constant.B3Status; 5 | import com.github.silencesu.behavior3java.core.Blackboard; 6 | import com.github.silencesu.behavior3java.core.Tick; 7 | import com.github.silencesu.behavior3java.core.Composite; 8 | 9 | /** 10 | * 记忆顺序节点 11 | * @author SilenceSu 12 | * @Email Silence.Sx@Gmail.com 13 | * Created by Silence on 2019/3/2. 14 | */ 15 | public class MemSequence extends Composite { 16 | 17 | 18 | @Override 19 | public void onOpen(Tick tick) { 20 | 21 | Blackboard.Memory memory = tick.getBlackboard().getMemeory(tick.getTree().getId(), this.getId()); 22 | memory.getMemeory().put(B3Const.RUNNING_CHILD, 0); 23 | } 24 | 25 | @Override 26 | public B3Status onTick(Tick tick) { 27 | 28 | Blackboard.Memory mm = tick.getBlackboard().getMemeory(tick.getTree().getId(), this.getId()); 29 | 30 | int child = (int) mm.getMemeory().get(B3Const.RUNNING_CHILD); 31 | 32 | int childCount = getChildCount(); 33 | 34 | for (int i = child; i < childCount; i++) { 35 | B3Status status = this.getChild(i).execute(tick); 36 | 37 | if (status != B3Status.SUCCESS) { 38 | if (status == B3Status.RUNNING) { 39 | tick.getBlackboard().setParam(B3Const.RUNNING_CHILD, i, tick.getTree().getId(), this.getId()); 40 | } 41 | return status; 42 | 43 | } 44 | 45 | } 46 | 47 | return B3Status.SUCCESS; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/composites/Priority.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.composites; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | import com.github.silencesu.behavior3java.core.Tick; 5 | import com.github.silencesu.behavior3java.core.Composite; 6 | 7 | /** 8 | * 选择节点 9 | * @author SilenceSu 10 | * @Email Silence.Sx@Gmail.com 11 | * Created by Silence on 2019/3/2. 12 | */ 13 | public class Priority extends Composite { 14 | @Override 15 | public B3Status onTick(Tick tick) { 16 | for (int i = 0; i < this.getChildCount(); i++) { 17 | B3Status status = this.getChild(i).execute(tick); 18 | if (status != B3Status.FAILURE) { 19 | return status; 20 | } 21 | } 22 | return B3Status.FAILURE; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/composites/Sequence.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.composites; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | import com.github.silencesu.behavior3java.core.Tick; 5 | import com.github.silencesu.behavior3java.core.Composite; 6 | 7 | /** 8 | * 顺序节点 9 | * 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | public class Sequence extends Composite { 15 | @Override 16 | public B3Status onTick(Tick tick) { 17 | for (int i = 0; i < this.getChildCount(); i++) { 18 | B3Status status = this.getChild(i).execute(tick); 19 | if (status != B3Status.SUCCESS) { 20 | return status; 21 | } 22 | 23 | } 24 | return B3Status.SUCCESS; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/config/BTNodeCfg.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.config; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * 行为树节点配置 11 | * 12 | * @author SilenceSu 13 | * @Email Silence.Sx@Gmail.com 14 | * Created by Silence on 2019/3/2. 15 | */ 16 | @Setter 17 | @Getter 18 | public class BTNodeCfg { 19 | private String id; 20 | private String name; 21 | private String title; 22 | private String category; 23 | private String description; 24 | private List children; 25 | private String child; 26 | private Map parameters; 27 | private Map properties; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/config/BTTreeCfg.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.config; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * 行为树配置 9 | * @author SilenceSu 10 | * @Email Silence.Sx@Gmail.com 11 | * Created by Silence on 2019/3/2. 12 | */ 13 | @Data 14 | public class BTTreeCfg { 15 | private String id; 16 | private String title; 17 | private String description; 18 | private String root; 19 | private Map properties; 20 | private Map nodes; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/config/BTTreeProjectCfg.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.config; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | /** 7 | * 行为树工程配置 8 | * 9 | * @author SilenceSu 10 | * @Email Silence.Sx@Gmail.com 11 | * Created by Silence on 2020/1/18. 12 | */ 13 | @Setter 14 | @Getter 15 | public class BTTreeProjectCfg { 16 | private String name; 17 | 18 | private String description; 19 | 20 | private String scope; 21 | 22 | 23 | private BTTreeProjectDataCfg data; 24 | 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/config/BTTreeProjectDataCfg.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.config; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2020/1/18. 13 | */ 14 | @Setter 15 | @Getter 16 | public class BTTreeProjectDataCfg { 17 | private String version; 18 | private String scope; 19 | private String selectedTree; 20 | private List trees = new ArrayList<>(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/config/BevTreeConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.github.silencesu.behavior3java.util.FileUtil; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * bt配置 13 | * 14 | * @author SilenceSu 15 | * @Email Silence.Sx@Gmail.com 16 | * Created by Silence on 2019/3/2. 17 | */ 18 | public class BevTreeConfig { 19 | 20 | 21 | /** 22 | * 加载行为树配置 23 | * 24 | * @param path 25 | * @return 26 | */ 27 | public static BTTreeCfg LoadTreeCfg(String path) { 28 | BTTreeCfg treeCfg; 29 | String text = FileUtil.readFile(path); 30 | treeCfg = JSON.parseObject(text, BTTreeCfg.class); 31 | return treeCfg; 32 | } 33 | /** 34 | * 加载项目工程,将整个行为树都加载了 35 | * 36 | * @param path 37 | * @return 38 | */ 39 | public static List LoadTreesCfg(String path) { 40 | String text = FileUtil.readFile(path); 41 | JSONObject jsonObj = JSON.parseObject(text); 42 | JSONArray trees = jsonObj.getJSONArray("trees"); 43 | List list = trees.toJavaList(BTTreeCfg.class); 44 | return list; 45 | } 46 | 47 | 48 | /** 49 | * 加载整体工程数据 50 | * 51 | * @param path 52 | * @return 53 | */ 54 | public static BTTreeProjectCfg LoadBTTreePorjectCfg(String path) { 55 | String json = FileUtil.readFile(path); 56 | return JSON.parseObject(json, BTTreeProjectCfg.class); 57 | } 58 | 59 | 60 | /** 61 | * 获取节点配置的文件 62 | * 63 | * @param cfg 64 | * @param name 65 | * @param 66 | * @return 67 | */ 68 | @SuppressWarnings("unchecked") 69 | public T getProperty(BTNodeCfg cfg, String name) { 70 | 71 | Map props = cfg.getProperties(); 72 | if (props != null) { 73 | return (T) props.get(name); 74 | } 75 | return null; 76 | } 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/config/DefaultNodes.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.config; 2 | 3 | import com.github.silencesu.behavior3java.actions.Error; 4 | import com.github.silencesu.behavior3java.actions.*; 5 | import com.github.silencesu.behavior3java.composites.MemPriority; 6 | import com.github.silencesu.behavior3java.composites.MemSequence; 7 | import com.github.silencesu.behavior3java.composites.Priority; 8 | import com.github.silencesu.behavior3java.composites.Sequence; 9 | import com.github.silencesu.behavior3java.core.BaseNode; 10 | import com.github.silencesu.behavior3java.core.SubTree; 11 | import com.github.silencesu.behavior3java.decorators.*; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * 默认节点 18 | * @author SilenceSu 19 | * @Email Silence.Sx@Gmail.com 20 | * Created by Silence on 2019/3/6. 21 | */ 22 | public class DefaultNodes { 23 | /** 24 | * 注册支持的节点 25 | * key name 26 | * value node class 27 | */ 28 | private static Map> defaultNodes = new HashMap<>(); 29 | 30 | 31 | 32 | static { 33 | //actions 34 | defaultNodes.put("Error", Error.class); 35 | defaultNodes.put("Failer", Failer.class); 36 | defaultNodes.put("Runner", Runner.class); 37 | defaultNodes.put("Succeeder", Succeeder.class); 38 | defaultNodes.put("Wait", Wait.class); 39 | defaultNodes.put("SubTree", SubTree.class); 40 | 41 | //composites 42 | defaultNodes.put("MemPriority", MemPriority.class); 43 | defaultNodes.put("MemSequence", MemSequence.class); 44 | defaultNodes.put("Priority", Priority.class); 45 | defaultNodes.put("Sequence", Sequence.class); 46 | 47 | //decorators 48 | defaultNodes.put("Inverter", Inverter.class); 49 | defaultNodes.put("Limiter", Limiter.class); 50 | defaultNodes.put("MaxTime", MaxTime.class); 51 | defaultNodes.put("Repeater", Repeater.class); 52 | defaultNodes.put("RepeatUntilFailure", RepeatUntilFailure.class); 53 | defaultNodes.put("RepeatUntilSuccess", RepeatUntilSuccess.class); 54 | 55 | 56 | } 57 | /** 58 | * 扩充Nodes节点 59 | */ 60 | public static Map> extendCustomNodes(Map> maps) { 61 | defaultNodes.putAll(maps); 62 | return defaultNodes; 63 | } 64 | 65 | public static Map> get(){ 66 | return defaultNodes; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/constant/B3Const.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.constant; 2 | 3 | /** 4 | * 系统常量 5 | * @author SilenceSu 6 | * @Email Silence.Sx@Gmail.com 7 | * Created by Silence on 2019/3/2. 8 | */ 9 | public class B3Const { 10 | // Node categories 11 | public static final String COMPOSITE = "composite"; 12 | public static final String DECORATOR = "decorator"; 13 | public static final String ACTION = "action"; 14 | public static final String CONDITION = "condition"; 15 | public static final String SUBTREE = "tree"; 16 | public static final String SUBTREE_CLAZZ = "SubTree"; 17 | 18 | 19 | 20 | //param 21 | public static final String START_TIME = "condition"; 22 | public static final String RUNNING_CHILD = "runningChild"; 23 | public static final String MAX_LOOP = "maxLoop"; 24 | public static final String MAX_TIME = "maxTime"; 25 | public static final String END_TIME = "milliseconds"; 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/constant/B3Status.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.constant; 2 | 3 | /** 4 | * 行为树状态 5 | * 6 | * @author SilenceSu 7 | * @Email Silence.Sx@Gmail.com 8 | * Created by Silence on 2019/3/2. 9 | */ 10 | public enum B3Status { 11 | SUCCESS(1), FAILURE(2), RUNNING(3), ERROR(4), 12 | ; 13 | 14 | private int value; 15 | 16 | B3Status(int i) { 17 | 18 | this.value = i; 19 | } 20 | 21 | public int getValue() { 22 | return value; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/Action.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Const; 4 | 5 | /** 6 | * 行为节点 叶节点 7 | * @author SilenceSu 8 | * @Email Silence.Sx@Gmail.com 9 | * Created by Silence on 2019/3/2. 10 | */ 11 | public abstract class Action extends BaseNode implements IAction { 12 | 13 | 14 | @Override 15 | public String getCategory() { 16 | return B3Const.ACTION; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/BaseNode.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Status; 5 | import lombok.Data; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * 基础节点 11 | * 12 | * @author SilenceSu 13 | * @Email Silence.Sx@Gmail.com 14 | * Created by Silence on 2019/3/2. 15 | */ 16 | @Data 17 | public abstract class BaseNode implements INode, INodeWorker { 18 | 19 | 20 | private String id; 21 | private String name; 22 | private String title; 23 | private String description; 24 | 25 | private Map parameters; 26 | private Map properties; 27 | 28 | protected BehaviorTreeProject projectInfo; 29 | 30 | 31 | @Override 32 | public void onEnter(Tick tick) { 33 | 34 | } 35 | 36 | @Override 37 | public void onOpen(Tick tick) { 38 | 39 | } 40 | 41 | 42 | @Override 43 | public void onClose(Tick tick) { 44 | 45 | } 46 | 47 | @Override 48 | public void onExit(Tick tick) { 49 | 50 | } 51 | 52 | @Override 53 | public void initialize(BTNodeCfg nodeCfg) { 54 | 55 | this.id = nodeCfg.getId(); 56 | this.name = nodeCfg.getName(); 57 | this.title = nodeCfg.getTitle(); 58 | this.description = nodeCfg.getDescription(); 59 | this.parameters = nodeCfg.getParameters(); 60 | this.properties = nodeCfg.getProperties(); 61 | 62 | } 63 | 64 | @Override 65 | public B3Status run(Tick tick) { 66 | 67 | this.enter(tick); 68 | 69 | if (!tick.getBlackboard().getBool("isOpen", tick.getTree().getId(), this.id)) { 70 | this.open(tick); 71 | } 72 | 73 | B3Status status = this.tick(tick); 74 | 75 | if (status != B3Status.RUNNING) { 76 | this.onClose(tick); 77 | 78 | } 79 | 80 | this.exit(tick); 81 | 82 | return status; 83 | } 84 | 85 | 86 | @Override 87 | public void enter(Tick tick) { 88 | tick.enterNode(this); 89 | this.onEnter(tick); 90 | 91 | } 92 | 93 | @Override 94 | public void open(Tick tick) { 95 | 96 | tick.openNode(this); 97 | 98 | tick.getBlackboard().setParam("isOpen", true, tick.getTree().getId(), this.getId()); 99 | 100 | 101 | this.onOpen(tick); 102 | } 103 | 104 | @Override 105 | public B3Status tick(Tick tick) { 106 | 107 | tick.tickNode(this); 108 | 109 | return this.onTick(tick); 110 | } 111 | 112 | @Override 113 | public void close(Tick tick) { 114 | tick.closeNode(this); 115 | tick.getBlackboard().setParam("isOpen", false, tick.getTree().getId(), this.getId()); 116 | this.onClose(tick); 117 | 118 | } 119 | 120 | @Override 121 | public void exit(Tick tick) { 122 | tick.exitNNode(this 123 | ); 124 | this.onExit(tick); 125 | } 126 | 127 | @Override 128 | public B3Status execute(Tick tick) { 129 | return this.run(tick); 130 | } 131 | 132 | @Override 133 | public String getName() { 134 | return this.name; 135 | } 136 | 137 | @Override 138 | public String getTitle() { 139 | return this.title; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/BehaviorTree.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.config.BTTreeCfg; 5 | import com.github.silencesu.behavior3java.config.DefaultNodes; 6 | import com.github.silencesu.behavior3java.constant.B3Const; 7 | import com.github.silencesu.behavior3java.constant.B3Status; 8 | import lombok.Getter; 9 | import lombok.Setter; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.UUID; 16 | 17 | /** 18 | * 行为树 19 | * 20 | * @author SilenceSu 21 | * @Email Silence.Sx@Gmail.com 22 | * Created by Silence on 2019/3/2. 23 | */ 24 | @Getter 25 | @Setter 26 | @Slf4j 27 | public class BehaviorTree { 28 | 29 | private String id = UUID.randomUUID().toString().replaceAll("-", ""); 30 | private String titile; 31 | private String description; 32 | private Map properties = new HashMap<>(); 33 | 34 | private BaseNode root; 35 | 36 | //project 引用对象 37 | private BehaviorTreeProject projectInfo; 38 | 39 | 40 | 41 | public void load(BTTreeCfg cfg) { 42 | load(cfg, new HashMap<>()); 43 | } 44 | 45 | 46 | public void load(BTTreeCfg cfg, Map> extendNodes) { 47 | 48 | this.titile = cfg.getTitle(); 49 | this.description = cfg.getDescription(); 50 | this.properties = cfg.getProperties(); 51 | 52 | Map> nodeMaps = new HashMap<>(DefaultNodes.get()); 53 | //加载扩展nodes 54 | if (extendNodes != null && extendNodes.size() > 0) { 55 | nodeMaps.putAll(extendNodes); 56 | } 57 | 58 | 59 | Map nodes = new HashMap<>(); 60 | //create nodes 61 | for (Map.Entry nodeEntry : cfg.getNodes().entrySet()) { 62 | 63 | String id = nodeEntry.getKey(); 64 | 65 | BTNodeCfg nodeCfg = nodeEntry.getValue(); 66 | 67 | BaseNode node = null; 68 | 69 | //检查是或否为子树 70 | if (nodeCfg.getCategory()!=null && nodeCfg.getCategory().equals(B3Const.SUBTREE)) { 71 | node = new SubTree(); 72 | } else { 73 | //普通结点加载 74 | Class clazz = nodeMaps.get(nodeCfg.getName()); 75 | if (clazz != null) { 76 | try { 77 | node = clazz.newInstance(); 78 | } catch (InstantiationException | IllegalAccessException e) { 79 | e.printStackTrace(); 80 | } 81 | } 82 | } 83 | 84 | 85 | if (node == null) { 86 | log.error("create node erro:{}", nodeCfg.getName()); 87 | break; 88 | } 89 | 90 | node.initialize(nodeCfg); 91 | 92 | if (projectInfo != null) { 93 | node.setProjectInfo(projectInfo); 94 | } 95 | 96 | nodes.put(id, node); 97 | 98 | } 99 | 100 | 101 | /** 102 | * connect the nodes 103 | */ 104 | for (Map.Entry nodeEntry : cfg.getNodes().entrySet()) { 105 | BaseNode node = nodes.get(nodeEntry.getKey()); 106 | BTNodeCfg nodeCfg = nodeEntry.getValue(); 107 | 108 | if (node.getCategory().equals(B3Const.COMPOSITE) && nodeCfg.getChildren() != null) { 109 | for (String cid : nodeCfg.getChildren()) { 110 | IComposite comp = (IComposite) node; 111 | comp.addChild(nodes.get(cid)); 112 | } 113 | 114 | } else if (node.getCategory().equals(B3Const.DECORATOR) && nodeCfg.getChild().length() > 0) { 115 | IDecorator deco = (IDecorator) node; 116 | deco.setChild(nodes.get(nodeCfg.getChild())); 117 | } 118 | 119 | 120 | } 121 | 122 | 123 | //设置root节点 124 | this.root = nodes.get(cfg.getRoot()); 125 | 126 | } 127 | 128 | 129 | public B3Status tick(T t, Blackboard blackboard) { 130 | 131 | if (blackboard == null) { 132 | log.error("The blackboard parameter is obligatory and must be an instance of b3.Blackboard"); 133 | return B3Status.ERROR; 134 | } 135 | 136 | /* CREATE A TICK OBJECT */ 137 | Tick tick = new Tick(); 138 | tick.setTarget(t); 139 | tick.setBlackboard(blackboard); 140 | tick.setTree(this); 141 | 142 | 143 | B3Status status = this.root.run(tick); 144 | 145 | List lastOpenNodes = blackboard.getTreeData(this.id).openNodes; 146 | 147 | List currOpenNodes = tick.getOpenNodes(); 148 | 149 | // does not close if it is still open in this tick 150 | int start = 0; 151 | for (int i = 0; i < (lastOpenNodes.size() > currOpenNodes.size() ? currOpenNodes.size() : lastOpenNodes.size()); i++) { 152 | start = i + 1; 153 | if (lastOpenNodes.get(i) != currOpenNodes.get(i)) { 154 | break; 155 | } 156 | } 157 | 158 | // close the nodes 159 | for (int i = lastOpenNodes.size() - 1; i >= start; i--) { 160 | lastOpenNodes.get(i).close(tick); 161 | } 162 | 163 | /* POPULATE BLACKBOARD */ 164 | blackboard.getTreeData(this.id).openNodes = currOpenNodes; 165 | blackboard.SetTree("nodeCount", tick.getNodeCount(), this.id); 166 | 167 | 168 | return status; 169 | } 170 | 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/BehaviorTreeProject.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.config.BTTreeCfg; 4 | import com.github.silencesu.behavior3java.config.BTTreeProjectCfg; 5 | import lombok.Data; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * 行为树工程 13 | * 14 | * @author SilenceSu 15 | * @Email Silence.Sx@Gmail.com 16 | * Created by Silence on 2019/3/2. 17 | */ 18 | @Data 19 | @Slf4j 20 | public class BehaviorTreeProject { 21 | 22 | /** 23 | * 工程内的树 24 | */ 25 | private Map titleTreeMap = new HashMap<>(); 26 | 27 | private Map idTreeMap = new HashMap<>(); 28 | 29 | public BehaviorTree findBTTreeByTitle(String treeTitle) { 30 | return titleTreeMap.get(treeTitle.trim()); 31 | } 32 | 33 | public BehaviorTree findBTTreeById(String id) { 34 | return idTreeMap.get(id); 35 | } 36 | 37 | 38 | /** 39 | * 初始化工程数据数据 40 | * 41 | * @param projectCfg 工程配置 42 | * @param extendNodes 扩展结点 43 | */ 44 | public void initProject(BTTreeProjectCfg projectCfg, Map> extendNodes) { 45 | 46 | 47 | for (BTTreeCfg treeCfg : projectCfg.getData().getTrees()) { 48 | 49 | BehaviorTree behaviorTree = new BehaviorTree(); 50 | behaviorTree.setProjectInfo(this); 51 | behaviorTree.load(treeCfg, extendNodes); 52 | 53 | titleTreeMap.put(treeCfg.getTitle(), behaviorTree); 54 | idTreeMap.put(treeCfg.getId(), behaviorTree); 55 | 56 | 57 | } 58 | 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/Blackboard.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * 黑板报 12 | * k-v存储数据 13 | * 14 | * @author SilenceSu 15 | * @Email Silence.Sx@Gmail.com 16 | * Created by Silence on 2019/3/2. 17 | */ 18 | @Data 19 | public class Blackboard { 20 | 21 | 22 | // private Memory baseMemory; 23 | private Map treeMemory = new HashMap<>(); 24 | 25 | 26 | TreeData getTreeData(String treeScope) { 27 | 28 | if (treeMemory.get(treeScope) == null) { 29 | return new TreeData(); 30 | } 31 | 32 | return treeMemory.get(treeScope).getTreeData(); 33 | 34 | } 35 | 36 | 37 | void SetTree(String key, Object object, String treeScope) { 38 | 39 | 40 | Memory memory = this.getMemeory(treeScope, ""); 41 | 42 | memory.getMemeory().put(key, object); 43 | } 44 | 45 | private TreeMemory getTreeMemory(String treeScope) { 46 | 47 | TreeMemory tm = treeMemory.get(treeScope); 48 | if (tm == null) { 49 | tm = new TreeMemory(); 50 | treeMemory.put(treeScope, tm); 51 | } 52 | 53 | return tm; 54 | 55 | 56 | } 57 | 58 | public Memory getMemeory(String treeScope, String nodeScope) { 59 | 60 | TreeMemory tm = getTreeMemory(treeScope); 61 | 62 | 63 | return getNodeMemory(tm, nodeScope); 64 | } 65 | 66 | 67 | Boolean getBool(String key, String treeScope, String nodeScope) { 68 | 69 | Memory memory = getMemeory(treeScope, nodeScope); 70 | if (memory == null) { 71 | return false; 72 | } 73 | Object object = memory.getMemeory().get(key); 74 | if (object == null) { 75 | return false; 76 | } 77 | return (Boolean) object; 78 | } 79 | 80 | 81 | private Memory getNodeMemory(TreeMemory treeMemory, String nodeScope) { 82 | 83 | Memory memory = treeMemory.getNodeMemory().get(nodeScope); 84 | 85 | if (memory == null) { 86 | 87 | memory = new Memory(); 88 | treeMemory.getNodeMemory().put(nodeScope, memory); 89 | 90 | } 91 | return memory; 92 | 93 | 94 | } 95 | 96 | 97 | public void setParam(String key, Object value, String treeScope, String nodeScope) { 98 | Memory memory = getMemeory(treeScope, nodeScope); 99 | memory.getMemeory().put(key, value); 100 | } 101 | 102 | 103 | @SuppressWarnings("unchecked") 104 | public T getParam(String key, String treeScope, String nodeScope) { 105 | Memory memory = getMemeory(treeScope, nodeScope); 106 | Object object = memory.getMemeory().get(key); 107 | return (T) object; 108 | } 109 | 110 | @Data 111 | class TreeData { 112 | 113 | Memory nodeMemory = new Memory(); 114 | List openNodes = new ArrayList<>(); 115 | private int traversalDepth; 116 | private int traversalCycle; 117 | 118 | 119 | } 120 | 121 | @Data 122 | public class Memory { 123 | private Map memeory = new HashMap<>(); 124 | 125 | public Object get(String key) { 126 | return memeory.get(key); 127 | } 128 | } 129 | 130 | @Data 131 | public class TreeMemory { 132 | private TreeData treeData = new TreeData(); 133 | private Map nodeMemory = new HashMap<>(); 134 | 135 | } 136 | 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/Composite.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Const; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * 组合节点 10 | * 11 | * @author SilenceSu 12 | * @Email Silence.Sx@Gmail.com 13 | * Created by Silence on 2019/3/2. 14 | */ 15 | public abstract class Composite extends BaseNode implements IComposite { 16 | 17 | private List children = new ArrayList<>(); 18 | 19 | @Override 20 | public int getChildCount() { 21 | return children.size(); 22 | } 23 | 24 | @Override 25 | public BaseNode getChild(int index) { 26 | return children.get(index); 27 | } 28 | 29 | 30 | @Override 31 | public void addChild(BaseNode baseNode) { 32 | children.add(baseNode); 33 | } 34 | 35 | @Override 36 | public String getCategory() { 37 | return B3Const.COMPOSITE; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/Condition.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Const; 4 | 5 | /** 6 | * 条件节点 叶节点 7 | * @author SilenceSu 8 | * @Email Silence.Sx@Gmail.com 9 | * Created by Silence on 2019/3/4. 10 | */ 11 | public abstract class Condition extends BaseNode implements ICondition { 12 | 13 | @Override 14 | public String getCategory() { 15 | return B3Const.CONDITION; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/Decorator.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Const; 4 | 5 | /** 6 | * 装饰节点 7 | * 8 | * @author SilenceSu 9 | * @Email Silence.Sx@Gmail.com 10 | * Created by Silence on 2019/3/2. 11 | */ 12 | public abstract class Decorator extends BaseNode implements IDecorator { 13 | private BaseNode child; 14 | 15 | @Override 16 | public void setChild(BaseNode child) { 17 | 18 | this.child = child; 19 | } 20 | 21 | @Override 22 | public BaseNode getChild() { 23 | return this.child; 24 | } 25 | 26 | @Override 27 | public String getCategory() { 28 | return B3Const.DECORATOR; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/IAction.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | /** 4 | * 行为节点 5 | * @author SilenceSu 6 | * @Email Silence.Sx@Gmail.com 7 | * Created by Silence on 2019/3/2. 8 | */ 9 | public interface IAction { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/IBaseWrapper.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | 5 | /** 6 | * 包装类 7 | * @author SilenceSu 8 | * @Email Silence.Sx@Gmail.com 9 | * Created by Silence on 2019/3/4. 10 | */ 11 | public interface IBaseWrapper { 12 | 13 | B3Status run(Tick tick); 14 | 15 | void enter(Tick tick); 16 | 17 | void open(Tick tick); 18 | 19 | B3Status tick(Tick tick); 20 | 21 | void close(Tick tick); 22 | 23 | void exit(Tick tick); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/IComposite.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | /** 4 | * 组合节点 5 | * 6 | * @author SilenceSu 7 | * @Email Silence.Sx@Gmail.com 8 | * Created by Silence on 2019/3/2. 9 | */ 10 | public interface IComposite { 11 | 12 | /** 13 | * 子节点count 14 | * 15 | * @return 16 | */ 17 | int getChildCount(); 18 | 19 | /** 20 | * 根据id 索引 子节点 21 | * 22 | * @param index 23 | * @return 24 | */ 25 | BaseNode getChild(int index); 26 | 27 | /** 28 | * 增加一个子节点 29 | * 30 | * @param baseNode 31 | */ 32 | void addChild(BaseNode baseNode); 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/ICondition.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | /** 4 | * 条件节点 5 | * @author SilenceSu 6 | * @Email Silence.Sx@Gmail.com 7 | * Created by Silence on 2019/3/2. 8 | */ 9 | public interface ICondition { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/IDecorator.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | /** 4 | * 装饰节点 5 | * @author SilenceSu 6 | * @Email Silence.Sx@Gmail.com 7 | * Created by Silence on 2019/3/2. 8 | */ 9 | public interface IDecorator { 10 | 11 | void setChild(BaseNode child); 12 | 13 | BaseNode getChild(); 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/INode.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Status; 5 | 6 | /** 7 | * @author SilenceSu 8 | * @Email Silence.Sx@Gmail.com 9 | * Created by Silence on 2019/3/2. 10 | */ 11 | public interface INode extends IBaseWrapper{ 12 | 13 | 14 | void initialize(BTNodeCfg nodeCfg); 15 | 16 | String getCategory(); 17 | 18 | B3Status execute(Tick tick); 19 | 20 | String getName(); 21 | 22 | String getTitle(); 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/INodeWorker.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | 5 | /** 6 | * @author SilenceSu 7 | * @Email Silence.Sx@Gmail.com 8 | * Created by Silence on 2019/3/2. 9 | */ 10 | public interface INodeWorker { 11 | 12 | void onEnter(Tick tick); 13 | 14 | void onOpen(Tick tick); 15 | 16 | B3Status onTick(Tick tick); 17 | 18 | void onClose(Tick tick); 19 | 20 | void onExit(Tick tick); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/SubTree.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Const; 4 | import com.github.silencesu.behavior3java.constant.B3Status; 5 | 6 | /** 7 | * 行为节点 子树节点 8 | * 9 | * @author ming ming 10 | * @Email mingtingjian@sina.com Created by ming ming on 2020/1/7. 11 | */ 12 | public class SubTree extends Action { 13 | 14 | private BehaviorTree subTree; 15 | 16 | 17 | 18 | @Override 19 | public String getCategory() { 20 | return B3Const.ACTION; 21 | } 22 | 23 | @Override 24 | public B3Status onTick(Tick tick) { 25 | //子树可能没有加载上来,所以要延迟加载执行 26 | if (subTree == null) { 27 | subTree = projectInfo.findBTTreeById(getName());//此处name为子树的id 28 | } 29 | if (subTree == null) { 30 | return B3Status.ERROR; 31 | } 32 | return subTree.tick(tick.getTarget(), tick.getBlackboard()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/core/Tick.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.core; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * Tick 10 | * 11 | * @author SilenceSu 12 | * @Email Silence.Sx@Gmail.com 13 | * Created by Silence on 2019/3/2. 14 | */ 15 | @Data 16 | public class Tick { 17 | 18 | private BehaviorTree tree; 19 | 20 | private Blackboard blackboard; 21 | 22 | private List openNodes = new ArrayList<>(); 23 | 24 | Object target; 25 | 26 | private int nodeCount; 27 | 28 | public Tick() { 29 | initialize(); 30 | 31 | } 32 | 33 | 34 | public void initialize() { 35 | 36 | this.tree = null; 37 | this.blackboard = null; 38 | 39 | 40 | this.openNodes = new ArrayList<>(); 41 | this.nodeCount = 0; 42 | 43 | } 44 | 45 | public String treeId() { 46 | return this.getTree().getId(); 47 | } 48 | 49 | 50 | public BehaviorTree getTree() { 51 | return this.tree; 52 | } 53 | 54 | 55 | public Blackboard getBlackboard() { 56 | return this.blackboard; 57 | } 58 | 59 | public void enterNode(BaseNode node) { 60 | this.nodeCount++; 61 | this.openNodes.add(node); 62 | } 63 | 64 | public void openNode(BaseNode node) { 65 | 66 | } 67 | 68 | public void tickNode(BaseNode node) { 69 | 70 | } 71 | 72 | public void closeNode(BaseNode node) { 73 | 74 | if (this.openNodes.size() > 0) { 75 | this.openNodes.remove(node); 76 | 77 | } 78 | 79 | 80 | } 81 | 82 | public void exitNNode(BaseNode node) { 83 | 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/decorators/Inverter.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.decorators; 2 | 3 | import com.github.silencesu.behavior3java.constant.B3Status; 4 | import com.github.silencesu.behavior3java.core.Tick; 5 | import com.github.silencesu.behavior3java.core.Decorator; 6 | 7 | /** 8 | * @author SilenceSu 9 | * @Email Silence.Sx@Gmail.com 10 | * Created by Silence on 2019/3/2. 11 | */ 12 | public class Inverter extends Decorator { 13 | 14 | @Override 15 | public B3Status onTick(Tick tick) { 16 | if (this.getChild() == null) { 17 | return B3Status.ERROR; 18 | } 19 | B3Status status = this.getChild().execute(tick); 20 | if (status == B3Status.SUCCESS) { 21 | status = B3Status.FAILURE; 22 | } else if (status == B3Status.FAILURE) { 23 | status = B3Status.SUCCESS; 24 | } 25 | return status; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/decorators/Limiter.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.decorators; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Const; 5 | import com.github.silencesu.behavior3java.constant.B3Status; 6 | import com.github.silencesu.behavior3java.core.Tick; 7 | import com.github.silencesu.behavior3java.core.Decorator; 8 | 9 | /** 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | public class Limiter extends Decorator { 15 | private int maxLoop; 16 | 17 | @Override 18 | public void initialize(BTNodeCfg nodeCfg) { 19 | super.initialize(nodeCfg); 20 | 21 | 22 | String ml = nodeCfg.getProperties().get(B3Const.MAX_LOOP); 23 | maxLoop = Integer.valueOf(ml); 24 | 25 | } 26 | 27 | @Override 28 | public void onOpen(Tick tick) { 29 | super.onOpen(tick); 30 | tick.getBlackboard().setParam("i", 0, tick.getTree().getId(), this.getId()); 31 | 32 | } 33 | 34 | @Override 35 | public B3Status onTick(Tick tick) { 36 | 37 | Integer i = tick.getBlackboard().getParam("i", tick.getTree().getId(), this.getId()); 38 | if (i < this.maxLoop) { 39 | 40 | B3Status status = this.getChild().execute(tick); 41 | 42 | if (status == B3Status.SUCCESS || B3Status.FAILURE == status) { 43 | tick.getBlackboard().setParam("i", i + 1, tick.getTree().getId(), this.getId()); 44 | } 45 | return status; 46 | 47 | 48 | } 49 | 50 | return B3Status.FAILURE; 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/decorators/MaxTime.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.decorators; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Const; 5 | import com.github.silencesu.behavior3java.constant.B3Status; 6 | import com.github.silencesu.behavior3java.core.Tick; 7 | import com.github.silencesu.behavior3java.core.Decorator; 8 | 9 | /** 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | public class MaxTime extends Decorator { 15 | 16 | private long maxTime; 17 | 18 | @Override 19 | public void initialize(BTNodeCfg nodeCfg) { 20 | super.initialize(nodeCfg); 21 | 22 | maxTime = Long.valueOf(nodeCfg.getProperties().get(B3Const.MAX_TIME)); 23 | 24 | 25 | } 26 | @Override 27 | public void onOpen(Tick tick) { 28 | super.onOpen(tick); 29 | 30 | long startTime = System.currentTimeMillis(); 31 | 32 | tick.getBlackboard().setParam(B3Const.START_TIME, startTime, tick.getTree().getId(), this.getId()); 33 | 34 | } 35 | 36 | 37 | 38 | @Override 39 | public B3Status onTick(Tick tick) { 40 | 41 | if (this.getChild() == null) { 42 | return B3Status.ERROR; 43 | } 44 | 45 | long currTime = System.currentTimeMillis(); 46 | Long startTime = tick.getBlackboard().getParam(B3Const.START_TIME, tick.getTree().getId(), this.getId()); 47 | B3Status status = this.getChild().execute(tick); 48 | if (currTime - startTime > this.maxTime) { 49 | return B3Status.FAILURE; 50 | } 51 | 52 | return status; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/decorators/RepeatUntilFailure.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.decorators; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Const; 5 | import com.github.silencesu.behavior3java.constant.B3Status; 6 | import com.github.silencesu.behavior3java.core.Tick; 7 | import com.github.silencesu.behavior3java.core.Decorator; 8 | 9 | /** 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | public class RepeatUntilFailure extends Decorator { 15 | private int maxLoop; 16 | 17 | @Override 18 | public void initialize(BTNodeCfg nodeCfg) { 19 | super.initialize(nodeCfg); 20 | this.maxLoop = Integer.valueOf(nodeCfg.getProperties().get(B3Const.MAX_LOOP)); 21 | 22 | } 23 | 24 | 25 | @Override 26 | public void onOpen(Tick tick) { 27 | super.onOpen(tick); 28 | tick.getBlackboard().setParam("i", 0, tick.getTree().getId(), this.getId()); 29 | 30 | } 31 | 32 | @Override 33 | public B3Status onTick(Tick tick) { 34 | 35 | if (this.getChild() == null) { 36 | return B3Status.FAILURE; 37 | } 38 | 39 | 40 | Integer i = tick.getBlackboard().getParam("i", tick.getTree().getId(), this.getId()); 41 | B3Status status = B3Status.ERROR; 42 | 43 | while (i < maxLoop) { 44 | status = this.getChild().execute(tick); 45 | if (status == B3Status.SUCCESS) { 46 | i++; 47 | }else{ 48 | break; 49 | 50 | } 51 | } 52 | 53 | 54 | tick.getBlackboard().setParam("i", i, tick.getTree().getId(), this.getId()); 55 | return status; 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/decorators/RepeatUntilSuccess.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.decorators; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Const; 5 | import com.github.silencesu.behavior3java.constant.B3Status; 6 | import com.github.silencesu.behavior3java.core.Tick; 7 | import com.github.silencesu.behavior3java.core.Decorator; 8 | 9 | /** 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | public class RepeatUntilSuccess extends Decorator { 15 | private int maxLoop; 16 | 17 | @Override 18 | public void initialize(BTNodeCfg nodeCfg) { 19 | super.initialize(nodeCfg); 20 | this.maxLoop = Integer.valueOf(nodeCfg.getProperties().get(B3Const.MAX_LOOP)); 21 | 22 | } 23 | 24 | @Override 25 | public void onOpen(Tick tick) { 26 | super.onOpen(tick); 27 | tick.getBlackboard().setParam("i", 0, tick.getTree().getId(), this.getId()); 28 | 29 | } 30 | 31 | @Override 32 | public B3Status onTick(Tick tick) { 33 | 34 | if (this.getChild() == null) { 35 | return B3Status.FAILURE; 36 | } 37 | 38 | Integer i = tick.getBlackboard().getParam("i", tick.getTree().getId(), this.getId()); 39 | B3Status status = B3Status.ERROR; 40 | 41 | while (i < maxLoop) { 42 | status = this.getChild().execute(tick); 43 | if (status == B3Status.FAILURE) { 44 | i++; 45 | } else { 46 | break; 47 | 48 | } 49 | } 50 | 51 | 52 | tick.getBlackboard().setParam("i", i, tick.getTree().getId(), this.getId()); 53 | return status; 54 | 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/decorators/Repeater.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.decorators; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Const; 5 | import com.github.silencesu.behavior3java.constant.B3Status; 6 | import com.github.silencesu.behavior3java.core.Tick; 7 | import com.github.silencesu.behavior3java.core.Decorator; 8 | 9 | /** 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | public class Repeater extends Decorator { 15 | 16 | private int maxLoop = 0; 17 | 18 | @Override 19 | public void initialize(BTNodeCfg nodeCfg) { 20 | super.initialize(nodeCfg); 21 | 22 | String ml = nodeCfg.getProperties().get(B3Const.MAX_LOOP); 23 | maxLoop = Integer.valueOf(ml); 24 | 25 | } 26 | 27 | @Override 28 | public void onOpen(Tick tick) { 29 | super.onOpen(tick); 30 | tick.getBlackboard().setParam("i", 0, tick.getTree().getId(), this.getId()); 31 | } 32 | 33 | @Override 34 | public B3Status onTick(Tick tick) { 35 | 36 | if (this.getChild() == null) { 37 | return B3Status.ERROR; 38 | } 39 | 40 | Integer objParam = tick.getBlackboard().getParam("i", tick.getTree().getId(), this.getId()); 41 | 42 | 43 | B3Status status = B3Status.SUCCESS; 44 | 45 | 46 | int i = objParam; 47 | 48 | 49 | while (i < this.maxLoop) { 50 | status = this.getChild().execute(tick); 51 | if (status == B3Status.SUCCESS || status == B3Status.FAILURE) { 52 | i++; 53 | } else { 54 | break; 55 | } 56 | } 57 | 58 | 59 | tick.getBlackboard().setParam("i", i, tick.getTree().getId(), this.getId()); 60 | 61 | 62 | return status; 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/github/silencesu/behavior3java/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.nio.charset.StandardCharsets; 8 | 9 | /** 10 | * 文件工具 11 | * @author SilenceSu 12 | * @Email Silence.Sx@Gmail.com 13 | * Created by Silence on 2019/3/2. 14 | */ 15 | public class FileUtil { 16 | 17 | 18 | /** 19 | * 读取文件内容 20 | * @param Path 21 | * @return 22 | */ 23 | public static String readFile(String Path) { 24 | 25 | BufferedReader reader = null; 26 | StringBuilder laststr = new StringBuilder(); 27 | try { 28 | FileInputStream fileInputStream = new FileInputStream(Path); 29 | InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); 30 | reader = new BufferedReader(inputStreamReader); 31 | String tempString; 32 | while ((tempString = reader.readLine()) != null) { 33 | laststr.append(tempString); 34 | } 35 | reader.close(); 36 | } catch (IOException e) { 37 | e.printStackTrace(); 38 | } finally { 39 | if (reader != null) { 40 | try { 41 | reader.close(); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | } 47 | return laststr.toString(); 48 | 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/github/silencesu/behavior3java/Loader.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java; 2 | 3 | import com.github.silencesu.behavior3java.actions.Log; 4 | import com.github.silencesu.behavior3java.core.BaseNode; 5 | import com.github.silencesu.behavior3java.core.BehaviorTree; 6 | import com.github.silencesu.behavior3java.core.BehaviorTreeProject; 7 | import com.github.silencesu.behavior3java.core.Blackboard; 8 | import org.junit.Test; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * 测试用例 15 | * 16 | * @author SilenceSu 17 | * @Email Silence.Sx@Gmail.com 18 | * Created by Silence on 2019/3/2. 19 | */ 20 | public class Loader { 21 | 22 | //自定义扩展log 23 | private static Map> extendNodes = new HashMap>() { 24 | { 25 | put("Log", Log.class); 26 | } 27 | }; 28 | 29 | 30 | @Test 31 | public void loadTree() { 32 | String confJson = Loader.class.getResource("/").getPath() + "tree.json"; 33 | BehaviorTree behaviorTree = B3Loader.loadB3Tree(confJson, extendNodes); 34 | Blackboard blackboard = new Blackboard(); 35 | behaviorTree.tick(new Object(), blackboard); 36 | } 37 | 38 | @Test 39 | public void loadProject() { 40 | String confJson = Loader.class.getResource("/").getPath() + "project.b3"; 41 | BehaviorTreeProject behaviorTreeProject = B3Loader.loadB3Project(confJson, extendNodes); 42 | Blackboard blackboard = new Blackboard(); 43 | BehaviorTree behaviorTree = behaviorTreeProject.findBTTreeByTitle("tree1"); 44 | behaviorTree.tick(new Object(), blackboard); 45 | 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/github/silencesu/behavior3java/actions/Log.java: -------------------------------------------------------------------------------- 1 | package com.github.silencesu.behavior3java.actions; 2 | 3 | import com.github.silencesu.behavior3java.config.BTNodeCfg; 4 | import com.github.silencesu.behavior3java.constant.B3Status; 5 | import com.github.silencesu.behavior3java.core.Tick; 6 | import com.github.silencesu.behavior3java.core.Action; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | /** 10 | * @author SilenceSu 11 | * @Email Silence.Sx@Gmail.com 12 | * Created by Silence on 2019/3/2. 13 | */ 14 | @Slf4j 15 | public class Log extends Action { 16 | private String info; 17 | 18 | 19 | @Override 20 | public void initialize(BTNodeCfg nodeCfg) { 21 | super.initialize(nodeCfg); 22 | info = nodeCfg.getProperties().get("info"); 23 | } 24 | 25 | @Override 26 | public void onOpen(Tick tick) { 27 | super.onOpen(tick); 28 | } 29 | 30 | @Override 31 | public B3Status onTick(Tick tick) { 32 | log.info("action-log:{}", this.info); 33 | return B3Status.SUCCESS; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/resources/project.b3: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aaa", 3 | "description": "", 4 | "data": { 5 | "version": "0.3.0", 6 | "scope": "project", 7 | "selectedTree": "67e3047e-0942-4d55-8443-9f28dc50b660", 8 | "trees": [ 9 | { 10 | "version": "0.3.0", 11 | "scope": "tree", 12 | "id": "ef899718-ff13-4ab7-9bcc-aae6752bca62", 13 | "title": "tree2", 14 | "description": "", 15 | "root": "eaab5232-0911-428c-8ef5-41fcb8d506a4", 16 | "properties": {}, 17 | "nodes": { 18 | "8afb402e-6cc1-46bd-8fd0-ea432bf40551": { 19 | "id": "8afb402e-6cc1-46bd-8fd0-ea432bf40551", 20 | "name": "Repeater", 21 | "category": "decorator", 22 | "title": "Repeat x", 23 | "description": "", 24 | "properties": { 25 | "maxLoop": 2 26 | }, 27 | "display": { 28 | "x": 144, 29 | "y": -144 30 | }, 31 | "child": "36d284cb-71ef-4962-8e1e-c174e44d7adb" 32 | }, 33 | "36d284cb-71ef-4962-8e1e-c174e44d7adb": { 34 | "id": "36d284cb-71ef-4962-8e1e-c174e44d7adb", 35 | "name": "67e3047e-0942-4d55-8443-9f28dc50b660", 36 | "category": "tree", 37 | "title": "tree1", 38 | "description": "", 39 | "properties": {}, 40 | "display": { 41 | "x": 360, 42 | "y": -144 43 | } 44 | }, 45 | "eaab5232-0911-428c-8ef5-41fcb8d506a4": { 46 | "id": "eaab5232-0911-428c-8ef5-41fcb8d506a4", 47 | "name": "Sequence", 48 | "category": "composite", 49 | "title": "Sequence", 50 | "description": "", 51 | "properties": {}, 52 | "display": { 53 | "x": -60, 54 | "y": -144 55 | }, 56 | "children": [ 57 | "8afb402e-6cc1-46bd-8fd0-ea432bf40551" 58 | ] 59 | } 60 | }, 61 | "display": { 62 | "camera_x": 960, 63 | "camera_y": 568.5, 64 | "camera_z": 1, 65 | "x": -264, 66 | "y": -144 67 | } 68 | }, 69 | { 70 | "version": "0.3.0", 71 | "scope": "tree", 72 | "id": "67e3047e-0942-4d55-8443-9f28dc50b660", 73 | "title": "tree1", 74 | "description": "", 75 | "root": "3739a6fd-3205-45df-8f18-ab02f0c4b07e", 76 | "properties": {}, 77 | "nodes": { 78 | "8b514f0a-913e-4eaf-817b-03e85b98efa0": { 79 | "id": "8b514f0a-913e-4eaf-817b-03e85b98efa0", 80 | "name": "Repeater", 81 | "category": "decorator", 82 | "title": "Repeat x", 83 | "description": "", 84 | "properties": { 85 | "maxLoop": 2 86 | }, 87 | "display": { 88 | "x": -144, 89 | "y": -108 90 | }, 91 | "child": "af4ac079-941b-49d6-85f2-11758f76bd26" 92 | }, 93 | "7deef17b-e7fa-4dcb-94c5-979e142c0eee": { 94 | "id": "7deef17b-e7fa-4dcb-94c5-979e142c0eee", 95 | "name": "Log", 96 | "category": "action", 97 | "title": "Log", 98 | "description": "Log", 99 | "properties": { 100 | "info": "Sequence log" 101 | }, 102 | "display": { 103 | "x": -132, 104 | "y": -12 105 | } 106 | }, 107 | "af4ac079-941b-49d6-85f2-11758f76bd26": { 108 | "id": "af4ac079-941b-49d6-85f2-11758f76bd26", 109 | "name": "Log", 110 | "category": "action", 111 | "title": "Log", 112 | "description": "Log", 113 | "properties": { 114 | "info": "repeat *2 log" 115 | }, 116 | "display": { 117 | "x": 120, 118 | "y": -108 119 | } 120 | }, 121 | "2a5b35a6-f8f2-4922-993e-c9a3fa02beea": { 122 | "id": "2a5b35a6-f8f2-4922-993e-c9a3fa02beea", 123 | "name": "Limiter", 124 | "category": "decorator", 125 | "title": "Limit Activations", 126 | "description": "", 127 | "properties": { 128 | "maxLoop": 2 129 | }, 130 | "display": { 131 | "x": -132, 132 | "y": 108 133 | }, 134 | "child": "6fb01fec-8fd7-49d0-b6ce-f9335937bfa3" 135 | }, 136 | "6fb01fec-8fd7-49d0-b6ce-f9335937bfa3": { 137 | "id": "6fb01fec-8fd7-49d0-b6ce-f9335937bfa3", 138 | "name": "Log", 139 | "category": "action", 140 | "title": "Log", 141 | "description": "Log", 142 | "properties": { 143 | "info": "limit log" 144 | }, 145 | "display": { 146 | "x": 108, 147 | "y": 108 148 | } 149 | }, 150 | "3739a6fd-3205-45df-8f18-ab02f0c4b07e": { 151 | "id": "3739a6fd-3205-45df-8f18-ab02f0c4b07e", 152 | "name": "MemSequence", 153 | "category": "composite", 154 | "title": "MemSequence", 155 | "description": "", 156 | "properties": {}, 157 | "display": { 158 | "x": -348, 159 | "y": -24 160 | }, 161 | "children": [ 162 | "8b514f0a-913e-4eaf-817b-03e85b98efa0", 163 | "7deef17b-e7fa-4dcb-94c5-979e142c0eee", 164 | "2a5b35a6-f8f2-4922-993e-c9a3fa02beea" 165 | ] 166 | } 167 | }, 168 | "display": { 169 | "camera_x": 936.5, 170 | "camera_y": 461, 171 | "camera_z": 1, 172 | "x": -504, 173 | "y": -24 174 | } 175 | } 176 | ], 177 | "custom_nodes": [ 178 | { 179 | "version": "0.3.0", 180 | "scope": "node", 181 | "name": "Log", 182 | "category": "action", 183 | "title": "Log", 184 | "description": "Log", 185 | "properties": { 186 | "info": "log..." 187 | } 188 | } 189 | ] 190 | }, 191 | "path": "C:\\Users\\Silence\\Desktop\\aaa.b3" 192 | } -------------------------------------------------------------------------------- /src/test/resources/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "67e3047e-0942-4d55-8443-9f28dc50b660", 3 | "title": "A behavior tree", 4 | "description": "", 5 | "root": "3739a6fd-3205-45df-8f18-ab02f0c4b07e", 6 | "properties": {}, 7 | "nodes": { 8 | "8b514f0a-913e-4eaf-817b-03e85b98efa0": { 9 | "id": "8b514f0a-913e-4eaf-817b-03e85b98efa0", 10 | "name": "Repeater", 11 | "title": "Repeat x", 12 | "description": "", 13 | "properties": { 14 | "maxLoop": 2 15 | }, 16 | "display": { 17 | "x": -144, 18 | "y": -108 19 | }, 20 | "child": "af4ac079-941b-49d6-85f2-11758f76bd26" 21 | }, 22 | "7deef17b-e7fa-4dcb-94c5-979e142c0eee": { 23 | "id": "7deef17b-e7fa-4dcb-94c5-979e142c0eee", 24 | "name": "Log", 25 | "title": "Log", 26 | "description": "Log", 27 | "properties": { 28 | "info": "Sequence log" 29 | }, 30 | "display": { 31 | "x": -144, 32 | "y": -12 33 | } 34 | }, 35 | "af4ac079-941b-49d6-85f2-11758f76bd26": { 36 | "id": "af4ac079-941b-49d6-85f2-11758f76bd26", 37 | "name": "Log", 38 | "title": "Log", 39 | "description": "Log", 40 | "properties": { 41 | "info": "repeat *2 log" 42 | }, 43 | "display": { 44 | "x": 96, 45 | "y": -120 46 | } 47 | }, 48 | "2a5b35a6-f8f2-4922-993e-c9a3fa02beea": { 49 | "id": "2a5b35a6-f8f2-4922-993e-c9a3fa02beea", 50 | "name": "Limiter", 51 | "title": "Limit Activations", 52 | "description": "", 53 | "properties": { 54 | "maxLoop": 2 55 | }, 56 | "display": { 57 | "x": -132, 58 | "y": 108 59 | }, 60 | "child": "6fb01fec-8fd7-49d0-b6ce-f9335937bfa3" 61 | }, 62 | "6fb01fec-8fd7-49d0-b6ce-f9335937bfa3": { 63 | "id": "6fb01fec-8fd7-49d0-b6ce-f9335937bfa3", 64 | "name": "Log", 65 | "title": "Log", 66 | "description": "Log", 67 | "properties": { 68 | "info": "limit log" 69 | }, 70 | "display": { 71 | "x": 108, 72 | "y": 108 73 | } 74 | }, 75 | "3739a6fd-3205-45df-8f18-ab02f0c4b07e": { 76 | "id": "3739a6fd-3205-45df-8f18-ab02f0c4b07e", 77 | "name": "MemSequence", 78 | "title": "MemSequence", 79 | "description": "", 80 | "properties": {}, 81 | "display": { 82 | "x": -348, 83 | "y": -24 84 | }, 85 | "children": [ 86 | "8b514f0a-913e-4eaf-817b-03e85b98efa0", 87 | "7deef17b-e7fa-4dcb-94c5-979e142c0eee", 88 | "2a5b35a6-f8f2-4922-993e-c9a3fa02beea" 89 | ] 90 | } 91 | }, 92 | "display": { 93 | "camera_x": 936.5, 94 | "camera_y": 461, 95 | "camera_z": 1, 96 | "x": -504, 97 | "y": -24 98 | }, 99 | "custom_nodes": [ 100 | { 101 | "name": "Log", 102 | "category": "action", 103 | "title": "Log", 104 | "description": "Log", 105 | "properties": { 106 | "info": "log..." 107 | } 108 | } 109 | ] 110 | } -------------------------------------------------------------------------------- /src/test/resources/tree1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilenceSu/behavior3java/e3c3bcb6fe22adeaa84969a6a49481509a48e7a8/src/test/resources/tree1.png -------------------------------------------------------------------------------- /src/test/resources/tree2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilenceSu/behavior3java/e3c3bcb6fe22adeaa84969a6a49481509a48e7a8/src/test/resources/tree2.png --------------------------------------------------------------------------------