Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
895629a007 | ||
|
|
9d0261fcd7 | ||
|
|
1c1bb56bb6 | ||
|
|
ae20ca0e78 | ||
|
|
9a4b44694a | ||
|
|
6e3db26b33 | ||
|
|
6f9097f73d | ||
|
|
2f4176857e | ||
|
|
dfe1f40b58 | ||
|
|
88cf9008ff | ||
|
|
5a4a49c782 | ||
|
|
0df12f4154 |
No files matched your search
@@ -1,3 +1,9 @@
|
|||||||
|
# vscode 插件
|
||||||
|
|
||||||
|
[Behavior3 Editor](https://marketplace.visualstudio.com/items?itemName=codetypess.vscode-behavior3)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
# 行为树编辑器
|
# 行为树编辑器
|
||||||
|
|
||||||
这是一个直观、可视化、通用的行为树编辑器,行为树的保存格式为 json,可以让策划自行去实现 AI,技能,buff 等复杂的游戏逻辑,从而减少不必要的沟通成本和提升开发效率。
|
这是一个直观、可视化、通用的行为树编辑器,行为树的保存格式为 json,可以让策划自行去实现 AI,技能,buff 等复杂的游戏逻辑,从而减少不必要的沟通成本和提升开发效率。
|
||||||
@@ -83,7 +89,8 @@ export interface NodeDef {
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm install # 安装依赖
|
npm install # 安装依赖
|
||||||
npm start # 运行测试
|
npm test # 运行测试
|
||||||
|
npm run dev # 启动开发环境
|
||||||
npm run build # 编译可执行文件
|
npm run build # 编译可执行文件
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -97,7 +104,7 @@ npm run build # 编译可执行文件
|
|||||||
## 示例行为树框架
|
## 示例行为树框架
|
||||||
|
|
||||||
- lua 版本 [behavior3lua](https://github.com/zhandouxiaojiji/behavior3lua)
|
- lua 版本 [behavior3lua](https://github.com/zhandouxiaojiji/behavior3lua)
|
||||||
- js/ts 版本 [behavior3-ts](https://github.com/zhongfq/behavior3-ts)。
|
- js/ts 版本 [behavior3-ts](https://github.com/codetypess/behavior3-ts)。
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
#include "baby_idle_behavior.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_random.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
|
||||||
|
static const char* TAG = "BabyIdleBehavior";
|
||||||
|
|
||||||
|
namespace sweekar {
|
||||||
|
namespace demo7 {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr int64_t kFirstRandomIdleDelayUs = 5000000; // 5s after entering idle
|
||||||
|
constexpr int64_t kRandomIdleCooldownUs = 7000000; // 7s after a random idle script
|
||||||
|
constexpr const char* kBaseIdleScript = "baby/idle/look_around";
|
||||||
|
constexpr const char* kCuriousIdleScript = "baby/interaction/curious";
|
||||||
|
constexpr const char* kShyIdleScript = "baby/interaction/shy";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void BabyIdleBehavior::OnEnter(IPetContext* ctx) {
|
||||||
|
PlayBaseIdle(ctx);
|
||||||
|
ScheduleRandomIdle(kFirstRandomIdleDelayUs);
|
||||||
|
phase_ = Phase::WAITING_RANDOM_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BabyIdleBehavior::OnExit(IPetContext* ctx) {
|
||||||
|
ctx->StopScript();
|
||||||
|
next_random_idle_us_ = 0;
|
||||||
|
phase_ = Phase::PLAYING_BASE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BabyIdleBehavior::HandleEvent(IPetContext* ctx, const Event& evt) {
|
||||||
|
switch (evt.type) {
|
||||||
|
case EventType::EVT_TOUCH_TAP:
|
||||||
|
ESP_LOGI(TAG, "Baby got touched. Play surprised.");
|
||||||
|
ctx->PlayScript("interaction/surprised");
|
||||||
|
phase_ = Phase::PLAYING_RANDOM_IDLE;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case EventType::EVT_KEY_LEFT_CLICK:
|
||||||
|
ESP_LOGI(TAG, "Left clicked in BabyIdle. Opening Baby Menu.");
|
||||||
|
ctx->RequestModeSwitch(PetModeType::MODE_MENU);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case EventType::EVT_KEY_RIGHT_CLICK:
|
||||||
|
ESP_LOGI(TAG, "Right clicked in BabyIdle. Learn to talk.");
|
||||||
|
ctx->PlayScript("interaction/learned_to_talk");
|
||||||
|
phase_ = Phase::PLAYING_RANDOM_IDLE;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case EventType::EVT_GYRO_SHAKE:
|
||||||
|
ESP_LOGI(TAG, "Baby shaken. Play dizzy.");
|
||||||
|
ctx->PlayScript("interaction/dizzy");
|
||||||
|
phase_ = Phase::PLAYING_RANDOM_IDLE;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case EventType::EVT_TOUCH_SWIPE_LEFT:
|
||||||
|
ESP_LOGI(TAG, "Baby touch swipe left. Play hurt.");
|
||||||
|
ctx->PlayScript("baby/interaction/hurt");
|
||||||
|
phase_ = Phase::PLAYING_RANDOM_IDLE;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case EventType::EVT_TOUCH_SWIPE_RIGHT:
|
||||||
|
ESP_LOGI(TAG, "Baby touch swipe right. Play curious.");
|
||||||
|
ctx->PlayScript(kCuriousIdleScript);
|
||||||
|
phase_ = Phase::PLAYING_RANDOM_IDLE;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case EventType::EVT_SCRIPT_DONE:
|
||||||
|
ESP_LOGI(TAG, "Baby idle script done.");
|
||||||
|
PlayBaseIdle(ctx);
|
||||||
|
if (phase_ == Phase::PLAYING_RANDOM_IDLE) {
|
||||||
|
ScheduleRandomIdle(kRandomIdleCooldownUs);
|
||||||
|
phase_ = Phase::COOLDOWN_AFTER_RANDOM;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BabyIdleBehavior::OnTick(IPetContext* ctx) {
|
||||||
|
if (!IsRandomIdleDue()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayRandomIdle(ctx);
|
||||||
|
phase_ = Phase::PLAYING_RANDOM_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BabyIdleBehavior::PlayBaseIdle(IPetContext* ctx) {
|
||||||
|
ESP_LOGI(TAG, "Play base idle script: %s", kBaseIdleScript);
|
||||||
|
ctx->PlayScript(kBaseIdleScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BabyIdleBehavior::PlayRandomIdle(IPetContext* ctx) {
|
||||||
|
uint32_t roll = esp_random() % 100;
|
||||||
|
const char* script = roll < 50 ? kCuriousIdleScript : kShyIdleScript;
|
||||||
|
ESP_LOGI(TAG, "Play random idle script: %s, roll=%lu", script, static_cast<unsigned long>(roll));
|
||||||
|
ctx->PlayScript(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BabyIdleBehavior::ScheduleRandomIdle(int64_t delay_us) {
|
||||||
|
next_random_idle_us_ = esp_timer_get_time() + delay_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BabyIdleBehavior::IsRandomIdleDue() const {
|
||||||
|
if (phase_ != Phase::WAITING_RANDOM_IDLE && phase_ != Phase::COOLDOWN_AFTER_RANDOM) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return next_random_idle_us_ > 0 && esp_timer_get_time() >= next_random_idle_us_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace demo7
|
||||||
|
} // namespace sweekar
|
||||||
@@ -0,0 +1,443 @@
|
|||||||
|
{
|
||||||
|
"$override": {
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
},
|
||||||
|
"desc": "宝宝待机行为树 v2:Selector 优先级级联 + 效用曲线 + 双层脑信号(WARNING/CRITICAL)。优先级:精力耗尽 > 饥饿 > 悲伤 > 开心 > 好奇 > 默认待机。",
|
||||||
|
"export": true,
|
||||||
|
"group": [
|
||||||
|
],
|
||||||
|
"import": [
|
||||||
|
],
|
||||||
|
"name": "example",
|
||||||
|
"prefix": "",
|
||||||
|
"root": {
|
||||||
|
"$id": "root001",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "idleRepeat001",
|
||||||
|
"args": {
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "idleSeq001",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "idleWaitRhythm",
|
||||||
|
"args": {
|
||||||
|
"random": 1.5,
|
||||||
|
"time": 2
|
||||||
|
},
|
||||||
|
"id": "40",
|
||||||
|
"name": "Wait"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "idleSelector001",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "exhaustedSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "exhaustedCheck",
|
||||||
|
"args": { "attr": "energy", "op": "<=", "value": 15 },
|
||||||
|
"id": "43",
|
||||||
|
"name": "CheckPhysio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "exhaustedScript",
|
||||||
|
"args": { "script": "baby/need/sleepy" },
|
||||||
|
"id": "44",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "42",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "hungrySeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "hungryCheck",
|
||||||
|
"args": { "attr": "fullness", "op": "<=", "value": 25 },
|
||||||
|
"id": "46",
|
||||||
|
"name": "CheckPhysio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "hungryScript",
|
||||||
|
"args": { "script": "baby/need/hungry" },
|
||||||
|
"id": "48",
|
||||||
|
"name": "PlayScript"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "hungryMoodDrop",
|
||||||
|
"args": { "attr": "happiness", "amount": -2 },
|
||||||
|
"id": "49",
|
||||||
|
"name": "AddPhysio"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "45",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "sadSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "sadCheck",
|
||||||
|
"args": { "attr": "sad", "op": ">", "value": 0.5 },
|
||||||
|
"id": "51",
|
||||||
|
"name": "CheckUtility"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "sadScript",
|
||||||
|
"args": { "script": "baby/need/sad_mild" },
|
||||||
|
"id": "52",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "50",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "laughSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "laughCheck",
|
||||||
|
"args": { "attr": "laugh", "op": ">", "value": 0.5 },
|
||||||
|
"id": "54",
|
||||||
|
"name": "CheckUtility"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "laughScript",
|
||||||
|
"args": { "script": "baby/interaction/laugh" },
|
||||||
|
"id": "55",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "53",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "curiousSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "curiousCheck",
|
||||||
|
"args": { "attr": "curious", "op": ">", "value": 0.4 },
|
||||||
|
"id": "57",
|
||||||
|
"name": "CheckUtility"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "curiousScript",
|
||||||
|
"args": { "script": "baby/interaction/curious" },
|
||||||
|
"id": "58",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "56",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "idleFallback",
|
||||||
|
"args": { "script": "baby/interaction/shy" },
|
||||||
|
"id": "59",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "41",
|
||||||
|
"name": "Selector"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "3",
|
||||||
|
"name": "Sequence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "2",
|
||||||
|
"name": "Repeat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenTriple001",
|
||||||
|
"args": {
|
||||||
|
"event": "EVT_TOUCH_TRIPLE_TAP"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "shyScript001",
|
||||||
|
"args": {
|
||||||
|
"script": "baby/interaction/shy"
|
||||||
|
},
|
||||||
|
"id": "17",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "16",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenTap001",
|
||||||
|
"args": {
|
||||||
|
"event": "EVT_TOUCH_TAP"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "tapSelector001",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "dirtyTapSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "dirtyTapCheck",
|
||||||
|
"args": { "attr": "cleanliness", "op": "<=", "value": 20 },
|
||||||
|
"id": "62",
|
||||||
|
"name": "CheckPhysio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "dirtyTapScript",
|
||||||
|
"args": { "script": "baby/interaction/hurt" },
|
||||||
|
"id": "64",
|
||||||
|
"name": "PlayScript"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "dirtyTapMoodDrop",
|
||||||
|
"args": { "attr": "happiness", "amount": -1 },
|
||||||
|
"id": "65",
|
||||||
|
"name": "AddPhysio"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "61",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "curiousTapSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "curiousTapCheck",
|
||||||
|
"args": { "attr": "curious", "op": ">", "value": 0.55 },
|
||||||
|
"id": "67",
|
||||||
|
"name": "CheckUtility"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "wantedScript001",
|
||||||
|
"args": { "script": "baby/interaction/wanted" },
|
||||||
|
"id": "69",
|
||||||
|
"name": "PlayScript"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "tapHappinessUp",
|
||||||
|
"args": { "attr": "happiness", "amount": 3 },
|
||||||
|
"id": "70",
|
||||||
|
"name": "AddPhysio"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "66",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "sadTapSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "sadTapCheck",
|
||||||
|
"args": { "attr": "sad", "op": ">", "value": 0.4 },
|
||||||
|
"id": "72",
|
||||||
|
"name": "CheckUtility"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "comfortScript",
|
||||||
|
"args": { "script": "baby/need/sad_mild" },
|
||||||
|
"id": "73",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "71",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "tapFallback",
|
||||||
|
"args": { "script": "baby/interaction/hurt" },
|
||||||
|
"id": "74",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "60",
|
||||||
|
"name": "Selector"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "18",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenLeftEar001",
|
||||||
|
"args": {
|
||||||
|
"abortType": "interrupt",
|
||||||
|
"event": "EVT_KEY_LEFT_CLICK"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "openMenu001",
|
||||||
|
"args": {
|
||||||
|
"mode": "MODE_MENU"
|
||||||
|
},
|
||||||
|
"id": "24",
|
||||||
|
"name": "RequestModeSwitch"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "23",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenBrainFullnessWarning",
|
||||||
|
"args": { "event": "EVT_INTERNAL_BRAIN_FULLNESS_WARNING" },
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "hungryWarnScript",
|
||||||
|
"args": { "script": "baby/need/hungry" },
|
||||||
|
"id": "30",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "29",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenBrainEnergyWarning",
|
||||||
|
"args": { "event": "EVT_INTERNAL_BRAIN_ENERGY_WARNING" },
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "sleepyWarnScript",
|
||||||
|
"args": { "script": "baby/need/sleepy" },
|
||||||
|
"id": "32",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "31",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenBrainHappinessWarning",
|
||||||
|
"args": { "event": "EVT_INTERNAL_BRAIN_HAPPINESS_WARNING" },
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "sadWarnScript",
|
||||||
|
"args": { "script": "baby/need/sad_mild" },
|
||||||
|
"id": "34",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "33",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenBrainCleanlinessWarning",
|
||||||
|
"args": { "event": "EVT_INTERNAL_BRAIN_CLEANLINESS_WARNING" },
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "dirtyWarnScript",
|
||||||
|
"args": { "script": "baby/need/dirty" },
|
||||||
|
"id": "36",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "35",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenBrainFullnessCritical",
|
||||||
|
"args": { "event": "EVT_INTERNAL_BRAIN_FULLNESS_CRITICAL", "abortType": "interrupt" },
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "hungryCriticalSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "hungryCriticalScript",
|
||||||
|
"args": { "script": "baby/need/hungry" },
|
||||||
|
"id": "82",
|
||||||
|
"name": "PlayScript"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "hungryCriticalMoodDrop",
|
||||||
|
"args": { "attr": "happiness", "amount": -5 },
|
||||||
|
"id": "83",
|
||||||
|
"name": "AddPhysio"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "81",
|
||||||
|
"name": "Sequence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "80",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenBrainEnergyCritical",
|
||||||
|
"args": { "event": "EVT_INTERNAL_BRAIN_ENERGY_CRITICAL", "abortType": "interrupt" },
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "energyCriticalSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "energyCriticalScript",
|
||||||
|
"args": { "script": "baby/need/sleepy" },
|
||||||
|
"id": "86",
|
||||||
|
"name": "PlayScript"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "energyCriticalMoodDrop",
|
||||||
|
"args": { "attr": "happiness", "amount": -5 },
|
||||||
|
"id": "87",
|
||||||
|
"name": "AddPhysio"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "85",
|
||||||
|
"name": "Sequence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "84",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenBrainHappinessCritical",
|
||||||
|
"args": { "event": "EVT_INTERNAL_BRAIN_HAPPINESS_CRITICAL", "abortType": "interrupt" },
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "sadCriticalScript",
|
||||||
|
"args": { "script": "baby/need/sad_mild" },
|
||||||
|
"id": "89",
|
||||||
|
"name": "PlayScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "88",
|
||||||
|
"name": "Listen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "listenBrainCleanlinessCritical",
|
||||||
|
"args": { "event": "EVT_INTERNAL_BRAIN_CLEANLINESS_CRITICAL", "abortType": "interrupt" },
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "dirtyCriticalSeq",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "dirtyCriticalScript",
|
||||||
|
"args": { "script": "baby/need/dirty" },
|
||||||
|
"id": "92",
|
||||||
|
"name": "PlayScript"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "dirtyCriticalMoodDrop",
|
||||||
|
"args": { "attr": "happiness", "amount": -3 },
|
||||||
|
"id": "93",
|
||||||
|
"name": "AddPhysio"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "91",
|
||||||
|
"name": "Sequence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "90",
|
||||||
|
"name": "Listen"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "1",
|
||||||
|
"name": "Parallel"
|
||||||
|
},
|
||||||
|
"vars": [
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"nodeConf": "node-config.b3-setting",
|
||||||
|
"metadata": [],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "example.json",
|
||||||
|
"desc": "最小宠物养成行为树"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"checkExpr": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+117
-1854
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "behavior3editor",
|
"name": "behavior3editor",
|
||||||
"version": "1.8.11",
|
"version": "1.9.1",
|
||||||
"main": "dist-electron/main/index.js",
|
"main": "dist-electron/main/index.js",
|
||||||
"description": "行为树编辑器",
|
"description": "行为树编辑器",
|
||||||
"author": "zhandouxiaojiji & codetypes",
|
"author": "zhandouxiaojiji & codetypes",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 532 KiB |
@@ -1,28 +1,37 @@
|
|||||||
{
|
{
|
||||||
"version": "1.8.1",
|
"$override": {
|
||||||
"name": "test-subtree",
|
},
|
||||||
|
"custom": {
|
||||||
|
},
|
||||||
"desc": "",
|
"desc": "",
|
||||||
"firstid": 1,
|
|
||||||
"export": true,
|
"export": true,
|
||||||
"group": [],
|
"group": [
|
||||||
"import": ["vars/declare-core.json"],
|
|
||||||
"vars": [
|
|
||||||
{
|
|
||||||
"name": "test",
|
|
||||||
"desc": "测试"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
|
"import": [
|
||||||
|
"vars/declare-core.json"
|
||||||
|
],
|
||||||
|
"name": "test-subtree",
|
||||||
|
"prefix": "",
|
||||||
"root": {
|
"root": {
|
||||||
"id": "1",
|
"$id": "8kjkfpkMB7",
|
||||||
"name": "Sequence",
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "2",
|
"$id": "3yauu6FM1P",
|
||||||
"name": "Log",
|
|
||||||
"args": {
|
"args": {
|
||||||
"message": "hello"
|
"message": "hello"
|
||||||
}
|
},
|
||||||
|
"id": "2",
|
||||||
|
"name": "Log"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
"id": "1",
|
||||||
}
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
"vars": [
|
||||||
|
{
|
||||||
|
"desc": "测试",
|
||||||
|
"name": "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
+59
-39
@@ -1,72 +1,92 @@
|
|||||||
{
|
{
|
||||||
"version": "1.8.0",
|
"$override": {
|
||||||
"name": "monster",
|
},
|
||||||
|
"custom": {
|
||||||
|
},
|
||||||
"desc": "怪物测试AI",
|
"desc": "怪物测试AI",
|
||||||
"firstid": 1,
|
|
||||||
"export": true,
|
"export": true,
|
||||||
"group": ["Test"],
|
"group": [
|
||||||
"import": [],
|
"Test"
|
||||||
"vars": [],
|
],
|
||||||
|
"import": [
|
||||||
|
],
|
||||||
|
"name": "monster",
|
||||||
|
"prefix": "",
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"$id": "UzMJ4RCPJi",
|
||||||
"name": "Sequence",
|
|
||||||
"desc": "怪物测试AI",
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 2,
|
"$id": "22VgEJ8qBb",
|
||||||
"name": "Sequence",
|
|
||||||
"desc": "攻击",
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 3,
|
"$id": "sPc1hCO5Y0",
|
||||||
"name": "GetHp",
|
|
||||||
"output": ["hp"],
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 4,
|
"$id": "IliI30nEez",
|
||||||
"name": "Sequence",
|
|
||||||
"desc": "新建行为树",
|
"desc": "新建行为树",
|
||||||
|
"id": "4",
|
||||||
|
"name": "Sequence",
|
||||||
"path": "workdir/subtree2.json"
|
"path": "workdir/subtree2.json"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"id": "3",
|
||||||
|
"name": "GetHp",
|
||||||
|
"output": [
|
||||||
|
"hp"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 6,
|
"$id": "UflkVPzbM2",
|
||||||
"name": "Cmp",
|
|
||||||
"args": {
|
"args": {
|
||||||
"value": "hp",
|
"gt": 50,
|
||||||
"gt": 50
|
"value": "hp"
|
||||||
},
|
},
|
||||||
"input": ["hp"]
|
"id": "6",
|
||||||
|
"input": [
|
||||||
|
"hp"
|
||||||
|
],
|
||||||
|
"name": "Cmp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 7,
|
"$id": "vGBhrKx6OT",
|
||||||
"name": "TestB3",
|
|
||||||
"desc": "攻击",
|
|
||||||
"args": {
|
"args": {
|
||||||
"time": 1,
|
|
||||||
"open": true,
|
|
||||||
"check": "1",
|
"check": "1",
|
||||||
"name": "1",
|
"name": "1",
|
||||||
"status": "FAILURE"
|
"open": true,
|
||||||
}
|
"status": "FAILURE",
|
||||||
|
"time": 1
|
||||||
|
},
|
||||||
|
"desc": "攻击",
|
||||||
|
"id": "7",
|
||||||
|
"name": "TestB3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 8,
|
"$id": "0XPfo7WtaM",
|
||||||
"name": "Sequence",
|
|
||||||
"desc": "新建行为树",
|
"desc": "新建行为树",
|
||||||
|
"id": "8",
|
||||||
|
"name": "Sequence",
|
||||||
"path": "workdir/subtree1.json"
|
"path": "workdir/subtree1.json"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"desc": "攻击",
|
||||||
|
"id": "2",
|
||||||
|
"name": "Sequence"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 11,
|
"$id": "k3OVVSX9ih",
|
||||||
"name": "Log",
|
|
||||||
"desc": "逃跑",
|
|
||||||
"args": {
|
"args": {
|
||||||
"message": "Run!"
|
"message": "Run!"
|
||||||
}
|
},
|
||||||
|
"desc": "逃跑",
|
||||||
|
"id": "11",
|
||||||
|
"name": "Log"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
"desc": "怪物测试AI",
|
||||||
}
|
"id": "1",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
"vars": [
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
@@ -1,30 +1,40 @@
|
|||||||
{
|
{
|
||||||
"version": "1.8.0",
|
"$override": {
|
||||||
"name": "subtree1",
|
},
|
||||||
|
"custom": {
|
||||||
|
},
|
||||||
"desc": "子树1",
|
"desc": "子树1",
|
||||||
"firstid": 1,
|
"group": [
|
||||||
"group": [],
|
],
|
||||||
"import": [],
|
"import": [
|
||||||
"vars": [],
|
],
|
||||||
|
"name": "subtree1",
|
||||||
|
"prefix": "",
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"$id": "IP1AisFtFv",
|
||||||
"name": "Sequence",
|
|
||||||
"desc": "新建行为树",
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 2,
|
"$id": "yZbdLrFj5E",
|
||||||
"name": "Log",
|
|
||||||
"args": {
|
"args": {
|
||||||
"message": "d"
|
"message": "d"
|
||||||
}
|
},
|
||||||
|
"id": "2",
|
||||||
|
"name": "Log"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"$id": "xkChWKGQn1",
|
||||||
"name": "Wait",
|
|
||||||
"args": {
|
"args": {
|
||||||
"time": 1
|
"time": 1
|
||||||
}
|
},
|
||||||
|
"id": "3",
|
||||||
|
"name": "Wait"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
"desc": "新建行为树",
|
||||||
}
|
"id": "1",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
"vars": [
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
@@ -1,30 +1,39 @@
|
|||||||
{
|
{
|
||||||
"version": "1.8.0",
|
"$override": {
|
||||||
"name": "subtree1",
|
},
|
||||||
|
"custom": {
|
||||||
|
},
|
||||||
"desc": "子树1",
|
"desc": "子树1",
|
||||||
"firstid": 1,
|
"group": [
|
||||||
"group": [],
|
],
|
||||||
"import": [],
|
"import": [
|
||||||
"vars": [],
|
],
|
||||||
|
"name": "subtree1",
|
||||||
|
"prefix": "",
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"$id": "GosdwkpHRi",
|
||||||
"name": "Sequence",
|
|
||||||
"desc": "新建行为树",
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 2,
|
"$id": "VJjl0tZcRq",
|
||||||
"name": "Log",
|
"desc": "新建行为树",
|
||||||
"args": {
|
"id": "2",
|
||||||
"message": "hello"
|
"name": "Sequence",
|
||||||
}
|
"path": "workdir/sub/subtree1.json"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"$id": "6OKeNNuNW9",
|
||||||
"name": "Wait",
|
|
||||||
"args": {
|
"args": {
|
||||||
"time": 1
|
"time": 1
|
||||||
}
|
},
|
||||||
|
"id": "5",
|
||||||
|
"name": "Wait"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
"desc": "新建行为树",
|
||||||
}
|
"id": "1",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
"vars": [
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
@@ -1,22 +1,34 @@
|
|||||||
{
|
{
|
||||||
"version": "1.8.0",
|
"$override": {
|
||||||
"name": "subtree2",
|
},
|
||||||
|
"custom": {
|
||||||
|
},
|
||||||
"desc": "子树2",
|
"desc": "子树2",
|
||||||
"firstid": 1,
|
"group": [
|
||||||
"group": [],
|
],
|
||||||
"import": [],
|
"import": [
|
||||||
"vars": [],
|
],
|
||||||
|
"name": "subtree2",
|
||||||
|
"prefix": "",
|
||||||
"root": {
|
"root": {
|
||||||
"id": 1,
|
"$id": "TZZbFRZLIC",
|
||||||
"name": "Sequence",
|
|
||||||
"desc": "新建行为树",
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 2,
|
"$id": "iF9KMbqNoB",
|
||||||
"name": "Cmp",
|
"args": {
|
||||||
"args": {},
|
},
|
||||||
"input": ["x"]
|
"id": "2",
|
||||||
|
"input": [
|
||||||
|
"x"
|
||||||
|
],
|
||||||
|
"name": "Cmp"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
"desc": "新建行为树",
|
||||||
}
|
"id": "1",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
"vars": [
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
+1
-1
Submodule src/behavior3 updated: 37eea09599...b7b4e52aff.
+254
-205
@@ -11,6 +11,7 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
Flex,
|
Flex,
|
||||||
Form,
|
Form,
|
||||||
|
FormInstance,
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Select,
|
Select,
|
||||||
@@ -26,7 +27,7 @@ import Markdown from "react-markdown";
|
|||||||
import { useDebounceCallback } from "usehooks-ts";
|
import { useDebounceCallback } from "usehooks-ts";
|
||||||
import { useShallow } from "zustand/react/shallow";
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import { ExpressionEvaluator } from "../behavior3/src/behavior3";
|
import { ExpressionEvaluator } from "../behavior3/src/behavior3";
|
||||||
import { EditNode, EditTree, useWorkspace } from "../contexts/workspace-context";
|
import { EditNode, EditorStore, EditTree, useWorkspace } from "../contexts/workspace-context";
|
||||||
import {
|
import {
|
||||||
hasArgOptions,
|
hasArgOptions,
|
||||||
ImportDecl,
|
ImportDecl,
|
||||||
@@ -43,12 +44,14 @@ import {
|
|||||||
import {
|
import {
|
||||||
checkNodeArgValue,
|
checkNodeArgValue,
|
||||||
checkOneof,
|
checkOneof,
|
||||||
|
getNodeArgOptions,
|
||||||
getNodeArgRawType,
|
getNodeArgRawType,
|
||||||
isNodeArgArray,
|
isNodeArgArray,
|
||||||
isNodeArgOptional,
|
isNodeArgOptional,
|
||||||
isValidChildren,
|
isValidChildren,
|
||||||
isValidVariableName,
|
isValidVariableName,
|
||||||
isVariadic,
|
isVariadic,
|
||||||
|
NodeDefs,
|
||||||
parseExpr,
|
parseExpr,
|
||||||
} from "../misc/b3util";
|
} from "../misc/b3util";
|
||||||
import { message } from "../misc/hooks";
|
import { message } from "../misc/hooks";
|
||||||
@@ -533,7 +536,6 @@ const validateArg = (
|
|||||||
return Promise.reject(new Error(i18n.t("node.invalidExpression")));
|
return Promise.reject(new Error(i18n.t("node.invalidExpression")));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
|
||||||
return Promise.reject(new Error(i18n.t("node.invalidExpression")));
|
return Promise.reject(new Error(i18n.t("node.invalidExpression")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -555,6 +557,223 @@ const validateArg = (
|
|||||||
return Promise.resolve(value);
|
return Promise.resolve(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateFormWithNode = (
|
||||||
|
form: FormInstance,
|
||||||
|
{
|
||||||
|
editingNode,
|
||||||
|
nodeDefs,
|
||||||
|
}: {
|
||||||
|
editingNode: EditNode;
|
||||||
|
nodeDefs: NodeDefs;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const data = editingNode.data;
|
||||||
|
const def = nodeDefs.get(data.name);
|
||||||
|
const t = i18n.t;
|
||||||
|
form.resetFields();
|
||||||
|
form.setFieldValue("id", editingNode.prefix + data.id);
|
||||||
|
form.setFieldValue("name", data.name);
|
||||||
|
form.setFieldValue("type", def.type);
|
||||||
|
form.setFieldValue("desc", data.desc || def.desc);
|
||||||
|
form.setFieldValue("debug", data.debug);
|
||||||
|
form.setFieldValue("disabled", data.disabled);
|
||||||
|
form.setFieldValue("path", data.path);
|
||||||
|
form.setFieldValue(
|
||||||
|
"group",
|
||||||
|
def.group?.map((g) => ({ label: g, value: g }))
|
||||||
|
);
|
||||||
|
if (def.children === undefined || def.children === -1) {
|
||||||
|
form.setFieldValue("children", t("node.children.unlimited"));
|
||||||
|
} else {
|
||||||
|
form.setFieldValue("children", def.children);
|
||||||
|
}
|
||||||
|
form.setFieldValue("json-data", JSON.stringify(data, null, 2));
|
||||||
|
def.args?.forEach((arg) => {
|
||||||
|
const type = getNodeArgRawType(arg);
|
||||||
|
const value = data.args?.[arg.name];
|
||||||
|
const name = `args.${arg.name}`;
|
||||||
|
if (isNodeArgArray(arg)) {
|
||||||
|
form.setFieldValue(
|
||||||
|
name,
|
||||||
|
(Array.isArray(value) ? value : []).map((item) => {
|
||||||
|
if (isJsonType(type)) {
|
||||||
|
return item === null ? "null" : JSON.stringify(item ?? arg.default, null, 2);
|
||||||
|
} else {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (isJsonType(type)) {
|
||||||
|
form.setFieldValue(
|
||||||
|
name,
|
||||||
|
value === null ? "null" : JSON.stringify(value ?? arg.default, null, 2)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
form.setFieldValue(name, value ?? arg.default);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
def.input?.forEach((_, i) => {
|
||||||
|
if (isVariadic(def.input!, i)) {
|
||||||
|
form.setFieldValue(`input.${i}`, data.input?.slice(i) ?? []);
|
||||||
|
} else {
|
||||||
|
form.setFieldValue(`input.${i}`, data.input?.[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
def.output?.forEach((_, i) => {
|
||||||
|
if (isVariadic(def.output!, i)) {
|
||||||
|
form.setFieldValue(`output.${i}`, data.output?.slice(i) ?? []);
|
||||||
|
} else {
|
||||||
|
form.setFieldValue(`output.${i}`, data.output?.[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createNodeFromForm = (
|
||||||
|
form: FormInstance,
|
||||||
|
{
|
||||||
|
editingNode,
|
||||||
|
nodeDefs,
|
||||||
|
}: {
|
||||||
|
editingNode: EditNode;
|
||||||
|
nodeDefs: NodeDefs;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const def = nodeDefs.get(editingNode.data.name);
|
||||||
|
const data = {} as NodeData;
|
||||||
|
const values = form.getFieldsValue();
|
||||||
|
data.$id = editingNode.data.$id;
|
||||||
|
data.id = editingNode.data.id;
|
||||||
|
data.name = values.name;
|
||||||
|
data.debug = values.debug || undefined;
|
||||||
|
data.disabled = values.disabled || undefined;
|
||||||
|
data.desc = values.desc && values.desc !== def.desc ? values.desc : undefined;
|
||||||
|
data.path = values.path || undefined;
|
||||||
|
|
||||||
|
if (def.args?.length) {
|
||||||
|
def.args?.forEach((arg) => {
|
||||||
|
const value = values[`args.${arg.name}`];
|
||||||
|
if (value !== null && value !== undefined && value !== "") {
|
||||||
|
data.args ||= {};
|
||||||
|
const type = getNodeArgRawType(arg);
|
||||||
|
if (isNodeArgArray(arg)) {
|
||||||
|
const arr: unknown[] = [];
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((item) => {
|
||||||
|
if (isJsonType(type)) {
|
||||||
|
try {
|
||||||
|
arr.push(item === "null" ? null : JSON.parse(item));
|
||||||
|
} catch {
|
||||||
|
/** ignore */
|
||||||
|
}
|
||||||
|
} else if (item !== null && item !== undefined) {
|
||||||
|
arr.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
data.args[arg.name] = !isNodeArgOptional(arg) ? arr : arr.length ? arr : undefined;
|
||||||
|
} else if (isJsonType(type)) {
|
||||||
|
try {
|
||||||
|
data.args[arg.name] = value === "null" ? null : JSON.parse(value);
|
||||||
|
} catch {
|
||||||
|
/** ignore */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data.args[arg.name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.args = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def.input?.length) {
|
||||||
|
def.input?.forEach((_, i) => {
|
||||||
|
data.input ||= [];
|
||||||
|
if (isVariadic(def.input!, i)) {
|
||||||
|
const arr = (values[`input.${i}`] ?? []) as string[];
|
||||||
|
data.input.push(...arr.filter((v) => typeof v === "string"));
|
||||||
|
} else {
|
||||||
|
const v = values[`input.${i}`];
|
||||||
|
data.input.push(v ?? "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.input = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def.output?.length) {
|
||||||
|
def.output?.forEach((_, i) => {
|
||||||
|
data.output ||= [];
|
||||||
|
if (isVariadic(def.output!, i)) {
|
||||||
|
const arr = (values[`output.${i}`] ?? []) as string[];
|
||||||
|
data.output.push(...arr.filter((v) => typeof v === "string"));
|
||||||
|
} else {
|
||||||
|
const v = values[`output.${i}`];
|
||||||
|
data.output.push(v ?? "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.output = [];
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createInOutOptions = ({
|
||||||
|
nodeDefs,
|
||||||
|
usingVars,
|
||||||
|
editing,
|
||||||
|
}: {
|
||||||
|
nodeDefs: NodeDefs;
|
||||||
|
usingVars: Record<string, VarDecl> | null;
|
||||||
|
editing: EditorStore | undefined;
|
||||||
|
}) => {
|
||||||
|
const options: OptionType[] = [];
|
||||||
|
const filter: Record<string, boolean> = {};
|
||||||
|
const collect = (node?: NodeData) => {
|
||||||
|
if (node) {
|
||||||
|
const def = nodeDefs.get(node.name);
|
||||||
|
node.input?.forEach((v, i) => {
|
||||||
|
let desc: string;
|
||||||
|
const inputDef = def.input;
|
||||||
|
if (inputDef?.length && i >= inputDef.length && isVariadic(inputDef, -1)) {
|
||||||
|
desc = inputDef[inputDef.length - 1];
|
||||||
|
} else {
|
||||||
|
desc = inputDef?.[i] ?? "<unknown>";
|
||||||
|
}
|
||||||
|
if (!filter[v]) {
|
||||||
|
options.push({ label: `${v}(${desc})`, value: v });
|
||||||
|
filter[v] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
node.output?.forEach((v, i) => {
|
||||||
|
let desc: string;
|
||||||
|
const outputDef = def.output;
|
||||||
|
if (outputDef?.length && i >= outputDef.length && isVariadic(outputDef, -1)) {
|
||||||
|
desc = outputDef[outputDef.length - 1];
|
||||||
|
} else {
|
||||||
|
desc = outputDef?.[i] ?? "<unknown>";
|
||||||
|
}
|
||||||
|
if (!filter[v]) {
|
||||||
|
options.push({ label: `${v}(${desc})`, value: v });
|
||||||
|
filter[v] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
node.children?.forEach((child) => collect(child));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (usingVars) {
|
||||||
|
Object.values(usingVars).forEach((v) => {
|
||||||
|
if (!filter[v.name]) {
|
||||||
|
options.push({ label: `${v.name}(${v.desc})`, value: v.name });
|
||||||
|
filter[v.name] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
collect(editing?.data.root);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
const NodeInspector: FC = () => {
|
const NodeInspector: FC = () => {
|
||||||
const workspace = useWorkspace(
|
const workspace = useWorkspace(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
@@ -579,7 +798,13 @@ const NodeInspector: FC = () => {
|
|||||||
100
|
100
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [nodeArgs, setNodeArgs] = useState<Record<string, unknown>>(
|
||||||
|
workspace.editingNode.data.args ?? {}
|
||||||
|
);
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
|
setNodeArgs(createNodeFromForm(form, workspace).args ?? {});
|
||||||
|
|
||||||
if (form.isFieldsValidating()) {
|
if (form.isFieldsValidating()) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
submit();
|
submit();
|
||||||
@@ -597,124 +822,24 @@ const NodeInspector: FC = () => {
|
|||||||
|
|
||||||
// set form values
|
// set form values
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const data = workspace.editingNode.data;
|
updateFormWithNode(form, workspace);
|
||||||
const def = workspace.nodeDefs.get(workspace.editingNode.data.name);
|
|
||||||
form.resetFields();
|
|
||||||
form.setFieldValue("id", workspace.editingNode.prefix + data.id);
|
|
||||||
form.setFieldValue("name", data.name);
|
|
||||||
form.setFieldValue("type", def.type);
|
|
||||||
form.setFieldValue("desc", data.desc || def.desc);
|
|
||||||
form.setFieldValue("debug", data.debug);
|
|
||||||
form.setFieldValue("disabled", data.disabled);
|
|
||||||
form.setFieldValue("path", data.path);
|
|
||||||
form.setFieldValue(
|
|
||||||
"group",
|
|
||||||
def.group?.map((g) => ({ label: g, value: g }))
|
|
||||||
);
|
|
||||||
if (def.children === undefined || def.children === -1) {
|
|
||||||
form.setFieldValue("children", t("node.children.unlimited"));
|
|
||||||
} else {
|
|
||||||
form.setFieldValue("children", def.children);
|
|
||||||
}
|
|
||||||
form.setFieldValue("json-data", JSON.stringify(data, null, 2));
|
|
||||||
def.args?.forEach((arg) => {
|
|
||||||
const type = getNodeArgRawType(arg);
|
|
||||||
const value = data.args?.[arg.name];
|
|
||||||
const name = `args.${arg.name}`;
|
|
||||||
if (isNodeArgArray(arg)) {
|
|
||||||
form.setFieldValue(
|
|
||||||
name,
|
|
||||||
(Array.isArray(value) ? value : []).map((item) => {
|
|
||||||
if (isJsonType(type)) {
|
|
||||||
return item === null ? "null" : JSON.stringify(item ?? arg.default, null, 2);
|
|
||||||
} else {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (isJsonType(type)) {
|
|
||||||
form.setFieldValue(
|
|
||||||
name,
|
|
||||||
value === null ? "null" : JSON.stringify(value ?? arg.default, null, 2)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
form.setFieldValue(name, value ?? arg.default);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
def.input?.forEach((_, i) => {
|
|
||||||
if (isVariadic(def.input!, i)) {
|
|
||||||
form.setFieldValue(`input.${i}`, data.input?.slice(i) ?? []);
|
|
||||||
} else {
|
|
||||||
form.setFieldValue(`input.${i}`, data.input?.[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
def.output?.forEach((_, i) => {
|
|
||||||
if (isVariadic(def.output!, i)) {
|
|
||||||
form.setFieldValue(`output.${i}`, data.output?.slice(i) ?? []);
|
|
||||||
} else {
|
|
||||||
form.setFieldValue(`output.${i}`, data.output?.[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
validateFieldsLater();
|
validateFieldsLater();
|
||||||
}, [workspace.editingNode]);
|
}, [workspace.editingNode]);
|
||||||
|
|
||||||
// auto complete for node
|
// auto complete for node
|
||||||
const nodeOptions = useMemo(() => {
|
const nodeOptions = useMemo(
|
||||||
const options: OptionType[] = [];
|
() =>
|
||||||
workspace.nodeDefs.forEach((e) => {
|
Array.from(workspace.nodeDefs.entries()).map(([name, def]) => {
|
||||||
options.push({ label: `${e.name}(${e.desc})`, value: e.name });
|
return { label: `${name}(${def.desc})`, value: name };
|
||||||
});
|
}),
|
||||||
return options;
|
[workspace.nodeDefs]
|
||||||
}, [workspace.nodeDefs]);
|
);
|
||||||
|
|
||||||
// auto complete for input and output
|
// auto complete for input and output
|
||||||
const inoutVarOptions = useMemo(() => {
|
const inoutVarOptions = useMemo(
|
||||||
const options: OptionType[] = [];
|
() => createInOutOptions(workspace),
|
||||||
const filter: Record<string, boolean> = {};
|
[workspace.editing, workspace.usingVars]
|
||||||
const collect = (node?: NodeData) => {
|
);
|
||||||
if (node) {
|
|
||||||
const def = workspace.nodeDefs.get(node.name);
|
|
||||||
node.input?.forEach((v, i) => {
|
|
||||||
let desc: string;
|
|
||||||
const inputDef = def.input;
|
|
||||||
if (inputDef?.length && i >= inputDef.length && isVariadic(inputDef, -1)) {
|
|
||||||
desc = inputDef[inputDef.length - 1];
|
|
||||||
} else {
|
|
||||||
desc = inputDef?.[i] ?? "<unknown>";
|
|
||||||
}
|
|
||||||
if (!filter[v]) {
|
|
||||||
options.push({ label: `${v}(${desc})`, value: v });
|
|
||||||
filter[v] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
node.output?.forEach((v, i) => {
|
|
||||||
let desc: string;
|
|
||||||
const outputDef = def.output;
|
|
||||||
if (outputDef?.length && i >= outputDef.length && isVariadic(outputDef, -1)) {
|
|
||||||
desc = outputDef[outputDef.length - 1];
|
|
||||||
} else {
|
|
||||||
desc = outputDef?.[i] ?? "<unknown>";
|
|
||||||
}
|
|
||||||
if (!filter[v]) {
|
|
||||||
options.push({ label: `${v}(${desc})`, value: v });
|
|
||||||
filter[v] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
node.children?.forEach((child) => collect(child));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (workspace.usingVars) {
|
|
||||||
Object.values(workspace.usingVars).forEach((v) => {
|
|
||||||
if (!filter[v.name]) {
|
|
||||||
options.push({ label: `${v.name}(${v.desc})`, value: v.name });
|
|
||||||
filter[v.name] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
collect(workspace.editing?.data.root);
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}, [workspace.editing, workspace.usingVars]);
|
|
||||||
|
|
||||||
// auto complete for subtree
|
// auto complete for subtree
|
||||||
const subtreeOptions = useMemo(() => {
|
const subtreeOptions = useMemo(() => {
|
||||||
@@ -735,87 +860,9 @@ const NodeInspector: FC = () => {
|
|||||||
const def = workspace.nodeDefs.get(editingNode.data.name);
|
const def = workspace.nodeDefs.get(editingNode.data.name);
|
||||||
const disabled = editingNode.disabled;
|
const disabled = editingNode.disabled;
|
||||||
|
|
||||||
// update value
|
const finish = () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const finish = (values: any) => {
|
|
||||||
const data = {} as NodeData;
|
|
||||||
data.$id = editingNode.data.$id;
|
|
||||||
data.id = editingNode.data.id;
|
|
||||||
data.name = values.name;
|
|
||||||
data.debug = values.debug || undefined;
|
|
||||||
data.disabled = values.disabled || undefined;
|
|
||||||
data.desc = values.desc && values.desc !== def.desc ? values.desc : undefined;
|
|
||||||
data.path = values.path || undefined;
|
|
||||||
|
|
||||||
if (def.args?.length) {
|
|
||||||
def.args?.forEach((arg) => {
|
|
||||||
const value = values[`args.${arg.name}`];
|
|
||||||
if (value !== null && value !== undefined && value !== "") {
|
|
||||||
data.args ||= {};
|
|
||||||
const type = getNodeArgRawType(arg);
|
|
||||||
if (isNodeArgArray(arg)) {
|
|
||||||
const arr: unknown[] = [];
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
value.forEach((item) => {
|
|
||||||
if (isJsonType(type)) {
|
|
||||||
try {
|
|
||||||
arr.push(item === "null" ? null : JSON.parse(item));
|
|
||||||
} catch {
|
|
||||||
/** ignore */
|
|
||||||
}
|
|
||||||
} else if (item !== null && item !== undefined) {
|
|
||||||
arr.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
data.args[arg.name] = !isNodeArgOptional(arg) ? arr : arr.length ? arr : undefined;
|
|
||||||
} else if (isJsonType(type)) {
|
|
||||||
try {
|
|
||||||
data.args[arg.name] = value === "null" ? null : JSON.parse(value);
|
|
||||||
} catch {
|
|
||||||
/** ignore */
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data.args[arg.name] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data.args = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def.input?.length) {
|
|
||||||
def.input?.forEach((_, i) => {
|
|
||||||
data.input ||= [];
|
|
||||||
if (isVariadic(def.input!, i)) {
|
|
||||||
const arr = (values[`input.${i}`] ?? []) as string[];
|
|
||||||
data.input.push(...arr.filter((v) => typeof v === "string"));
|
|
||||||
} else {
|
|
||||||
const v = values[`input.${i}`];
|
|
||||||
data.input.push(v ?? "");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data.input = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def.output?.length) {
|
|
||||||
def.output?.forEach((_, i) => {
|
|
||||||
data.output ||= [];
|
|
||||||
if (isVariadic(def.output!, i)) {
|
|
||||||
const arr = (values[`output.${i}`] ?? []) as string[];
|
|
||||||
data.output.push(...arr.filter((v) => typeof v === "string"));
|
|
||||||
} else {
|
|
||||||
const v = values[`output.${i}`];
|
|
||||||
data.output.push(v ?? "");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data.output = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
workspace.editing?.dispatch?.("updateNode", {
|
workspace.editing?.dispatch?.("updateNode", {
|
||||||
data: data,
|
data: createNodeFromForm(form, workspace),
|
||||||
} as EditNode);
|
} as EditNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -834,7 +881,7 @@ const NodeInspector: FC = () => {
|
|||||||
prefix: editingNode.prefix,
|
prefix: editingNode.prefix,
|
||||||
disabled: editingNode.disabled,
|
disabled: editingNode.disabled,
|
||||||
});
|
});
|
||||||
finish(form.getFieldsValue());
|
finish();
|
||||||
} else {
|
} else {
|
||||||
submit();
|
submit();
|
||||||
}
|
}
|
||||||
@@ -842,7 +889,7 @@ const NodeInspector: FC = () => {
|
|||||||
|
|
||||||
const changeSubtree = () => {
|
const changeSubtree = () => {
|
||||||
if (form.getFieldValue("path") !== editingNode.data.path) {
|
if (form.getFieldValue("path") !== editingNode.data.path) {
|
||||||
finish(form.getFieldsValue());
|
finish();
|
||||||
} else {
|
} else {
|
||||||
submit();
|
submit();
|
||||||
}
|
}
|
||||||
@@ -1183,7 +1230,7 @@ const NodeInspector: FC = () => {
|
|||||||
() => ({
|
() => ({
|
||||||
validator(_, value) {
|
validator(_, value) {
|
||||||
return validateArg(
|
return validateArg(
|
||||||
editingNode.data,
|
createNodeFromForm(form, workspace),
|
||||||
arg,
|
arg,
|
||||||
value,
|
value,
|
||||||
workspace.usingVars
|
workspace.usingVars
|
||||||
@@ -1216,12 +1263,14 @@ const NodeInspector: FC = () => {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onBlur={submit}
|
onBlur={submit}
|
||||||
onChange={submit}
|
onChange={submit}
|
||||||
options={(arg.options ?? []).map((option) => {
|
options={(getNodeArgOptions(arg, nodeArgs) ?? []).map(
|
||||||
return {
|
(option) => {
|
||||||
value: option.value,
|
return {
|
||||||
label: `${option.name}(${option.value})`,
|
value: option.value,
|
||||||
};
|
label: `${option.name}(${option.value})`,
|
||||||
})}
|
};
|
||||||
|
}
|
||||||
|
)}
|
||||||
filterOption={(value, option) => {
|
filterOption={(value, option) => {
|
||||||
value = value.toUpperCase();
|
value = value.toUpperCase();
|
||||||
return !!option?.label
|
return !!option?.label
|
||||||
@@ -1281,7 +1330,7 @@ const NodeInspector: FC = () => {
|
|||||||
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
||||||
async validator(_, value) {
|
async validator(_, value) {
|
||||||
return validateArg(
|
return validateArg(
|
||||||
editingNode.data,
|
createNodeFromForm(form, workspace),
|
||||||
arg,
|
arg,
|
||||||
value,
|
value,
|
||||||
workspace.usingVars
|
workspace.usingVars
|
||||||
@@ -1345,7 +1394,7 @@ const NodeInspector: FC = () => {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onBlur={submit}
|
onBlur={submit}
|
||||||
onChange={submit}
|
onChange={submit}
|
||||||
options={(arg.options ?? []).map((option) => {
|
options={(getNodeArgOptions(arg, nodeArgs) ?? []).map((option) => {
|
||||||
return {
|
return {
|
||||||
value: option.value,
|
value: option.value,
|
||||||
label: `${option.name}(${option.value})`,
|
label: `${option.name}(${option.value})`,
|
||||||
|
|||||||
@@ -373,6 +373,16 @@ export const Workspace: FC = () => {
|
|||||||
>
|
>
|
||||||
<PiTreeStructureFill size="50px" />
|
<PiTreeStructureFill size="50px" />
|
||||||
<div>Behavior3 Editor</div>
|
<div>Behavior3 Editor</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
paddingLeft: "5px",
|
||||||
|
paddingTop: "15px",
|
||||||
|
fontSize: "18px",
|
||||||
|
color: "gray",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{app.getVersion()}
|
||||||
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex vertical style={{ paddingLeft: "55px", paddingBottom: "15px" }}>
|
<Flex vertical style={{ paddingLeft: "55px", paddingBottom: "15px" }}>
|
||||||
<div style={{ fontSize: "22px", fontWeight: "500" }}>{t("start")}</div>
|
<div style={{ fontSize: "22px", fontWeight: "500" }}>{t("start")}</div>
|
||||||
|
|||||||
@@ -137,6 +137,14 @@ body {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-select-dropdown .ant-select-item-option-selected {
|
||||||
|
background-color: var(--b3-color-selected) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select-dropdown .ant-select-item-option-active {
|
||||||
|
background-color: var(--b3-color-selected) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-form-item .ant-form-item-explain {
|
.ant-form-item .ant-form-item-explain {
|
||||||
font-size: small;
|
font-size: small;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import { NodeDef } from "../behavior3/src/behavior3";
|
import { NodeDef } from "../behavior3/src/behavior3";
|
||||||
|
|
||||||
export const VERSION = "1.8.10";
|
export const VERSION = "1.9.0";
|
||||||
|
|
||||||
export const keyWords = ["true", "false", "null", "undefined", "NaN", "Infinity"];
|
export const keyWords = ["true", "false", "null", "undefined", "NaN", "Infinity"];
|
||||||
|
|
||||||
|
|||||||
+43
-12
@@ -69,9 +69,28 @@ export const initWorkdir = (path: string, handler: typeof alertError) => {
|
|||||||
const nodeDefData = readJson(`${workdir}/node-config.b3-setting`) as NodeDef[];
|
const nodeDefData = readJson(`${workdir}/node-config.b3-setting`) as NodeDef[];
|
||||||
const groups: Set<string> = new Set();
|
const groups: Set<string> = new Set();
|
||||||
nodeDefs = new NodeDefs();
|
nodeDefs = new NodeDefs();
|
||||||
for (const v of nodeDefData) {
|
for (const node of nodeDefData) {
|
||||||
nodeDefs.set(v.name, v);
|
node.args?.forEach((arg) => {
|
||||||
v.group?.forEach((g) => groups.add(g));
|
if (arg.options && !arg.options[0].source) {
|
||||||
|
arg.options = [
|
||||||
|
{
|
||||||
|
source: arg.options as unknown as Array<{ name: string; value: unknown }>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
arg.options?.forEach((option) => {
|
||||||
|
Object.keys(option.match ?? {}).forEach((key) => {
|
||||||
|
if (!node.args?.find((v) => v.name === key)) {
|
||||||
|
console.error(
|
||||||
|
`match key '${key}' in arg '${arg.name}' of ` +
|
||||||
|
`node '${node.name}' is not found in args`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
nodeDefs.set(node.name, node);
|
||||||
|
node.group?.forEach((g) => groups.add(g));
|
||||||
}
|
}
|
||||||
groupDefs = Array.from(groups).sort();
|
groupDefs = Array.from(groups).sort();
|
||||||
};
|
};
|
||||||
@@ -105,7 +124,7 @@ export const parseExpr = (expr: string) => {
|
|||||||
return parsedExprs[expr];
|
return parsedExprs[expr];
|
||||||
}
|
}
|
||||||
const result = expr
|
const result = expr
|
||||||
.split(/[^a-zA-Z0-9_.]/)
|
.split(/[^a-zA-Z0-9_.'"]/)
|
||||||
.map((v) => v.split(".")[0])
|
.map((v) => v.split(".")[0])
|
||||||
.filter((v) => isValidVariableName(v));
|
.filter((v) => isValidVariableName(v));
|
||||||
parsedExprs[expr] = result;
|
parsedExprs[expr] = result;
|
||||||
@@ -207,6 +226,19 @@ export const isNodeArgOptional = (arg: NodeArg) => {
|
|||||||
return arg.type.includes("?");
|
return arg.type.includes("?");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getNodeArgOptions = (arg: NodeArg, args: Record<string, unknown>) => {
|
||||||
|
if (!arg.options) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const defaultMatch = arg.options.find((option) => !option.match);
|
||||||
|
if (defaultMatch) {
|
||||||
|
return defaultMatch.source;
|
||||||
|
}
|
||||||
|
return arg.options.find((entry) =>
|
||||||
|
Object.entries(entry.match!).every(([key, value]) => value.includes(args[key]))
|
||||||
|
)?.source;
|
||||||
|
};
|
||||||
|
|
||||||
export const checkNodeArgValue = (
|
export const checkNodeArgValue = (
|
||||||
data: NodeData,
|
data: NodeData,
|
||||||
arg: NodeArg,
|
arg: NodeArg,
|
||||||
@@ -264,7 +296,8 @@ export const checkNodeArgValue = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasArgOptions(arg)) {
|
if (hasArgOptions(arg)) {
|
||||||
const found = !!arg.options?.find((option) => option.value === value);
|
const options = getNodeArgOptions(arg, data.args ?? {});
|
||||||
|
const found = !!options?.find((option) => option.value === value);
|
||||||
const isOptional = value === undefined && isNodeArgOptional(arg);
|
const isOptional = value === undefined && isNodeArgOptional(arg);
|
||||||
if (!(found || isOptional)) {
|
if (!(found || isOptional)) {
|
||||||
error(`'${arg.name}=${JSON.stringify(value)}' is not a one of the option values`);
|
error(`'${arg.name}=${JSON.stringify(value)}' is not a one of the option values`);
|
||||||
@@ -435,12 +468,10 @@ const checkNodeData = (data: NodeData | null | undefined, printer: ErrorPrinter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf.children !== undefined && conf.children !== -1) {
|
if (!isValidChildren(data)) {
|
||||||
const count = data.children?.length || 0;
|
hasError = true;
|
||||||
if (conf.children !== count) {
|
const count = data.children?.filter((c) => !c.disabled).length || 0;
|
||||||
hasError = true;
|
error(`expect ${conf.children} children, but got ${count}`);
|
||||||
error(`expect ${conf.children} children, but got ${count}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let hasVaridicInput = false;
|
let hasVaridicInput = false;
|
||||||
@@ -667,7 +698,7 @@ const buildStatusFlag = (data: NodeData, childStatus: number) => {
|
|||||||
export const isValidChildren = (data: NodeData) => {
|
export const isValidChildren = (data: NodeData) => {
|
||||||
const def = nodeDefs.get(data.name);
|
const def = nodeDefs.get(data.name);
|
||||||
if (def.children !== undefined && def.children !== -1) {
|
if (def.children !== undefined && def.children !== -1) {
|
||||||
return (data.children?.length || 0) === def.children;
|
return (data.children?.filter((c) => !c.disabled).length || 0) === def.children;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Node, NodeDef } from "../behavior3/src/behavior3";
|
import { Node, NodeDef } from "../behavior3/src/behavior3";
|
||||||
import { Context, DeepReadonly } from "../behavior3/src/behavior3/context";
|
import { Context } from "../behavior3/src/behavior3/context";
|
||||||
|
|
||||||
export const zhNodeDef = () => {
|
export const zhNodeDef = () => {
|
||||||
const context = new (class extends Context {
|
const context = new (class extends Context {
|
||||||
@@ -7,7 +7,7 @@ export const zhNodeDef = () => {
|
|||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
const defs: DeepReadonly<NodeDef>[] = [];
|
const defs: NodeDef[] = [];
|
||||||
for (const k in context.nodeDefs) {
|
for (const k in context.nodeDefs) {
|
||||||
const descriptor = context.nodeDefs[k];
|
const descriptor = context.nodeDefs[k];
|
||||||
defs.push(descriptor);
|
defs.push(descriptor);
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"$override": {
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
},
|
||||||
|
"group": [
|
||||||
|
],
|
||||||
|
"import": [
|
||||||
|
],
|
||||||
|
"name": "example",
|
||||||
|
"prefix": "",
|
||||||
|
"root": {
|
||||||
|
"$id": "WlN4KrHMeI",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "VewHwrsZoK",
|
||||||
|
"args": {
|
||||||
|
"level": "log",
|
||||||
|
"message": "hello"
|
||||||
|
},
|
||||||
|
"id": "2",
|
||||||
|
"name": "Log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "w2qpYhd3fi",
|
||||||
|
"args": {
|
||||||
|
"time": 1
|
||||||
|
},
|
||||||
|
"id": "3",
|
||||||
|
"name": "Wait"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "bro2CDGyFh",
|
||||||
|
"id": "4",
|
||||||
|
"name": "unknow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "1",
|
||||||
|
"name": "Sequence"
|
||||||
|
},
|
||||||
|
"vars": [
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,856 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "AlwaysFailure",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "始终返回失败",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `running` 时,返回 `running`\n+ 其它情况,不管子节点是否成功都返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AlwaysRunning",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "始终返回运行中状态",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 始终返回 `running`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AlwaysSuccess",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "始终返回成功",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `running` 时,返回 `running`\n+ 其它情况,不管子节点是否成功都返回 `success`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Assert",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "断言",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "消息"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `failure` 时,抛出异常\n+ 其余情况返回子节点的执行状态\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Calculate",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "简单的数值公式计算",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "expr",
|
||||||
|
"desc": "计算公式"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"计算结果"
|
||||||
|
],
|
||||||
|
"doc": "+ 做简单的数值公式计算,返回结果到输出\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Case",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": 2,
|
||||||
|
"status": [
|
||||||
|
"&success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "分支选择",
|
||||||
|
"doc": "+ 必须有两个子节点\n+ 第一个子节点为判断条件\n+ 第二个子节点为判断为 `success` 时执行的节点\n+ 此节点不会真正意义的执行,而是交由 `Switch` 节点来执行\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Check",
|
||||||
|
"type": "Condition",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "检查True或False",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "expr",
|
||||||
|
"desc": "值"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 做简单数值公式判定,返回 `success` 或 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Concat",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "将两个输入合并为一个数组,并返回新数组",
|
||||||
|
"input": [
|
||||||
|
"数组1",
|
||||||
|
"数组2"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"新数组"
|
||||||
|
],
|
||||||
|
"doc": "+ 如果输入不是数组,则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Delay",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "延时执行子节点",
|
||||||
|
"input": [
|
||||||
|
"延时时间?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "delay",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "延时时间",
|
||||||
|
"oneof": "延时时间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cacheVars",
|
||||||
|
"type": "string[]?",
|
||||||
|
"desc": "暂存环境变量"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 当延时触发时,执行第一个子节点,多个仅执行第一个\n+ 如果子节点返回 `running`,会中断执行并清理执行栈"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Filter",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "返回满足条件的元素",
|
||||||
|
"input": [
|
||||||
|
"输入数组"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"变量",
|
||||||
|
"新数组"
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 遍历输入数组,将当前元素写入`变量`,满足条件的元素放入新数组\n+ 只有当新数组不为空时,才返回 `success`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ForEach",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"|running",
|
||||||
|
"|failure"
|
||||||
|
],
|
||||||
|
"desc": "遍历数组",
|
||||||
|
"input": [
|
||||||
|
"数组"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"变量",
|
||||||
|
"索引?"
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 遍历输入数组,将当前元素写入`变量`\n+ 当子节点返回 `failure` 时,退出遍历并返回 `failure` 状态\n+ 执行完所有子节点后,返回 `success`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "GetField",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "获取对象的字段值",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "field",
|
||||||
|
"type": "string?",
|
||||||
|
"desc": "字段(field)",
|
||||||
|
"oneof": "字段(field)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"input": [
|
||||||
|
"对象",
|
||||||
|
"字段(field)?"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"字段值(value)"
|
||||||
|
],
|
||||||
|
"doc": "+ 合法元素不包括 `undefined` 和 `null`\n+ 只有获取到合法元素时候才会返回 `success`,否则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IfElse",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": 3,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "条件执行",
|
||||||
|
"doc": "+ 必须有三个子节点\n+ 第一个子节点为条件节点\n+ 第二个子节点为条件为 `success` 时执行的节点\n+ 第三个子节点为条件为 `failure` 时执行的节点,\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Includes",
|
||||||
|
"type": "Condition",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "判断元素是否在数组中",
|
||||||
|
"input": [
|
||||||
|
"数组",
|
||||||
|
"元素"
|
||||||
|
],
|
||||||
|
"doc": "+ 若输入的元素不合法,返回 `failure`\n+ 只有数组包含元素时返回 `success`,否则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Index",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "索引输入的数组",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "index",
|
||||||
|
"type": "int?",
|
||||||
|
"desc": "索引",
|
||||||
|
"oneof": "索引"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"input": [
|
||||||
|
"数组",
|
||||||
|
"索引?"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"值"
|
||||||
|
],
|
||||||
|
"doc": "+ 合法元素不包括 `undefined` 和 `null`\n+ 索引数组的时候,第一个元素的索引为 0\n+ 只有索引到有合法元素时候才会返回 `success`,否则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Invert",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"!success",
|
||||||
|
"!failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "反转子节点运行结果",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `success` 时返回 `failure`\n+ 当子节点返回 `failure` 时返回 `success`\n+ 当子节点返回 `error` 时返回 `error`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IsNull",
|
||||||
|
"type": "Condition",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "判断变量是否不存在",
|
||||||
|
"input": [
|
||||||
|
"判断的变量"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JustFailure",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "什么都不干,只返回失败"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JustSuccess",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "什么都不干,只返回成功"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Let",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "定义新的变量名",
|
||||||
|
"input": [
|
||||||
|
"已存在变量名?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "json?",
|
||||||
|
"desc": "值(value)",
|
||||||
|
"oneof": "已存在变量名"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"新变量名"
|
||||||
|
],
|
||||||
|
"doc": "+ 如果有输入变量,则给已有变量重新定义一个名字\n+ 如果`值(value)`为 `null`,则清除变量\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Listen",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "侦听事件",
|
||||||
|
"input": [
|
||||||
|
"目标对象?"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"事件目标?",
|
||||||
|
"事件参数..."
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "event",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "事件",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"source": [
|
||||||
|
{
|
||||||
|
"name": "行为树被中断",
|
||||||
|
"value": "treeInterrupted"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树开始执行前",
|
||||||
|
"value": "treeBeforeTicked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树执行完成后",
|
||||||
|
"value": "treeAfterTicked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树执行成功后",
|
||||||
|
"value": "treeTickedSuccess"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树执行失败后",
|
||||||
|
"value": "treeTickedFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树被清理",
|
||||||
|
"value": "treeCleaned"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 当事件触发时,执行第一个子节点,多个仅执行第一个\n+ 如果子节点返回 `running`,会中断执行并清理执行栈"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Log",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "打印日志",
|
||||||
|
"input": [
|
||||||
|
"日志?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "日志"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "level",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "日志级别",
|
||||||
|
"default": "log",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"source": [
|
||||||
|
{
|
||||||
|
"name": "LOG",
|
||||||
|
"value": "log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "INFO",
|
||||||
|
"value": "info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DEBUG",
|
||||||
|
"value": "debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WARN",
|
||||||
|
"value": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ERROR",
|
||||||
|
"value": "error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Math",
|
||||||
|
"type": "Action",
|
||||||
|
"desc": "执行数学运算",
|
||||||
|
"input": [
|
||||||
|
"参数..."
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"结果"
|
||||||
|
],
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "op",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "数学运算类型",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"source": [
|
||||||
|
{
|
||||||
|
"name": "绝对值",
|
||||||
|
"value": "abs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "向上取整",
|
||||||
|
"value": "ceil"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "向下取整",
|
||||||
|
"value": "floor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "四舍五入",
|
||||||
|
"value": "round"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "符号",
|
||||||
|
"value": "sign"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "正弦",
|
||||||
|
"value": "sin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "余弦",
|
||||||
|
"value": "cos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "正切",
|
||||||
|
"value": "tan"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "反正切2",
|
||||||
|
"value": "atan2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "幂运算",
|
||||||
|
"value": "pow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "平方根",
|
||||||
|
"value": "sqrt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "自然对数",
|
||||||
|
"value": "log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "最小值",
|
||||||
|
"value": "min"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "最大值",
|
||||||
|
"value": "max"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "随机数",
|
||||||
|
"value": "random"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "随机整数",
|
||||||
|
"value": "randInt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "随机浮点数",
|
||||||
|
"value": "randFloat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "求和",
|
||||||
|
"value": "sum"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "平均值",
|
||||||
|
"value": "average"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "乘积",
|
||||||
|
"value": "product"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value1",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "参数1(优先使用)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value2",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "参数2(优先使用)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NotNull",
|
||||||
|
"type": "Condition",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "判断变量是否存在",
|
||||||
|
"input": [
|
||||||
|
"判断的变量"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Now",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "获取当前时间",
|
||||||
|
"output": [
|
||||||
|
"当前时间"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Once",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "只执行一次",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 第一次执行完全部子节点时返回 `success`,之后永远返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Parallel",
|
||||||
|
"type": "Composite",
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"children": -1,
|
||||||
|
"desc": "并行执行",
|
||||||
|
"doc": "+ 并行执行所有子节点\n+ 当有子节点返回 `running` 时,返回 `running` 状态\n+ 执行完所有子节点后,返回 `success`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Push",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "向数组中添加元素",
|
||||||
|
"input": [
|
||||||
|
"数组",
|
||||||
|
"元素"
|
||||||
|
],
|
||||||
|
"doc": "+ 当变量`数组`不是数组类型时返回 `failure`\n+ 其余返回 `success`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Race",
|
||||||
|
"type": "Composite",
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"&failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"children": -1,
|
||||||
|
"desc": "竞争执行",
|
||||||
|
"doc": "+ 并行执行所有子节点\n+ 当有子节点返回 `success` 时,立即返回 `success` 状态,并中断其他子节点\n+ 如果所有子节点返回 `failure`或 `error` 则返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RandomIndex",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "随机返回输入的其中一个!",
|
||||||
|
"input": [
|
||||||
|
"输入目标"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"随机目标",
|
||||||
|
"索引?"
|
||||||
|
],
|
||||||
|
"doc": "+ 合法元素不包括 `undefined` 和 `null`\n+ 在输入数组中,随机返回其中一个\n+ 当输入数组为空时,或者没有合法元素,返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Repeat",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"|running",
|
||||||
|
"|failure"
|
||||||
|
],
|
||||||
|
"desc": "循环执行",
|
||||||
|
"input": [
|
||||||
|
"循环次数?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"type": "int?",
|
||||||
|
"desc": "循环次数",
|
||||||
|
"oneof": "循环次数"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `failure` 时,退出遍历并返回 `failure` 状态\n+ 执行完所有子节点后,返回 `success`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RetryUntilFailure",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"!success",
|
||||||
|
"failure",
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "一直尝试直到子节点返回失败",
|
||||||
|
"input": [
|
||||||
|
"最大尝试次数?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"type": "int?",
|
||||||
|
"desc": "最大尝试次数"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 只有当子节点返回 `failure` 时,才返回 `success`,其它情况返回 `running` 状态\n+ 如果设定了尝试次数,超过指定次数则返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RetryUntilSuccess",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"failure",
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "一直尝试直到子节点返回成功",
|
||||||
|
"input": [
|
||||||
|
"最大尝试次数?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"type": "int?",
|
||||||
|
"desc": "最大尝试次数"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 只有当子节点返回 `success` 时,才返回 `success`,其它情况返回 `running` 状态\n+ 如果设定了尝试次数,超过指定次数则返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Selector",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": -1,
|
||||||
|
"desc": "选择执行",
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"&failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"doc": "+ 一直往下执行,直到有子节点返回 `success` 则返回 `success`\n+ 若全部节点返回 `failure`或 `error` 则返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sequence",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": -1,
|
||||||
|
"status": [
|
||||||
|
"&success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "顺序执行",
|
||||||
|
"doc": "+ 一直往下执行,只有当所有子节点都返回 `success`, 才返回 `success`\n+ 若子节点返回 `failure`,则直接返回 `failure` 状态\n+ 其余情况返回 `running` 状态\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SetField",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "设置对象字段值",
|
||||||
|
"input": [
|
||||||
|
"输入对象",
|
||||||
|
"字段(field)?",
|
||||||
|
"值(value)?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "field",
|
||||||
|
"type": "string?",
|
||||||
|
"desc": "字段(field)",
|
||||||
|
"oneof": "字段(field)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "json?",
|
||||||
|
"desc": "值(value)",
|
||||||
|
"oneof": "值(value)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 对输入对象设置 `field` 和 `value`\n+ 输入参数1必须为对象,否则返回 `failure`\n+ 如果 `field` 不为 `string`, 也返回 `failure`\n+ 如果 `value` 为 `undefined` 或 `null`, 则删除 `field` 的值\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Switch",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": -1,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "分支执行",
|
||||||
|
"doc": "+ 按顺序测试 `Case` 节点的判断条件(第一个子节点)\n+ 若测试返回 `success` 则执行 `Case` 第二个子节点,并返回子节点的执行状态\n+ 若没有判断为 `success` 的 `Case` 节点,则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Timeout",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"|running",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "超时",
|
||||||
|
"input": [
|
||||||
|
"超时时间?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "time",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "超时时间",
|
||||||
|
"oneof": "超时时间"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点执行超时或返回 `failure` 时,返回 `failure`\n+ 其余情况返回子节点的执行状态\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Wait",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "等待",
|
||||||
|
"input": [
|
||||||
|
"等待时间?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "time",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "等待时间",
|
||||||
|
"oneof": "等待时间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "random",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "随机范围"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "等待事件触发",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "event",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "事件名称"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Watchdog",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": 3,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "测试执行",
|
||||||
|
"doc": "+ 必须有3个子节点\n+ 第一个子节点为条件节点,返回 `success` 或 `failure` 状态\n+ 第二个子节点为条件为 `success` 时执行的节点\n+ 第三个子节点为条件为 `failure` 时执行的节点,不管子节点返回什么状态,都直接返回 `failure`\n+ 当第二个子节点返回 `running` 时,由本节点维护其运行状态\n+ 每一次 tick 都会先执行测试节点,只有当测试节点返回 `success`,才执行第二个子节点\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"nodeConf": "node-config.b3-setting",
|
||||||
|
"metadata": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,429 @@
|
|||||||
|
{
|
||||||
|
"$override": {},
|
||||||
|
"custom": {},
|
||||||
|
"desc": "幼年期待机状态树(依据 xlsx:幼年期行为树)",
|
||||||
|
"export": true,
|
||||||
|
"group": [],
|
||||||
|
"import": [],
|
||||||
|
"name": "idle_state",
|
||||||
|
"prefix": "",
|
||||||
|
"root": {
|
||||||
|
"$id": "rootIdle001",
|
||||||
|
"id": "1",
|
||||||
|
"name": "Parallel",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "idleLoop001",
|
||||||
|
"id": "10",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "idleSeq001",
|
||||||
|
"id": "11",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "idleLog001",
|
||||||
|
"id": "12",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "待机: 基础表情 BABY_EMOMID_BASIC_NORMAL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "idleWait001",
|
||||||
|
"id": "13",
|
||||||
|
"name": "Wait",
|
||||||
|
"args": {
|
||||||
|
"time": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "idleCur001",
|
||||||
|
"id": "14",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "待机触发: 好奇 BABY_EMOCHAT_CURIOUS_NORMAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evSwipe001",
|
||||||
|
"id": "20",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evSwipeSeq",
|
||||||
|
"id": "21",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evSwipeWait",
|
||||||
|
"id": "22",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "touch_strip_swipe"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evSwipeIf",
|
||||||
|
"id": "23",
|
||||||
|
"name": "IfElse",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evSwipeCheck",
|
||||||
|
"id": "24",
|
||||||
|
"name": "Check",
|
||||||
|
"args": {
|
||||||
|
"value": "emotionHigh"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evSwipeHigh",
|
||||||
|
"id": "25",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "触摸条左/右滑(高情绪): 撒娇 BABY_EMOLOW_CLINGY_NORMAL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evSwipeLow",
|
||||||
|
"id": "26",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "触摸条左/右滑(低情绪): 委屈 BABY_EMOCHAT_HURT_NORMAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evLong001",
|
||||||
|
"id": "30",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evLongSeq",
|
||||||
|
"id": "31",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evLongWait",
|
||||||
|
"id": "32",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "touch_strip_long"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evLongLog",
|
||||||
|
"id": "33",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "长时间抚摸: 害羞 BABY_EMOCHAT_SHY_NORMAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evTap001",
|
||||||
|
"id": "40",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evTapSeq",
|
||||||
|
"id": "41",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evTapWait",
|
||||||
|
"id": "42",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "touch_strip_tap_once"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evTapLog",
|
||||||
|
"id": "43",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "敲击1下: 疑惑 BABY_EMOCHAT_CONFUSE_NORMAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evLeft001",
|
||||||
|
"id": "50",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evLeftSeq",
|
||||||
|
"id": "51",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evLeftWait",
|
||||||
|
"id": "52",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "left_ear_press"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evLeftLog",
|
||||||
|
"id": "53",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "左耳按一下: 菜单呼出"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evRight001",
|
||||||
|
"id": "60",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evRightSeq",
|
||||||
|
"id": "61",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evRightWait",
|
||||||
|
"id": "62",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "right_ear_press"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evRightLog",
|
||||||
|
"id": "63",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "右耳按一下: 学说话 BABY_EMORAISING_LEARNTOTALK_NORMAL (音频缺失)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evRadarIn01",
|
||||||
|
"id": "70",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evRadarInSq",
|
||||||
|
"id": "71",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evRadarInWt",
|
||||||
|
"id": "72",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "radar_person_enter_front_2_3m"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evRadarInLg",
|
||||||
|
"id": "73",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "雷达2-3m正面走来: 撒娇 BABY_EMOLOW_CLINGY_NORMAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evRadarOut1",
|
||||||
|
"id": "80",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evRadarOutS",
|
||||||
|
"id": "81",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evRadarOutW",
|
||||||
|
"id": "82",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "radar_person_leave_2_3m"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evRadarOutL",
|
||||||
|
"id": "83",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "走出雷达范围: 好奇 BABY_EMOCHAT_CURIOUS_NORMAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evSound001",
|
||||||
|
"id": "90",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evSoundSeq",
|
||||||
|
"id": "91",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evSoundWait",
|
||||||
|
"id": "92",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "sound_localized"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evSoundLog",
|
||||||
|
"id": "93",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "听到声音并定位: 学说话 BABY_EMORAISING_LEARNTOTALK_NORMAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evPickup001",
|
||||||
|
"id": "100",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evPickupSeq",
|
||||||
|
"id": "101",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evPickupW",
|
||||||
|
"id": "102",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "posture_pickup"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evPickupL",
|
||||||
|
"id": "103",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "info",
|
||||||
|
"message": "识别拿起: 好奇 BABY_EMOCHAT_CURIOUS_NORMAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evShake001",
|
||||||
|
"id": "110",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evShakeSeq",
|
||||||
|
"id": "111",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evShakeWait",
|
||||||
|
"id": "112",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "posture_shake"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evShakeLog",
|
||||||
|
"id": "113",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "warn",
|
||||||
|
"message": "识别摇晃: 晕了的表情(表情缺失)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evUpside001",
|
||||||
|
"id": "120",
|
||||||
|
"name": "Repeat",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evUpsideSeq",
|
||||||
|
"id": "121",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"$id": "evUpsideWt",
|
||||||
|
"id": "122",
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"args": {
|
||||||
|
"event": "posture_upside_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$id": "evUpsideLog",
|
||||||
|
"id": "123",
|
||||||
|
"name": "Log",
|
||||||
|
"args": {
|
||||||
|
"level": "warn",
|
||||||
|
"message": "识别倒立: 晕了的表情(表情缺失)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"vars": [
|
||||||
|
{
|
||||||
|
"name": "emotionHigh",
|
||||||
|
"desc": "当前是否高情绪值,供触摸条左/右滑分支判断"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,856 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "AlwaysFailure",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "始终返回失败",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `running` 时,返回 `running`\n+ 其它情况,不管子节点是否成功都返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AlwaysRunning",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "始终返回运行中状态",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 始终返回 `running`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AlwaysSuccess",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "始终返回成功",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `running` 时,返回 `running`\n+ 其它情况,不管子节点是否成功都返回 `success`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Assert",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "断言",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "消息"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `failure` 时,抛出异常\n+ 其余情况返回子节点的执行状态\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Calculate",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "简单的数值公式计算",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "expr",
|
||||||
|
"desc": "计算公式"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"计算结果"
|
||||||
|
],
|
||||||
|
"doc": "+ 做简单的数值公式计算,返回结果到输出\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Case",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": 2,
|
||||||
|
"status": [
|
||||||
|
"&success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "分支选择",
|
||||||
|
"doc": "+ 必须有两个子节点\n+ 第一个子节点为判断条件\n+ 第二个子节点为判断为 `success` 时执行的节点\n+ 此节点不会真正意义的执行,而是交由 `Switch` 节点来执行\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Check",
|
||||||
|
"type": "Condition",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "检查True或False",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "expr",
|
||||||
|
"desc": "值"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 做简单数值公式判定,返回 `success` 或 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Concat",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "将两个输入合并为一个数组,并返回新数组",
|
||||||
|
"input": [
|
||||||
|
"数组1",
|
||||||
|
"数组2"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"新数组"
|
||||||
|
],
|
||||||
|
"doc": "+ 如果输入不是数组,则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Delay",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "延时执行子节点",
|
||||||
|
"input": [
|
||||||
|
"延时时间?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "delay",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "延时时间",
|
||||||
|
"oneof": "延时时间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cacheVars",
|
||||||
|
"type": "string[]?",
|
||||||
|
"desc": "暂存环境变量"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 当延时触发时,执行第一个子节点,多个仅执行第一个\n+ 如果子节点返回 `running`,会中断执行并清理执行栈"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Filter",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "返回满足条件的元素",
|
||||||
|
"input": [
|
||||||
|
"输入数组"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"变量",
|
||||||
|
"新数组"
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 遍历输入数组,将当前元素写入`变量`,满足条件的元素放入新数组\n+ 只有当新数组不为空时,才返回 `success`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ForEach",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"|running",
|
||||||
|
"|failure"
|
||||||
|
],
|
||||||
|
"desc": "遍历数组",
|
||||||
|
"input": [
|
||||||
|
"数组"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"变量",
|
||||||
|
"索引?"
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 遍历输入数组,将当前元素写入`变量`\n+ 当子节点返回 `failure` 时,退出遍历并返回 `failure` 状态\n+ 执行完所有子节点后,返回 `success`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "GetField",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "获取对象的字段值",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "field",
|
||||||
|
"type": "string?",
|
||||||
|
"desc": "字段(field)",
|
||||||
|
"oneof": "字段(field)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"input": [
|
||||||
|
"对象",
|
||||||
|
"字段(field)?"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"字段值(value)"
|
||||||
|
],
|
||||||
|
"doc": "+ 合法元素不包括 `undefined` 和 `null`\n+ 只有获取到合法元素时候才会返回 `success`,否则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IfElse",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": 3,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "条件执行",
|
||||||
|
"doc": "+ 必须有三个子节点\n+ 第一个子节点为条件节点\n+ 第二个子节点为条件为 `success` 时执行的节点\n+ 第三个子节点为条件为 `failure` 时执行的节点,\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Includes",
|
||||||
|
"type": "Condition",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "判断元素是否在数组中",
|
||||||
|
"input": [
|
||||||
|
"数组",
|
||||||
|
"元素"
|
||||||
|
],
|
||||||
|
"doc": "+ 若输入的元素不合法,返回 `failure`\n+ 只有数组包含元素时返回 `success`,否则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Index",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "索引输入的数组",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "index",
|
||||||
|
"type": "int?",
|
||||||
|
"desc": "索引",
|
||||||
|
"oneof": "索引"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"input": [
|
||||||
|
"数组",
|
||||||
|
"索引?"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"值"
|
||||||
|
],
|
||||||
|
"doc": "+ 合法元素不包括 `undefined` 和 `null`\n+ 索引数组的时候,第一个元素的索引为 0\n+ 只有索引到有合法元素时候才会返回 `success`,否则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Invert",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"!success",
|
||||||
|
"!failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "反转子节点运行结果",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `success` 时返回 `failure`\n+ 当子节点返回 `failure` 时返回 `success`\n+ 当子节点返回 `error` 时返回 `error`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IsNull",
|
||||||
|
"type": "Condition",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "判断变量是否不存在",
|
||||||
|
"input": [
|
||||||
|
"判断的变量"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JustFailure",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "什么都不干,只返回失败"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JustSuccess",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "什么都不干,只返回成功"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Let",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "定义新的变量名",
|
||||||
|
"input": [
|
||||||
|
"已存在变量名?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "json?",
|
||||||
|
"desc": "值(value)",
|
||||||
|
"oneof": "已存在变量名"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"新变量名"
|
||||||
|
],
|
||||||
|
"doc": "+ 如果有输入变量,则给已有变量重新定义一个名字\n+ 如果`值(value)`为 `null`,则清除变量\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Listen",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "侦听事件",
|
||||||
|
"input": [
|
||||||
|
"目标对象?"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"事件目标?",
|
||||||
|
"事件参数..."
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "event",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "事件",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"source": [
|
||||||
|
{
|
||||||
|
"name": "行为树被中断",
|
||||||
|
"value": "treeInterrupted"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树开始执行前",
|
||||||
|
"value": "treeBeforeTicked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树执行完成后",
|
||||||
|
"value": "treeAfterTicked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树执行成功后",
|
||||||
|
"value": "treeTickedSuccess"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树执行失败后",
|
||||||
|
"value": "treeTickedFailure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "行为树被清理",
|
||||||
|
"value": "treeCleaned"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 当事件触发时,执行第一个子节点,多个仅执行第一个\n+ 如果子节点返回 `running`,会中断执行并清理执行栈"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Log",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "打印日志",
|
||||||
|
"input": [
|
||||||
|
"日志?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "日志"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "level",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "日志级别",
|
||||||
|
"default": "log",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"source": [
|
||||||
|
{
|
||||||
|
"name": "LOG",
|
||||||
|
"value": "log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "INFO",
|
||||||
|
"value": "info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DEBUG",
|
||||||
|
"value": "debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WARN",
|
||||||
|
"value": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ERROR",
|
||||||
|
"value": "error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Math",
|
||||||
|
"type": "Action",
|
||||||
|
"desc": "执行数学运算",
|
||||||
|
"input": [
|
||||||
|
"参数..."
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"结果"
|
||||||
|
],
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "op",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "数学运算类型",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"source": [
|
||||||
|
{
|
||||||
|
"name": "绝对值",
|
||||||
|
"value": "abs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "向上取整",
|
||||||
|
"value": "ceil"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "向下取整",
|
||||||
|
"value": "floor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "四舍五入",
|
||||||
|
"value": "round"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "符号",
|
||||||
|
"value": "sign"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "正弦",
|
||||||
|
"value": "sin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "余弦",
|
||||||
|
"value": "cos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "正切",
|
||||||
|
"value": "tan"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "反正切2",
|
||||||
|
"value": "atan2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "幂运算",
|
||||||
|
"value": "pow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "平方根",
|
||||||
|
"value": "sqrt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "自然对数",
|
||||||
|
"value": "log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "最小值",
|
||||||
|
"value": "min"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "最大值",
|
||||||
|
"value": "max"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "随机数",
|
||||||
|
"value": "random"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "随机整数",
|
||||||
|
"value": "randInt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "随机浮点数",
|
||||||
|
"value": "randFloat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "求和",
|
||||||
|
"value": "sum"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "平均值",
|
||||||
|
"value": "average"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "乘积",
|
||||||
|
"value": "product"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value1",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "参数1(优先使用)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value2",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "参数2(优先使用)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NotNull",
|
||||||
|
"type": "Condition",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "判断变量是否存在",
|
||||||
|
"input": [
|
||||||
|
"判断的变量"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Now",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success"
|
||||||
|
],
|
||||||
|
"desc": "获取当前时间",
|
||||||
|
"output": [
|
||||||
|
"当前时间"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Once",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "只执行一次",
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 第一次执行完全部子节点时返回 `success`,之后永远返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Parallel",
|
||||||
|
"type": "Composite",
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"children": -1,
|
||||||
|
"desc": "并行执行",
|
||||||
|
"doc": "+ 并行执行所有子节点\n+ 当有子节点返回 `running` 时,返回 `running` 状态\n+ 执行完所有子节点后,返回 `success`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Push",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "向数组中添加元素",
|
||||||
|
"input": [
|
||||||
|
"数组",
|
||||||
|
"元素"
|
||||||
|
],
|
||||||
|
"doc": "+ 当变量`数组`不是数组类型时返回 `failure`\n+ 其余返回 `success`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Race",
|
||||||
|
"type": "Composite",
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"&failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"children": -1,
|
||||||
|
"desc": "竞争执行",
|
||||||
|
"doc": "+ 并行执行所有子节点\n+ 当有子节点返回 `success` 时,立即返回 `success` 状态,并中断其他子节点\n+ 如果所有子节点返回 `failure`或 `error` 则返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RandomIndex",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "随机返回输入的其中一个!",
|
||||||
|
"input": [
|
||||||
|
"输入目标"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"随机目标",
|
||||||
|
"索引?"
|
||||||
|
],
|
||||||
|
"doc": "+ 合法元素不包括 `undefined` 和 `null`\n+ 在输入数组中,随机返回其中一个\n+ 当输入数组为空时,或者没有合法元素,返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Repeat",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"|running",
|
||||||
|
"|failure"
|
||||||
|
],
|
||||||
|
"desc": "循环执行",
|
||||||
|
"input": [
|
||||||
|
"循环次数?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"type": "int?",
|
||||||
|
"desc": "循环次数",
|
||||||
|
"oneof": "循环次数"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点返回 `failure` 时,退出遍历并返回 `failure` 状态\n+ 执行完所有子节点后,返回 `success`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RetryUntilFailure",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"!success",
|
||||||
|
"failure",
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "一直尝试直到子节点返回失败",
|
||||||
|
"input": [
|
||||||
|
"最大尝试次数?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"type": "int?",
|
||||||
|
"desc": "最大尝试次数"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 只有当子节点返回 `failure` 时,才返回 `success`,其它情况返回 `running` 状态\n+ 如果设定了尝试次数,超过指定次数则返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RetryUntilSuccess",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"failure",
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "一直尝试直到子节点返回成功",
|
||||||
|
"input": [
|
||||||
|
"最大尝试次数?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"type": "int?",
|
||||||
|
"desc": "最大尝试次数"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 只有当子节点返回 `success` 时,才返回 `success`,其它情况返回 `running` 状态\n+ 如果设定了尝试次数,超过指定次数则返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Selector",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": -1,
|
||||||
|
"desc": "选择执行",
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"&failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"doc": "+ 一直往下执行,直到有子节点返回 `success` 则返回 `success`\n+ 若全部节点返回 `failure`或 `error` 则返回 `failure`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sequence",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": -1,
|
||||||
|
"status": [
|
||||||
|
"&success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "顺序执行",
|
||||||
|
"doc": "+ 一直往下执行,只有当所有子节点都返回 `success`, 才返回 `success`\n+ 若子节点返回 `failure`,则直接返回 `failure` 状态\n+ 其余情况返回 `running` 状态\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SetField",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "设置对象字段值",
|
||||||
|
"input": [
|
||||||
|
"输入对象",
|
||||||
|
"字段(field)?",
|
||||||
|
"值(value)?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "field",
|
||||||
|
"type": "string?",
|
||||||
|
"desc": "字段(field)",
|
||||||
|
"oneof": "字段(field)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "json?",
|
||||||
|
"desc": "值(value)",
|
||||||
|
"oneof": "值(value)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 对输入对象设置 `field` 和 `value`\n+ 输入参数1必须为对象,否则返回 `failure`\n+ 如果 `field` 不为 `string`, 也返回 `failure`\n+ 如果 `value` 为 `undefined` 或 `null`, 则删除 `field` 的值\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Switch",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": -1,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "分支执行",
|
||||||
|
"doc": "+ 按顺序测试 `Case` 节点的判断条件(第一个子节点)\n+ 若测试返回 `success` 则执行 `Case` 第二个子节点,并返回子节点的执行状态\n+ 若没有判断为 `success` 的 `Case` 节点,则返回 `failure`\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Timeout",
|
||||||
|
"type": "Decorator",
|
||||||
|
"children": 1,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"|running",
|
||||||
|
"failure"
|
||||||
|
],
|
||||||
|
"desc": "超时",
|
||||||
|
"input": [
|
||||||
|
"超时时间?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "time",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "超时时间",
|
||||||
|
"oneof": "超时时间"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doc": "+ 只能有一个子节点,多个仅执行第一个\n+ 当子节点执行超时或返回 `failure` 时,返回 `failure`\n+ 其余情况返回子节点的执行状态\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Wait",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "等待",
|
||||||
|
"input": [
|
||||||
|
"等待时间?"
|
||||||
|
],
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "time",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "等待时间",
|
||||||
|
"oneof": "等待时间"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "random",
|
||||||
|
"type": "float?",
|
||||||
|
"desc": "随机范围"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WaitForEvent",
|
||||||
|
"type": "Action",
|
||||||
|
"children": 0,
|
||||||
|
"status": [
|
||||||
|
"success",
|
||||||
|
"running"
|
||||||
|
],
|
||||||
|
"desc": "等待事件触发",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "event",
|
||||||
|
"type": "string",
|
||||||
|
"desc": "事件名称"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Watchdog",
|
||||||
|
"type": "Composite",
|
||||||
|
"children": 3,
|
||||||
|
"status": [
|
||||||
|
"|success",
|
||||||
|
"|failure",
|
||||||
|
"|running"
|
||||||
|
],
|
||||||
|
"desc": "测试执行",
|
||||||
|
"doc": "+ 必须有3个子节点\n+ 第一个子节点为条件节点,返回 `success` 或 `failure` 状态\n+ 第二个子节点为条件为 `success` 时执行的节点\n+ 第三个子节点为条件为 `failure` 时执行的节点,不管子节点返回什么状态,都直接返回 `failure`\n+ 当第二个子节点返回 `running` 时,由本节点维护其运行状态\n+ 每一次 tick 都会先执行测试节点,只有当测试节点返回 `success`,才执行第二个子节点\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"nodeConf": "node-config.b3-setting",
|
||||||
|
"metadata": [],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "example.json",
|
||||||
|
"desc": "幼年期待机状态树"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"checkExpr": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Reference in new issue
Block a user