mirror of
https://github.com/gongxh0901/kunpocc-behaviortree.git
synced 2025-12-26 16:48:56 +00:00
1.组合节点全部添加记忆功能;
2.优化并行节点和随机选择节点的逻辑; 3.修复随机选择节点和并行节点关闭时,子节点不能正确关闭的bug
This commit is contained in:
File diff suppressed because one or more lines are too long
286
bt-demo/assets/resources/juese1.atlas
Normal file
286
bt-demo/assets/resources/juese1.atlas
Normal file
@@ -0,0 +1,286 @@
|
||||
|
||||
juese1.png
|
||||
size: 1024,1024
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
右手_1
|
||||
rotate: true
|
||||
xy: 415, 448
|
||||
size: 57, 125
|
||||
orig: 57, 125
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
右眼1
|
||||
rotate: true
|
||||
xy: 417, 110
|
||||
size: 26, 36
|
||||
orig: 28, 38
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
右眼2
|
||||
rotate: false
|
||||
xy: 230, 326
|
||||
size: 40, 49
|
||||
orig: 42, 51
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
右眼上
|
||||
rotate: false
|
||||
xy: 377, 648
|
||||
size: 59, 25
|
||||
orig: 59, 25
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
哭嘴巴
|
||||
rotate: false
|
||||
xy: 367, 110
|
||||
size: 48, 26
|
||||
orig: 48, 26
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
哭眼睛
|
||||
rotate: false
|
||||
xy: 2, 5
|
||||
size: 42, 20
|
||||
orig: 42, 20
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
嘴巴
|
||||
rotate: true
|
||||
xy: 199, 4
|
||||
size: 28, 7
|
||||
orig: 30, 9
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
嘴巴2
|
||||
rotate: false
|
||||
xy: 308, 2
|
||||
size: 34, 18
|
||||
orig: 34, 18
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
头
|
||||
rotate: true
|
||||
xy: 205, 108
|
||||
size: 213, 160
|
||||
orig: 215, 162
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
头发1
|
||||
rotate: true
|
||||
xy: 363, 333
|
||||
size: 112, 107
|
||||
orig: 112, 107
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
头发2
|
||||
rotate: false
|
||||
xy: 455, 8
|
||||
size: 147, 128
|
||||
orig: 147, 128
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
头发3
|
||||
rotate: true
|
||||
xy: 367, 138
|
||||
size: 193, 110
|
||||
orig: 195, 112
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
左眼1
|
||||
rotate: false
|
||||
xy: 438, 651
|
||||
size: 26, 36
|
||||
orig: 28, 38
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
左眼2
|
||||
rotate: false
|
||||
xy: 482, 608
|
||||
size: 40, 48
|
||||
orig: 42, 50
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
左眼上
|
||||
rotate: true
|
||||
xy: 203, 323
|
||||
size: 52, 25
|
||||
orig: 52, 25
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
扫把
|
||||
rotate: false
|
||||
xy: 61, 34
|
||||
size: 142, 287
|
||||
orig: 142, 287
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
抹布
|
||||
rotate: true
|
||||
xy: 377, 842
|
||||
size: 179, 175
|
||||
orig: 179, 175
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
挡
|
||||
rotate: false
|
||||
xy: 391, 11
|
||||
size: 53, 13
|
||||
orig: 53, 13
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
收音机1
|
||||
rotate: false
|
||||
xy: 336, 331
|
||||
size: 25, 114
|
||||
orig: 25, 114
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
收音机2
|
||||
rotate: true
|
||||
xy: 377, 675
|
||||
size: 165, 47
|
||||
orig: 165, 47
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
收音机3
|
||||
rotate: true
|
||||
xy: 46, 7
|
||||
size: 18, 151
|
||||
orig: 18, 151
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
椅子
|
||||
rotate: true
|
||||
xy: 208, 7
|
||||
size: 99, 62
|
||||
orig: 99, 62
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
水印
|
||||
rotate: false
|
||||
xy: 2, 323
|
||||
size: 199, 323
|
||||
orig: 199, 323
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
白眼
|
||||
rotate: false
|
||||
xy: 482, 513
|
||||
size: 45, 45
|
||||
orig: 45, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
相机
|
||||
rotate: true
|
||||
xy: 426, 689
|
||||
size: 151, 108
|
||||
orig: 151, 108
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
相机2
|
||||
rotate: false
|
||||
xy: 262, 447
|
||||
size: 151, 199
|
||||
orig: 151, 199
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
相机光
|
||||
rotate: false
|
||||
xy: 2, 648
|
||||
size: 373, 373
|
||||
orig: 373, 373
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
眉毛右
|
||||
rotate: false
|
||||
xy: 466, 673
|
||||
size: 42, 14
|
||||
orig: 44, 16
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
眉毛左
|
||||
rotate: false
|
||||
xy: 466, 658
|
||||
size: 40, 13
|
||||
orig: 42, 15
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
眉毛皱眉
|
||||
rotate: false
|
||||
xy: 344, 5
|
||||
size: 45, 15
|
||||
orig: 45, 15
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
眼泪1
|
||||
rotate: true
|
||||
xy: 2, 27
|
||||
size: 294, 57
|
||||
orig: 294, 57
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
眼泪2
|
||||
rotate: true
|
||||
xy: 203, 377
|
||||
size: 269, 57
|
||||
orig: 269, 57
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
眼泪3
|
||||
rotate: false
|
||||
xy: 308, 22
|
||||
size: 58, 84
|
||||
orig: 58, 84
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
组 4
|
||||
rotate: false
|
||||
xy: 368, 26
|
||||
size: 75, 82
|
||||
orig: 77, 84
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
腿_1
|
||||
rotate: false
|
||||
xy: 272, 14
|
||||
size: 34, 92
|
||||
orig: 34, 92
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
话筒
|
||||
rotate: false
|
||||
xy: 272, 323
|
||||
size: 62, 122
|
||||
orig: 64, 124
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
身体
|
||||
rotate: false
|
||||
xy: 472, 369
|
||||
size: 73, 77
|
||||
orig: 75, 79
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
阴影
|
||||
rotate: true
|
||||
xy: 415, 507
|
||||
size: 139, 65
|
||||
orig: 141, 67
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
领结
|
||||
rotate: true
|
||||
xy: 482, 560
|
||||
size: 46, 40
|
||||
orig: 48, 42
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
鼻子
|
||||
rotate: true
|
||||
xy: 272, 4
|
||||
size: 8, 12
|
||||
orig: 10, 14
|
||||
offset: 1, 1
|
||||
index: -1
|
||||
12
bt-demo/assets/resources/juese1.atlas.meta
Normal file
12
bt-demo/assets/resources/juese1.atlas.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.0.0",
|
||||
"importer": "*",
|
||||
"imported": true,
|
||||
"uuid": "2a140e27-a3cf-4808-87f1-234d62f6a3af",
|
||||
"files": [
|
||||
".atlas",
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
7830
bt-demo/assets/resources/juese1.json
Normal file
7830
bt-demo/assets/resources/juese1.json
Normal file
File diff suppressed because it is too large
Load Diff
13
bt-demo/assets/resources/juese1.json.meta
Normal file
13
bt-demo/assets/resources/juese1.json.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.2.7",
|
||||
"importer": "spine-data",
|
||||
"imported": true,
|
||||
"uuid": "6aa6ec55-2434-467e-becb-934b6411ff37",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"atlasUuid": "2a140e27-a3cf-4808-87f1-234d62f6a3af"
|
||||
}
|
||||
}
|
||||
BIN
bt-demo/assets/resources/juese1.png
Normal file
BIN
bt-demo/assets/resources/juese1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
42
bt-demo/assets/resources/juese1.png.meta
Normal file
42
bt-demo/assets/resources/juese1.png.meta
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"ver": "1.0.27",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "f3e02866-6afe-4e54-aea5-d742ce999083",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "f3e02866-6afe-4e54-aea5-d742ce999083@6c48a",
|
||||
"displayName": "juese1",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "repeat",
|
||||
"wrapModeT": "repeat",
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0,
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "f3e02866-6afe-4e54-aea5-d742ce999083",
|
||||
"visible": false
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "texture",
|
||||
"hasAlpha": true,
|
||||
"fixAlphaTransparencyArtifacts": false,
|
||||
"redirect": "f3e02866-6afe-4e54-aea5-d742ce999083@6c48a"
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,10 @@
|
||||
"skeleton": {
|
||||
"__id__": 6
|
||||
},
|
||||
"btConfig": null,
|
||||
"btConfig": {
|
||||
"__uuid__": "c8aeef5d-6d0e-4093-848e-7d8f1ca30261",
|
||||
"__expectedType__": "cc.JsonAsset"
|
||||
},
|
||||
"_id": "69LhmWaZRIUpmYvdiN82Ha"
|
||||
},
|
||||
{
|
||||
@@ -226,11 +229,11 @@
|
||||
"a": 255
|
||||
},
|
||||
"_skeletonData": {
|
||||
"__uuid__": "39a7d8cd-533a-479a-b909-9575bf720338",
|
||||
"__uuid__": "6aa6ec55-2434-467e-becb-934b6411ff37",
|
||||
"__expectedType__": "sp.SkeletonData"
|
||||
},
|
||||
"defaultSkin": "default",
|
||||
"defaultAnimation": "idle",
|
||||
"defaultAnimation": "zoulu",
|
||||
"_premultipliedAlpha": true,
|
||||
"_timeScale": 1,
|
||||
"_preCacheMode": 0,
|
||||
@@ -304,13 +307,13 @@
|
||||
"__prefab": null,
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 419.8399963378906,
|
||||
"height": 686.0800170898438
|
||||
"width": 606,
|
||||
"height": 398.8900146484375
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.45412539378136013,
|
||||
"y": 0.011660447470739235
|
||||
"x": 0.2154950434618657,
|
||||
"y": 0.07997191819064561
|
||||
},
|
||||
"_id": "c9XaAZS6pNILxWx4jmSYKE"
|
||||
},
|
||||
|
||||
@@ -116,3 +116,16 @@ export class BTCondition extends BT.ConditionDecorator {
|
||||
return Math.random() > this._value;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////自定义行为树////////////////////////////////////
|
||||
|
||||
/** 爆发状态条件节点 */
|
||||
@BT.ClassCondition("BTConditionTest", { name: "爆发状态条件节点", group: "基础条件节点", desc: "" })
|
||||
export class BTConditionInCrit extends BT.Condition {
|
||||
|
||||
public isEligible(): boolean {
|
||||
let isCrit: boolean = this.get("isCrit")
|
||||
// console.log("是否处于爆发状态?", isCrit)
|
||||
return isCrit;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export class GameEntry extends Component {
|
||||
private _tree: BT.BehaviorTree<sp.Skeleton> = null;
|
||||
start(): void {
|
||||
console.log("btConfig", this.btConfig);
|
||||
let btTree1: BT.INodeConfig[] = this.btConfig.json["bt-tree1"]
|
||||
let btTree1: BT.INodeConfig[] = this.btConfig.json["actor2"]
|
||||
this._tree = BT.createBehaviorTree(btTree1, this.skeleton);
|
||||
}
|
||||
|
||||
|
||||
481
bt-demo/extensions-config/bt-editor/actor2.json
Normal file
481
bt-demo/extensions-config/bt-editor/actor2.json
Normal file
@@ -0,0 +1,481 @@
|
||||
{
|
||||
"name": "actor2",
|
||||
"description": "",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "1760666774273_6twatctr8",
|
||||
"className": "Selector",
|
||||
"name": "选择节点",
|
||||
"position": {
|
||||
"x": -60,
|
||||
"y": -220
|
||||
},
|
||||
"parameters": {},
|
||||
"children": [
|
||||
"1760666778409_kl1cri6xp",
|
||||
"1760668712628_4untk0pc1",
|
||||
"1760668771864_xs1f57m7t"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760666778409_kl1cri6xp",
|
||||
"className": "Sequence",
|
||||
"name": "顺序节点",
|
||||
"position": {
|
||||
"x": -620,
|
||||
"y": -120
|
||||
},
|
||||
"parameters": {},
|
||||
"children": [
|
||||
"1760666838184_0sf89zrd4",
|
||||
"1760666881111_eu9y7q5m6"
|
||||
],
|
||||
"alias": "待机顺序节点"
|
||||
},
|
||||
{
|
||||
"id": "1760666838184_0sf89zrd4",
|
||||
"className": "BTConditionRandom",
|
||||
"name": "随机条件节点",
|
||||
"position": {
|
||||
"x": -680,
|
||||
"y": -60
|
||||
},
|
||||
"parameters": {
|
||||
"_value": 0.6
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760666881111_eu9y7q5m6",
|
||||
"className": "LimitTime",
|
||||
"name": "时间限制器",
|
||||
"position": {
|
||||
"x": -540,
|
||||
"y": -40
|
||||
},
|
||||
"parameters": {
|
||||
"_max": 4
|
||||
},
|
||||
"children": [
|
||||
"1760666881111_qoyqb9mkm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760666881111_qoyqb9mkm",
|
||||
"className": "BTAnimation",
|
||||
"name": "播放动画",
|
||||
"position": {
|
||||
"x": -540,
|
||||
"y": 40
|
||||
},
|
||||
"parameters": {
|
||||
"_name": "daiji",
|
||||
"_loop": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760668712628_4untk0pc1",
|
||||
"className": "Sequence",
|
||||
"name": "顺序节点",
|
||||
"position": {
|
||||
"x": -340,
|
||||
"y": -120
|
||||
},
|
||||
"parameters": {},
|
||||
"children": [
|
||||
"1760668729192_kuio9twvr",
|
||||
"1760668729192_pvcbrqpga"
|
||||
],
|
||||
"alias": "走路顺序节点"
|
||||
},
|
||||
{
|
||||
"id": "1760668729192_kuio9twvr",
|
||||
"className": "BTConditionRandom",
|
||||
"name": "随机条件节点",
|
||||
"position": {
|
||||
"x": -400,
|
||||
"y": -60
|
||||
},
|
||||
"parameters": {
|
||||
"_value": 0.6
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760668729192_pvcbrqpga",
|
||||
"className": "LimitTime",
|
||||
"name": "时间限制器",
|
||||
"position": {
|
||||
"x": -260,
|
||||
"y": -40
|
||||
},
|
||||
"parameters": {
|
||||
"_max": 4
|
||||
},
|
||||
"children": [
|
||||
"1760668729192_6f5mhy8o5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760668729192_6f5mhy8o5",
|
||||
"className": "BTAnimation",
|
||||
"name": "播放动画",
|
||||
"position": {
|
||||
"x": -260,
|
||||
"y": 40
|
||||
},
|
||||
"parameters": {
|
||||
"_name": "zoulu",
|
||||
"_loop": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760668771864_s2nrgxu59",
|
||||
"className": "BTAnimation",
|
||||
"name": "播放动画",
|
||||
"position": {
|
||||
"x": -120,
|
||||
"y": 40
|
||||
},
|
||||
"parameters": {
|
||||
"_name": "shouyin1",
|
||||
"_loop": false
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760668771864_ryllyy413",
|
||||
"className": "BTAnimation",
|
||||
"name": "播放动画",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": 40
|
||||
},
|
||||
"parameters": {
|
||||
"_name": "shouyin2",
|
||||
"_loop": false
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760668771864_36a49lgd5",
|
||||
"className": "BTAnimation",
|
||||
"name": "播放动画",
|
||||
"position": {
|
||||
"x": 160,
|
||||
"y": 40
|
||||
},
|
||||
"parameters": {
|
||||
"_name": "dasao1",
|
||||
"_loop": false
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760668771864_xs1f57m7t",
|
||||
"className": "RandomSelector",
|
||||
"name": "随机选择节点",
|
||||
"position": {
|
||||
"x": 220,
|
||||
"y": -120
|
||||
},
|
||||
"parameters": {},
|
||||
"children": [
|
||||
"1760669249155_dd86swcyd",
|
||||
"1760669282323_w8e5afa7f",
|
||||
"1760669285498_33g4ibw4s",
|
||||
"1760669288762_vavcm4qce",
|
||||
"1760669292090_vri0pwmjl",
|
||||
"1760669293676_4iizzuu5y"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760668771864_v45om2x5m",
|
||||
"className": "BTAnimation",
|
||||
"name": "播放动画",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": 40
|
||||
},
|
||||
"parameters": {
|
||||
"_name": "dasao2",
|
||||
"_loop": false
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760668771864_l7co5r94f",
|
||||
"className": "BTAnimation",
|
||||
"name": "播放动画",
|
||||
"position": {
|
||||
"x": 440,
|
||||
"y": 40
|
||||
},
|
||||
"parameters": {
|
||||
"_name": "sheying1",
|
||||
"_loop": false
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760668771864_mve2r80e3",
|
||||
"className": "BTAnimation",
|
||||
"name": "播放动画",
|
||||
"position": {
|
||||
"x": 580,
|
||||
"y": 40
|
||||
},
|
||||
"parameters": {
|
||||
"_name": "sheying2",
|
||||
"_loop": false
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "1760669249155_dd86swcyd",
|
||||
"className": "WeightDecorator",
|
||||
"name": "权重装饰节点",
|
||||
"position": {
|
||||
"x": -120,
|
||||
"y": -40
|
||||
},
|
||||
"parameters": {
|
||||
"_weight": 1
|
||||
},
|
||||
"children": [
|
||||
"1760668771864_s2nrgxu59"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760669282323_w8e5afa7f",
|
||||
"className": "WeightDecorator",
|
||||
"name": "权重装饰节点",
|
||||
"position": {
|
||||
"x": 20,
|
||||
"y": -40
|
||||
},
|
||||
"parameters": {
|
||||
"_weight": 1
|
||||
},
|
||||
"children": [
|
||||
"1760668771864_ryllyy413"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760669285498_33g4ibw4s",
|
||||
"className": "WeightDecorator",
|
||||
"name": "权重装饰节点",
|
||||
"position": {
|
||||
"x": 160,
|
||||
"y": -40
|
||||
},
|
||||
"parameters": {
|
||||
"_weight": 1
|
||||
},
|
||||
"children": [
|
||||
"1760668771864_36a49lgd5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760669288762_vavcm4qce",
|
||||
"className": "WeightDecorator",
|
||||
"name": "权重装饰节点",
|
||||
"position": {
|
||||
"x": 300,
|
||||
"y": -40
|
||||
},
|
||||
"parameters": {
|
||||
"_weight": 1
|
||||
},
|
||||
"children": [
|
||||
"1760668771864_v45om2x5m"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760669292090_vri0pwmjl",
|
||||
"className": "WeightDecorator",
|
||||
"name": "权重装饰节点",
|
||||
"position": {
|
||||
"x": 440,
|
||||
"y": -40
|
||||
},
|
||||
"parameters": {
|
||||
"_weight": 1
|
||||
},
|
||||
"children": [
|
||||
"1760668771864_l7co5r94f"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1760669293676_4iizzuu5y",
|
||||
"className": "WeightDecorator",
|
||||
"name": "权重装饰节点",
|
||||
"position": {
|
||||
"x": 580,
|
||||
"y": -40
|
||||
},
|
||||
"parameters": {
|
||||
"_weight": 1
|
||||
},
|
||||
"children": [
|
||||
"1760668771864_mve2r80e3"
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"id": "conn_1760666871430_poh4dvjwi",
|
||||
"sourceNodeId": "1760666778409_kl1cri6xp",
|
||||
"targetNodeId": "1760666838184_0sf89zrd4",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760666881111_a8i6ryu0v",
|
||||
"sourceNodeId": "1760666881111_eu9y7q5m6",
|
||||
"targetNodeId": "1760666881111_qoyqb9mkm",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760666914926_34fuxnrwc",
|
||||
"sourceNodeId": "1760666778409_kl1cri6xp",
|
||||
"targetNodeId": "1760666881111_eu9y7q5m6",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760668704512_mpippypy6",
|
||||
"sourceNodeId": "1760666774273_6twatctr8",
|
||||
"targetNodeId": "1760666778409_kl1cri6xp",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760668729192_qt1bumeu7",
|
||||
"sourceNodeId": "1760668729192_pvcbrqpga",
|
||||
"targetNodeId": "1760668729192_6f5mhy8o5",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760668731720_c2a4cu2iz",
|
||||
"sourceNodeId": "1760668712628_4untk0pc1",
|
||||
"targetNodeId": "1760668729192_kuio9twvr",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760668733320_y6s636wtc",
|
||||
"sourceNodeId": "1760668712628_4untk0pc1",
|
||||
"targetNodeId": "1760668729192_pvcbrqpga",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760668756520_yxs004ghe",
|
||||
"sourceNodeId": "1760666774273_6twatctr8",
|
||||
"targetNodeId": "1760668712628_4untk0pc1",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669254025_9of7apgsm",
|
||||
"sourceNodeId": "1760668771864_xs1f57m7t",
|
||||
"targetNodeId": "1760669249155_dd86swcyd",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669265721_rbxmewqrx",
|
||||
"sourceNodeId": "1760669249155_dd86swcyd",
|
||||
"targetNodeId": "1760668771864_s2nrgxu59",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669297425_93y6h29te",
|
||||
"sourceNodeId": "1760669282323_w8e5afa7f",
|
||||
"targetNodeId": "1760668771864_ryllyy413",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669300401_wj5smi6q5",
|
||||
"sourceNodeId": "1760669285498_33g4ibw4s",
|
||||
"targetNodeId": "1760668771864_36a49lgd5",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669303074_2zcgl7tmh",
|
||||
"sourceNodeId": "1760669288762_vavcm4qce",
|
||||
"targetNodeId": "1760668771864_v45om2x5m",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669305922_el07uklys",
|
||||
"sourceNodeId": "1760669292090_vri0pwmjl",
|
||||
"targetNodeId": "1760668771864_l7co5r94f",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669308449_h0p002ff4",
|
||||
"sourceNodeId": "1760669293676_4iizzuu5y",
|
||||
"targetNodeId": "1760668771864_mve2r80e3",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669311177_631n86t83",
|
||||
"sourceNodeId": "1760668771864_xs1f57m7t",
|
||||
"targetNodeId": "1760669282323_w8e5afa7f",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669313009_n87mb2ovs",
|
||||
"sourceNodeId": "1760668771864_xs1f57m7t",
|
||||
"targetNodeId": "1760669285498_33g4ibw4s",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669314577_967btvjvq",
|
||||
"sourceNodeId": "1760668771864_xs1f57m7t",
|
||||
"targetNodeId": "1760669288762_vavcm4qce",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669316441_h73u37spc",
|
||||
"sourceNodeId": "1760668771864_xs1f57m7t",
|
||||
"targetNodeId": "1760669292090_vri0pwmjl",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760669318825_j3noil5ka",
|
||||
"sourceNodeId": "1760668771864_xs1f57m7t",
|
||||
"targetNodeId": "1760669293676_4iizzuu5y",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
},
|
||||
{
|
||||
"id": "conn_1760670761598_u2fpnqus3",
|
||||
"sourceNodeId": "1760666774273_6twatctr8",
|
||||
"targetNodeId": "1760668771864_xs1f57m7t",
|
||||
"sourcePointType": "child",
|
||||
"targetPointType": "parent"
|
||||
}
|
||||
],
|
||||
"canvasScale": 0.9294397018150369,
|
||||
"canvasOffset": {
|
||||
"x": 713.7056029818497,
|
||||
"y": 429.08390345073457
|
||||
}
|
||||
}
|
||||
@@ -486,9 +486,9 @@
|
||||
"targetPointType": "parent"
|
||||
}
|
||||
],
|
||||
"canvasScale": 1.0893125857312862,
|
||||
"canvasScale": 0.7146979874982969,
|
||||
"canvasOffset": {
|
||||
"x": 1076.4697723606591,
|
||||
"y": 543.3385223005863
|
||||
"x": 845.1442956244039,
|
||||
"y": 486.99278521877983
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "test-bttree",
|
||||
"description": "",
|
||||
"nodes": [],
|
||||
"connections": [],
|
||||
"canvasScale": 1,
|
||||
"canvasOffset": {
|
||||
"x": 723,
|
||||
"y": 531.5
|
||||
}
|
||||
}
|
||||
14
bt-demo/settings/v2/packages/bt-editor.json
Normal file
14
bt-demo/settings/v2/packages/bt-editor.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"__version__": "1.0.1",
|
||||
"panelSize": {
|
||||
"width": 1800,
|
||||
"height": 1172,
|
||||
"timestamp": 1759221857309
|
||||
},
|
||||
"panel": {
|
||||
"size": {
|
||||
"width": 2056,
|
||||
"height": 900
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kunpocc-behaviortree",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.5",
|
||||
"description": "行为树",
|
||||
"main": "./dist/kunpocc-behaviortree.cjs",
|
||||
"module": "./dist/kunpocc-behaviortree.mjs",
|
||||
|
||||
@@ -38,6 +38,14 @@ export interface IBTNode {
|
||||
|
||||
/** 获取关联的实体 */
|
||||
getEntity<T>(): T;
|
||||
|
||||
/**
|
||||
* 递归清理子节点的打开状态
|
||||
* 用途:
|
||||
* 1.装饰节点的子节点未关闭,但是装饰节点关闭时,需要清理子节点的打开状态
|
||||
* 2.组合节点关闭,但是子节点可能还在运行,需要清理子节点的打开状态
|
||||
*/
|
||||
cleanupChild(): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,17 +100,16 @@ export abstract class BTNode implements IBTNode {
|
||||
*/
|
||||
protected open(): void { }
|
||||
protected close(): void { }
|
||||
/**
|
||||
* 清理子节点的打开状态
|
||||
* 一般用于装饰节点的非子节点关闭时, 用来清理子节点的打开状态
|
||||
*/
|
||||
protected cleanupChild(): void {
|
||||
const child = this.children[0];
|
||||
if (child && this._local.openNodes.has(child)) {
|
||||
|
||||
public cleanupChild(): void {
|
||||
for (const child of this.children) {
|
||||
if (this._local.openNodes.has(child)) {
|
||||
child.cleanupChild();
|
||||
this._local.openNodes.delete(child);
|
||||
(child as BTNode).close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行节点逻辑
|
||||
|
||||
@@ -7,11 +7,22 @@ import { WeightDecorator } from "./Decorator";
|
||||
/**
|
||||
* 组合节点基类
|
||||
* 有多个子节点
|
||||
*
|
||||
* 组合节点全部都有记忆能力
|
||||
*/
|
||||
export abstract class Composite extends BTNode {
|
||||
constructor(...children: IBTNode[]) {
|
||||
super(children);
|
||||
}
|
||||
|
||||
public override _initialize(global: IBlackboard, branch: IBlackboard): void {
|
||||
super._initialize(global, branch);
|
||||
this._local = branch.createChild();
|
||||
}
|
||||
|
||||
protected override open(): void {
|
||||
this.set(`__nRunningIndex`, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,16 +34,6 @@ export abstract class Composite extends BTNode {
|
||||
*/
|
||||
@BT.ClassComposite("Selector", { name: "选择节点", group: "基础组合节点", desc: "子节点从左到右执行, 子节点状态: 成功则选择成立, 失败继续下一个, 执行中则返回执行中, 下次从该节点开始" })
|
||||
export class Selector extends Composite {
|
||||
public override _initialize(global: IBlackboard, branch: IBlackboard): void {
|
||||
super._initialize(global, branch);
|
||||
this._local = branch.createChild();
|
||||
}
|
||||
|
||||
protected override open(): void {
|
||||
super.open();
|
||||
this.set(`__nRunningIndex`, 0);
|
||||
}
|
||||
|
||||
public tick(dt: number): Status {
|
||||
let index = this.get<number>(`__nRunningIndex`);
|
||||
for (let i = index; i < this.children.length; i++) {
|
||||
@@ -59,16 +60,6 @@ export class Selector extends Composite {
|
||||
*/
|
||||
@BT.ClassComposite("Sequence", { name: "顺序节点", group: "基础组合节点", desc: "子节点从左到右执行, 子节点状态: 成功则继续下一个, 失败则停止迭代返回失败, 执行中返回执行中, 下次从该节点开始" })
|
||||
export class Sequence extends Composite {
|
||||
public override _initialize(global: IBlackboard, branch: IBlackboard): void {
|
||||
super._initialize(global, branch);
|
||||
this._local = branch.createChild();
|
||||
}
|
||||
|
||||
protected override open(): void {
|
||||
super.open();
|
||||
this.set(`__nRunningIndex`, 0);
|
||||
}
|
||||
|
||||
public tick(dt: number): Status {
|
||||
let index = this.get<number>(`__nRunningIndex`);
|
||||
for (let i = index; i < this.children.length; i++) {
|
||||
@@ -87,22 +78,49 @@ export class Sequence extends Composite {
|
||||
}
|
||||
|
||||
/**
|
||||
* 并行节点 从左到右依次执行所有子节点
|
||||
* 注意:这里的"并行"是逻辑概念,实际是顺序执行
|
||||
* 并行节点 从左到右依次执行所有子节点 必定全部执行一遍
|
||||
* 返回优先级 FAILURE > RUNNING > SUCCESS
|
||||
* 注意:这里的"并行"是逻辑概念,实际是顺序执行
|
||||
*
|
||||
* 记忆机制: 已经失败的子节点不会重复执行,只执行运行中的子节点
|
||||
*/
|
||||
@BT.ClassComposite("Parallel", { name: "并行节点", group: "基础组合节点", desc: "依次执行所有子节点(从左到右), 子节点状态: 任意失败则失败 > 任意执行中则执行中 > 全部成功则成功" })
|
||||
export class Parallel extends Composite {
|
||||
protected override open(): void {
|
||||
super.open();
|
||||
// 初始化所有子节点状态为RUNNING
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
this.set(`__childStatus_${i}`, Status.RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
public tick(dt: number): Status {
|
||||
let result = Status.SUCCESS;
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
let status = this.children[i]!._execute(dt);
|
||||
if (result === Status.FAILURE || status === Status.FAILURE) {
|
||||
// 获取该子节点的缓存状态
|
||||
let status = this.get<Status>(`__childStatus_${i}`);
|
||||
|
||||
// 只执行还在运行中的子节点
|
||||
if (status === Status.RUNNING) {
|
||||
status = this.children[i]!._execute(dt);
|
||||
// 缓存子节点状态
|
||||
if (status !== Status.FAILURE && result !== Status.FAILURE) {
|
||||
this.set(`__childStatus_${i}`, status);
|
||||
}
|
||||
}
|
||||
|
||||
// 优先级: FAILURE > RUNNING > SUCCESS
|
||||
// 必须执行完所有子节点,不能提前终止
|
||||
if (status === Status.FAILURE) {
|
||||
result = Status.FAILURE;
|
||||
} else if (status === Status.RUNNING) {
|
||||
} else if (status === Status.RUNNING && result !== Status.FAILURE) {
|
||||
result = Status.RUNNING;
|
||||
}
|
||||
}
|
||||
// 所有子节点执行完毕后,如果不是RUNNING则清理
|
||||
if (result !== Status.RUNNING) {
|
||||
this.cleanupChild();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -110,7 +128,7 @@ export class Parallel extends Composite {
|
||||
|
||||
/**
|
||||
* 随机选择节点
|
||||
* 随机选择一个子节点执行
|
||||
* 随机选择一个子节点执行,支持权重
|
||||
* 返回子节点状态
|
||||
*/
|
||||
@BT.ClassComposite("RandomSelector", { name: "随机选择节点", group: "基础组合节点", desc: "随机选择一个子节点执行, 返回子节点状态" })
|
||||
@@ -124,14 +142,26 @@ export class RandomSelector extends Composite {
|
||||
this._weights = [];
|
||||
|
||||
for (const child of children) {
|
||||
const weight = this.getChildWeight(child);
|
||||
const weight = (child instanceof WeightDecorator) ? child.weight : 1;
|
||||
this._totalWeight += weight;
|
||||
this._weights.push(this._totalWeight);
|
||||
}
|
||||
}
|
||||
|
||||
private getChildWeight(child: IBTNode): number {
|
||||
return (child instanceof WeightDecorator) ? (child.weight) : 1;
|
||||
protected override open(): void {
|
||||
this.set(`__nRunningIndex`, -1);
|
||||
}
|
||||
|
||||
/** 根据权重随机选择子节点索引 */
|
||||
private selectRandomIndex(): number {
|
||||
const randomValue = Math.random() * this._totalWeight;
|
||||
// 线性查找(行为树子节点数量通常很少,性能足够)
|
||||
for (let i = 0; i < this._weights.length; i++) {
|
||||
if (randomValue < this._weights[i]!) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return this._weights.length - 1;
|
||||
}
|
||||
|
||||
public tick(dt: number): Status {
|
||||
@@ -139,45 +169,67 @@ export class RandomSelector extends Composite {
|
||||
return Status.FAILURE;
|
||||
}
|
||||
|
||||
// 基于权重的随机选择
|
||||
const randomValue = Math.random() * this._totalWeight;
|
||||
|
||||
// 使用二分查找找到对应的子节点索引(O(log n)复杂度)
|
||||
let left = 0;
|
||||
let right = this._weights.length - 1;
|
||||
let childIndex = 0;
|
||||
|
||||
while (left <= right) {
|
||||
const mid = Math.floor((left + right) / 2);
|
||||
if (this._weights[mid]! > randomValue) {
|
||||
childIndex = mid;
|
||||
right = mid - 1;
|
||||
} else {
|
||||
left = mid + 1;
|
||||
// 获取或选择子节点索引
|
||||
let index = this.get<number>(`__nRunningIndex`);
|
||||
if (index === -1) {
|
||||
index = this.selectRandomIndex();
|
||||
}
|
||||
|
||||
// 执行选中的子节点
|
||||
const status = this.children[index]!._execute(dt);
|
||||
|
||||
// 如果返回RUNNING,记录索引以便下次继续执行
|
||||
if (status === Status.RUNNING) {
|
||||
this.set(`__nRunningIndex`, index);
|
||||
}
|
||||
const status = this.children[childIndex]!._execute(dt);
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 并行任意成功节点 从左到右依次执行所有子节点
|
||||
* 注意:这里的"并行"是逻辑概念,实际是顺序执行
|
||||
* 并行任意成功节点 从左到右依次执行所有子节点 必定全部执行一遍
|
||||
* 返回优先级 SUCCESS > RUNNING > FAILURE
|
||||
* 注意:这里的"并行"是逻辑概念,实际是顺序执行
|
||||
*
|
||||
* 记忆机制: 已经失败的子节点不会重复执行,只执行运行中的子节点
|
||||
*/
|
||||
@BT.ClassComposite("ParallelAnySuccess", { name: "并行任意成功节点", group: "基础组合节点", desc: "依次执行所有子节点(从左到右), 任意一个成功则成功 > 任意一个执行中则执行中 > 全部失败则失败" })
|
||||
export class ParallelAnySuccess extends Composite {
|
||||
protected override open(): void {
|
||||
super.open();
|
||||
// 初始化所有子节点状态为RUNNING
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
this.set(`__childStatus_${i}`, Status.RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
public tick(dt: number): Status {
|
||||
let result = Status.FAILURE;
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
let status = this.children[i]!._execute(dt);
|
||||
if (result === Status.SUCCESS || status === Status.SUCCESS) {
|
||||
// 获取该子节点的缓存状态
|
||||
let status = this.get<Status>(`__childStatus_${i}`);
|
||||
|
||||
// 只执行还在运行中的子节点
|
||||
if (status === Status.RUNNING) {
|
||||
status = this.children[i]!._execute(dt);
|
||||
// 缓存子节点状态
|
||||
if (status !== Status.SUCCESS && result !== Status.SUCCESS) {
|
||||
this.set(`__childStatus_${i}`, status);
|
||||
}
|
||||
}
|
||||
|
||||
// 优先级: SUCCESS > RUNNING > FAILURE
|
||||
if (status === Status.SUCCESS) {
|
||||
result = Status.SUCCESS;
|
||||
} else if (status === Status.RUNNING) {
|
||||
} else if (status === Status.RUNNING && result !== Status.SUCCESS) {
|
||||
result = Status.RUNNING;
|
||||
}
|
||||
}
|
||||
// 所有子节点执行完毕后,如果不是RUNNING则清理
|
||||
if (result !== Status.RUNNING) {
|
||||
this.cleanupChild();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ export abstract class ConditionDecorator extends Decorator {
|
||||
if (this.isEligible()) {
|
||||
return this.children[0]!._execute(dt);
|
||||
}
|
||||
this.cleanupChild();
|
||||
return Status.FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user