mirror of
https://gitee.com/sli97/behavior-eden-coco-plugin.git
synced 2024-12-25 03:09:09 +00:00
version:1.0.1
This commit is contained in:
parent
47104caa6e
commit
2ee8352e9d
18
CHANGELOG.md
Normal file
18
CHANGELOG.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
# 1.0.1 / 2023-09-26
|
||||
|
||||
**调整**
|
||||
|
||||
- 行为树编辑器面板默认在`Cocos`主编辑器内打开(场景 Panel 旁边),更方便用户操作。
|
||||
- 切换`层级管理器`节点时,会自动切换为当前节点的行为树,如果当前节点没有行为树,则显示 mask 提示用户`添加组件`并`创建json`。
|
||||
- 修改`BehaviorEditor`组件按钮文案为`Create / Editor`。
|
||||
- 在已经打开行为树编辑器的情况下,再次点击`BehaviorEditor`组件的`Create / Editor`,可以起到重启插件的效果,在更新了外部资源(JSON,组件)。
|
||||
- 画布大小从`2400 1600`下降为` 2000 1400`。
|
||||
- 禁止设置非组合节点`Composite`的`Abort Type`属性。
|
||||
- 左下角增加`Root`按钮回到根节点处
|
||||
- 样式修改
|
||||
|
||||
# 1.0.0 / 2023-09-21
|
||||
|
||||
**首次发布**
|
12
dist/contributions/inspector/behavior-editor.js
vendored
12
dist/contributions/inspector/behavior-editor.js
vendored
@ -3,8 +3,8 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ready = exports.update = exports.$ = exports.template = void 0;
|
||||
exports.template = `
|
||||
<div style="display:flex;justify-content:center;align-items:center;margin-top:10px;padding-right:8px;">
|
||||
<ui-button class="editor" style="height:24px;padding:0 16px;">Edit Tree</ui-prop>
|
||||
<div style="display:flex;justify-content:center;align-items:center;margin-top:10px;">
|
||||
<ui-button class="editor" style="height:24px;padding:0 16px;">Create / Edit</ui-prop>
|
||||
</div>
|
||||
|
||||
`;
|
||||
@ -64,15 +64,17 @@ function ready() {
|
||||
});
|
||||
if (success) {
|
||||
console.log(`JSON文件挂载成功`);
|
||||
Editor.Message.request("behavior-eden", "open-panel");
|
||||
}
|
||||
else {
|
||||
console.warn("JSON文件挂载失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 打开插件面板
|
||||
Editor.Message.request("behavior-eden", "open-panel");
|
||||
const success = await Editor.Message.request("behavior-eden", "open-panel");
|
||||
// success为false,可能是已经打开了,通知面板刷新
|
||||
if (!success) {
|
||||
await Editor.Message.request("behavior-eden", "refresh-panel");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
9
dist/main.js
vendored
9
dist/main.js
vendored
@ -11,8 +11,13 @@ const package_json_1 = __importDefault(require("../package.json"));
|
||||
* @zh 为扩展的主进程的注册方法
|
||||
*/
|
||||
exports.methods = {
|
||||
openPanel() {
|
||||
Editor.Panel.open(package_json_1.default.name);
|
||||
async openPanel() {
|
||||
return await Editor.Panel.openBeside("scene", package_json_1.default.name);
|
||||
},
|
||||
async refreshPanel() {
|
||||
await Editor.Panel.close(package_json_1.default.name);
|
||||
await new Promise((rs) => setTimeout(rs, 300));
|
||||
await Editor.Panel.openBeside("scene", package_json_1.default.name);
|
||||
},
|
||||
};
|
||||
/**
|
||||
|
325
dist/panels/default/index.js
vendored
325
dist/panels/default/index.js
vendored
@ -10,8 +10,8 @@ const node_1 = require("../../runtime/node");
|
||||
const decorator_1 = require("../../runtime/core/decorator");
|
||||
const utils_1 = require("../../runtime/core/utils");
|
||||
// 画布宽高
|
||||
const CANVAS_WIDTH = 2400;
|
||||
const CANVAS_HEIGHT = 1600;
|
||||
const CANVAS_WIDTH = 2000;
|
||||
const CANVAS_HEIGHT = 1400;
|
||||
// 节点box属性
|
||||
const BOX_WIDTH = 100;
|
||||
// const BOX_HEIGHT = 64; //height通过getBoxHeight方法动态计算
|
||||
@ -141,6 +141,21 @@ const ParentNodes = [...decorator_1.nodeClsMap.values()]
|
||||
return false;
|
||||
})
|
||||
.map((cls) => cls.name);
|
||||
/***
|
||||
* 获取所有Composite类型的节点
|
||||
*/
|
||||
const CompositeNodes = [...decorator_1.nodeClsMap.values()]
|
||||
.filter((cls) => {
|
||||
let parentCls = getParentCls(cls);
|
||||
while (parentCls) {
|
||||
if (parentCls.name === node_1.Composite.name) {
|
||||
return true;
|
||||
}
|
||||
parentCls = getParentCls(parentCls);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map((cls) => cls.name);
|
||||
const activeCursor = () => {
|
||||
stage && (stage.container().style.cursor = "pointer");
|
||||
};
|
||||
@ -151,7 +166,7 @@ const component = lib_1.Vue.extend({
|
||||
template: lib_1.fs.readFileSync(path_1.default.join(__dirname, "../../../src/panels/static/template/vue/app.html"), "utf-8"),
|
||||
$: {
|
||||
tree: "#tree",
|
||||
left: "#left",
|
||||
scroll: "#scroll",
|
||||
},
|
||||
filters: {
|
||||
toUpperCase(value) {
|
||||
@ -189,6 +204,7 @@ const component = lib_1.Vue.extend({
|
||||
*/
|
||||
nodeCompMethodInfo: {},
|
||||
logs: [],
|
||||
maskText: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -253,12 +269,143 @@ const component = lib_1.Vue.extend({
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
this.initCanvas();
|
||||
await this.getAssets();
|
||||
await this.selectCurrentAsset();
|
||||
this.$refs.left.scroll((CANVAS_WIDTH - this.$refs.left.getBoundingClientRect().width) / 2, ROOT_Y - 50);
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
/***
|
||||
* 初始化相关
|
||||
*/
|
||||
async init() {
|
||||
// 初始化画布
|
||||
this.initCanvas();
|
||||
// 根据当前选中节点初始化行为树
|
||||
await this.initSelection();
|
||||
this.backRoot();
|
||||
},
|
||||
initCanvas() {
|
||||
// 初始化舞台
|
||||
stage = new lib_1.Konva.Stage({
|
||||
container: this.$refs.tree,
|
||||
width: CANVAS_WIDTH,
|
||||
height: CANVAS_HEIGHT,
|
||||
});
|
||||
// 基础层,目前只有一层,每层都会生成一个canvas
|
||||
baseLayer = new lib_1.Konva.Layer();
|
||||
// 背景组
|
||||
bgGroup = this.generateBg();
|
||||
baseLayer.add(bgGroup);
|
||||
// 静态箭头层
|
||||
staticArrowGroup = new lib_1.Konva.Group();
|
||||
baseLayer.add(staticArrowGroup);
|
||||
// 动态箭头层(用户手动拉出来的箭头)
|
||||
dynamicArrowGroup = this.generateDynamicArrowGroup();
|
||||
baseLayer.add(dynamicArrowGroup);
|
||||
// Root组
|
||||
rootGroup = this.generateRoot();
|
||||
baseLayer.add(rootGroup);
|
||||
// 节点层
|
||||
nodeGroup = new lib_1.Konva.Group();
|
||||
baseLayer.add(nodeGroup);
|
||||
// 框选层
|
||||
selectionGroup = this.generatesSelectionGroup();
|
||||
baseLayer.add(selectionGroup);
|
||||
// 层添加到舞台
|
||||
stage.add(baseLayer);
|
||||
},
|
||||
// 获取json文件
|
||||
async initAssets() {
|
||||
const behaviorTreeComponentUuids = [];
|
||||
const dfs = (node) => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
for (const comp of node.components) {
|
||||
if (comp.type === BTreeCompName) {
|
||||
behaviorTreeComponentUuids.push(comp.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const item of node.children) {
|
||||
dfs(item);
|
||||
}
|
||||
};
|
||||
// 获取场景节点树
|
||||
const sceneNode = await Editor.Message.request("scene", "query-node-tree");
|
||||
// 收集场景上所有BehaviorTree组件uuid
|
||||
dfs(sceneNode);
|
||||
// 收集BehaviorTree组件上的json路径
|
||||
const rawUrls = await Promise.all(behaviorTreeComponentUuids.map((uuid) => Editor.Message.request("scene", "execute-component-method", {
|
||||
uuid: uuid,
|
||||
name: "getAssetUrl",
|
||||
})));
|
||||
// 过滤空的并去重
|
||||
const urls = [...new Set(rawUrls.filter(Boolean))];
|
||||
// 根据url获取所有json文件信息
|
||||
const assets = await Promise.all(urls.map((url) => Editor.Message.request("asset-db", "query-asset-info", url)));
|
||||
this.assets = assets.map(({ name, source, file }) => ({ name, url: source, file: file, content: "" }));
|
||||
},
|
||||
async initSelection() {
|
||||
// 找到当前选中的节点
|
||||
const node = await Editor.Message.request("scene", "query-node", Editor.Selection.getSelected("node"));
|
||||
// 未选中节点或者选中的是场景节点(场景节点不能添加组件)
|
||||
if (!(node === null || node === void 0 ? void 0 : node.__comps__)) {
|
||||
this.maskText = "请选中一个非场景根节点非空名称节点来开始行为树制作";
|
||||
return;
|
||||
}
|
||||
// 找到BehaviorTree组件
|
||||
const index = node.__comps__.findIndex((v) => v.type === BTreeCompName);
|
||||
if (index === -1) {
|
||||
this.maskText = "要制作行为树,需要先为当前节点添加行为树组件";
|
||||
return;
|
||||
}
|
||||
const comp = node.__comps__[index];
|
||||
// 调用BehaviorTree组件上的方法
|
||||
const url = await Editor.Message.request("scene", "execute-component-method", {
|
||||
uuid: comp.value.uuid.value,
|
||||
name: "getAssetUrl",
|
||||
});
|
||||
// JSON文件不存在
|
||||
if (!url) {
|
||||
this.maskText = "当前行为树组件缺少JSON资源,点击BehaviorEditor组件按钮即可创建";
|
||||
return;
|
||||
}
|
||||
this.maskText = "";
|
||||
// 选中此文件,设置好currentAsset才能把组件nodes数据同步到JSON
|
||||
this.handleSelectAsset(url);
|
||||
},
|
||||
async handleSelectAsset(url) {
|
||||
var _a;
|
||||
// 实时获取当前场景所有behaviorTree组件上的json文件
|
||||
await this.initAssets();
|
||||
const json = this.assets.find((e) => e.url === url);
|
||||
if (!json) {
|
||||
this.showWarn("JSON文件不存在");
|
||||
return;
|
||||
}
|
||||
if (((_a = this.currentAsset) === null || _a === void 0 ? void 0 : _a.url) === (json === null || json === void 0 ? void 0 : json.url)) {
|
||||
return;
|
||||
}
|
||||
this.currentAsset = json;
|
||||
try {
|
||||
const content = await lib_1.fs.readJSONSync(json.file);
|
||||
this.currentAsset.content = content;
|
||||
this.render();
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
// JSON文件语法异常的情况下,初始化内容
|
||||
this.showWarn("JSON文件内容初始化成功");
|
||||
const content = { nodes: [] };
|
||||
await lib_1.fs.writeJSONSync(json.file, content);
|
||||
this.currentAsset.content = content;
|
||||
this.render();
|
||||
}
|
||||
else {
|
||||
// 输出其他异常
|
||||
this.showWarn(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
handlePanelChange(value) {
|
||||
this.currentPanel = value;
|
||||
},
|
||||
@ -275,7 +422,7 @@ const component = lib_1.Vue.extend({
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
async handleEventNodeChange(lifeCycle, uuid = "") {
|
||||
async handleEventNodeChange(lifeCycle, uuid = "", shouldSave = true) {
|
||||
if (!this.currentNode) {
|
||||
this.showWarn("当前节点不存在");
|
||||
return;
|
||||
@ -287,9 +434,13 @@ const component = lib_1.Vue.extend({
|
||||
else {
|
||||
this.currentNode.event[lifeCycle].comp = "";
|
||||
this.currentNode.event[lifeCycle].method = "";
|
||||
this.currentNode.event[lifeCycle].data = "";
|
||||
// 事件参数就不手动清空了
|
||||
// this.currentNode.event[lifeCycle].data = "";
|
||||
}
|
||||
// 点击canvas某个节点的时候,会触发handleEventNodeChange获取场景节点数据,此时不保存数据
|
||||
if (shouldSave) {
|
||||
await this.saveAsset();
|
||||
}
|
||||
},
|
||||
async getNodeCompMethods(lifeCycle, uuid) {
|
||||
const [nodeInfo, compMethodInfo] = await Promise.all([
|
||||
@ -331,132 +482,16 @@ const component = lib_1.Vue.extend({
|
||||
this.currentNode.event[lifeCycle].data = data;
|
||||
await this.saveAsset();
|
||||
},
|
||||
async selectCurrentAsset() {
|
||||
// 找到点击组件的节点
|
||||
const node = await Editor.Message.request("scene", "query-node", Editor.Selection.getSelected("node"));
|
||||
if (!node) {
|
||||
this.showWarn(`未选中节点`);
|
||||
return;
|
||||
}
|
||||
// 找到BehaviorTree组件
|
||||
const index = node.__comps__.findIndex((v) => v.type === BTreeCompName);
|
||||
if (index === -1) {
|
||||
this.showWarn(`节点未挂载【${BTreeCompName}】组件`);
|
||||
return;
|
||||
}
|
||||
const comp = node.__comps__[index];
|
||||
// 调用BehaviorTree组件上的方法
|
||||
const url = await Editor.Message.request("scene", "execute-component-method", {
|
||||
uuid: comp.value.uuid.value,
|
||||
name: "getAssetUrl",
|
||||
});
|
||||
// JSON文件不存在
|
||||
if (!url) {
|
||||
this.showWarn(`【${BTreeCompName}】组件未指定JSON文件`);
|
||||
return;
|
||||
}
|
||||
// 选中此文件,设置好currentAsset才能把组件nodes数据同步到JSON
|
||||
this.handleSelectAsset(url);
|
||||
},
|
||||
async handleSelectAsset(url) {
|
||||
const json = this.assets.find((e) => e.url === url);
|
||||
if (!json) {
|
||||
this.showWarn("JSON文件不存在");
|
||||
return;
|
||||
}
|
||||
this.currentAsset = json;
|
||||
try {
|
||||
const content = await lib_1.fs.readJSONSync(json.file);
|
||||
this.currentAsset.content = content;
|
||||
this.render();
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
// JSON文件语法异常的情况下,初始化内容
|
||||
this.showWarn("JSON文件内容初始化成功");
|
||||
const content = { nodes: [] };
|
||||
await lib_1.fs.writeJSONSync(json.file, content);
|
||||
this.currentAsset.content = content;
|
||||
this.render();
|
||||
}
|
||||
else {
|
||||
// 输出其他异常
|
||||
this.showWarn(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 全部所有behaviorTree组件上使用到的json文件
|
||||
async getAssets() {
|
||||
const behaviorTreeComponentUuids = [];
|
||||
const dfs = (node) => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
for (const comp of node.components) {
|
||||
if (comp.type === BTreeCompName) {
|
||||
behaviorTreeComponentUuids.push(comp.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const item of node.children) {
|
||||
dfs(item);
|
||||
}
|
||||
};
|
||||
// 获取场景节点树
|
||||
const sceneNode = await Editor.Message.request("scene", "query-node-tree");
|
||||
// 收集场景上所有BehaviorTree组件uuid
|
||||
dfs(sceneNode);
|
||||
// 收集BehaviorTree组件上的json路径
|
||||
const rawUrls = await Promise.all(behaviorTreeComponentUuids.map((uuid) => Editor.Message.request("scene", "execute-component-method", {
|
||||
uuid: uuid,
|
||||
name: "getAssetUrl",
|
||||
})));
|
||||
// 过滤空的并去重
|
||||
const urls = [...new Set(rawUrls.filter(Boolean))];
|
||||
// 根据url获取所有json文件信息
|
||||
const assets = await Promise.all(urls.map((url) => Editor.Message.request("asset-db", "query-asset-info", url)));
|
||||
this.assets = assets.map(({ name, source, file }) => ({ name, url: source, file: file, content: "" }));
|
||||
},
|
||||
async saveAsset() {
|
||||
var _a;
|
||||
const url = (_a = this.currentAsset) === null || _a === void 0 ? void 0 : _a.url;
|
||||
if (!url) {
|
||||
const file = (_a = this.currentAsset) === null || _a === void 0 ? void 0 : _a.file;
|
||||
if (!file) {
|
||||
this.showWarn("数据存储失败,未指定JSON");
|
||||
return;
|
||||
}
|
||||
const content = JSON.stringify(this.currentAsset.content, null, 2);
|
||||
const content = this.currentAsset.content;
|
||||
// 修改json文件
|
||||
await Editor.Message.request("asset-db", "save-asset", url, content);
|
||||
},
|
||||
initCanvas() {
|
||||
// 初始化舞台
|
||||
stage = new lib_1.Konva.Stage({
|
||||
container: this.$refs.tree,
|
||||
width: CANVAS_WIDTH,
|
||||
height: CANVAS_HEIGHT,
|
||||
});
|
||||
// 基础层,目前只有一层,每层都会生成一个canvas
|
||||
baseLayer = new lib_1.Konva.Layer();
|
||||
// 背景组
|
||||
bgGroup = this.generateBg();
|
||||
baseLayer.add(bgGroup);
|
||||
// 静态箭头层
|
||||
staticArrowGroup = new lib_1.Konva.Group();
|
||||
baseLayer.add(staticArrowGroup);
|
||||
// 动态箭头层(用户手动拉出来的箭头)
|
||||
dynamicArrowGroup = this.generateDynamicArrowGroup();
|
||||
baseLayer.add(dynamicArrowGroup);
|
||||
// Root组
|
||||
rootGroup = this.generateRoot();
|
||||
baseLayer.add(rootGroup);
|
||||
// 节点层
|
||||
nodeGroup = new lib_1.Konva.Group();
|
||||
baseLayer.add(nodeGroup);
|
||||
// 框选层
|
||||
selectionGroup = this.generatesSelectionGroup();
|
||||
baseLayer.add(selectionGroup);
|
||||
// 层添加到舞台
|
||||
stage.add(baseLayer);
|
||||
lib_1.fs.writeJSONSync(file, content);
|
||||
},
|
||||
/***
|
||||
* 渲染画布
|
||||
@ -568,7 +603,7 @@ const component = lib_1.Vue.extend({
|
||||
type,
|
||||
abortType: this.AbortType.None,
|
||||
x: (CANVAS_WIDTH - BOX_WIDTH) / 2,
|
||||
y: CANVAS_HEIGHT / 4,
|
||||
y: ROOT_Y + 100,
|
||||
isRoot: false,
|
||||
children: [],
|
||||
event: {
|
||||
@ -828,7 +863,7 @@ const component = lib_1.Vue.extend({
|
||||
if (e.evt.buttons === 4) {
|
||||
const movementX = e.evt.movementX;
|
||||
const movementY = e.evt.movementY;
|
||||
this.$refs.left.scrollBy(-movementX, -movementY);
|
||||
this.$refs.scroll.scrollBy(-movementX, -movementY);
|
||||
return;
|
||||
}
|
||||
if (!selection.visible()) {
|
||||
@ -981,11 +1016,11 @@ const component = lib_1.Vue.extend({
|
||||
selectedBoxes = [];
|
||||
this.currentNode = node;
|
||||
this.handlePanelChange(1);
|
||||
this.render();
|
||||
// 获取节点事件相关信息
|
||||
this.handleEventNodeChange(this.onStart, this.currentNode.event[this.onStart].node);
|
||||
this.handleEventNodeChange(this.onUpdate, this.currentNode.event[this.onUpdate].node);
|
||||
this.handleEventNodeChange(this.onEnd, this.currentNode.event[this.onEnd].node);
|
||||
this.handleEventNodeChange(this.onStart, this.currentNode.event[this.onStart].node, false);
|
||||
this.handleEventNodeChange(this.onUpdate, this.currentNode.event[this.onUpdate].node, false);
|
||||
this.handleEventNodeChange(this.onEnd, this.currentNode.event[this.onEnd].node, false);
|
||||
this.render();
|
||||
});
|
||||
return wrapper;
|
||||
},
|
||||
@ -1247,6 +1282,12 @@ const component = lib_1.Vue.extend({
|
||||
}
|
||||
return BOX_HEIGHT;
|
||||
},
|
||||
isComposite(node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
return CompositeNodes.includes(node.type);
|
||||
},
|
||||
showWarn(text) {
|
||||
let time = getTime();
|
||||
const content = `${time}:${text}`;
|
||||
@ -1256,6 +1297,11 @@ const component = lib_1.Vue.extend({
|
||||
this.logs.push({ id: uuid(), content });
|
||||
console.warn(content);
|
||||
},
|
||||
// 视角回到root节点
|
||||
backRoot() {
|
||||
var _a, _b;
|
||||
(_b = (_a = this.$refs) === null || _a === void 0 ? void 0 : _a.scroll) === null || _b === void 0 ? void 0 : _b.scroll((CANVAS_WIDTH - this.$refs.scroll.getBoundingClientRect().width) / 2, ROOT_Y - 50);
|
||||
},
|
||||
},
|
||||
});
|
||||
const panelDataMap = new WeakMap();
|
||||
@ -1281,7 +1327,14 @@ module.exports = Editor.Panel.define({
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {},
|
||||
methods: {
|
||||
initSelection() {
|
||||
const vm = panelDataMap.get(this);
|
||||
if (vm) {
|
||||
vm.initSelection();
|
||||
}
|
||||
},
|
||||
},
|
||||
ready() {
|
||||
if (this.$.app) {
|
||||
const vm = new component();
|
||||
|
2
dist/runtime/core/Blackboard.js
vendored
2
dist/runtime/core/Blackboard.js
vendored
@ -22,5 +22,5 @@ class Blackboard {
|
||||
this.map.clear();
|
||||
}
|
||||
}
|
||||
exports.Blackboard = Blackboard;
|
||||
Blackboard.map = new Map();
|
||||
exports.Blackboard = Blackboard;
|
||||
|
@ -37,9 +37,14 @@
|
||||
"openPanel"
|
||||
]
|
||||
},
|
||||
"send-to-panel": {
|
||||
"refresh-panel": {
|
||||
"methods": [
|
||||
"default.hello"
|
||||
"refreshPanel"
|
||||
]
|
||||
},
|
||||
"selection:select": {
|
||||
"methods": [
|
||||
"default.initSelection"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -3,8 +3,8 @@
|
||||
type Selector<$> = { $: Record<keyof $, any | null> };
|
||||
|
||||
export const template = `
|
||||
<div style="display:flex;justify-content:center;align-items:center;margin-top:10px;padding-right:8px;">
|
||||
<ui-button class="editor" style="height:24px;padding:0 16px;">Edit Tree</ui-prop>
|
||||
<div style="display:flex;justify-content:center;align-items:center;margin-top:10px;">
|
||||
<ui-button class="editor" style="height:24px;padding:0 16px;">Create / Edit</ui-prop>
|
||||
</div>
|
||||
|
||||
`;
|
||||
@ -69,13 +69,16 @@ export function ready(this: Selector<typeof $> & any) {
|
||||
});
|
||||
if (success) {
|
||||
console.log(`JSON文件挂载成功`);
|
||||
Editor.Message.request("behavior-eden", "open-panel");
|
||||
} else {
|
||||
console.warn("JSON文件挂载失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 打开插件面板
|
||||
Editor.Message.request("behavior-eden", "open-panel");
|
||||
const success = await Editor.Message.request("behavior-eden", "open-panel");
|
||||
// success为false,可能是已经打开了,通知面板刷新
|
||||
if (!success) {
|
||||
await Editor.Message.request("behavior-eden", "refresh-panel");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -6,8 +6,13 @@ import packageJSON from "../package.json";
|
||||
* @zh 为扩展的主进程的注册方法
|
||||
*/
|
||||
export const methods: { [key: string]: (...any: any) => any } = {
|
||||
openPanel() {
|
||||
Editor.Panel.open(packageJSON.name);
|
||||
async openPanel() {
|
||||
return await Editor.Panel.openBeside("scene", packageJSON.name);
|
||||
},
|
||||
async refreshPanel() {
|
||||
await Editor.Panel.close(packageJSON.name);
|
||||
await new Promise((rs) => setTimeout(rs, 300));
|
||||
await Editor.Panel.openBeside("scene", packageJSON.name);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -6,8 +6,8 @@ import { nodeClsMap } from "../../runtime/core/decorator";
|
||||
import { buildTree, preOrder } from "../../runtime/core/utils";
|
||||
|
||||
// 画布宽高
|
||||
const CANVAS_WIDTH = 2400;
|
||||
const CANVAS_HEIGHT = 1600;
|
||||
const CANVAS_WIDTH = 2000;
|
||||
const CANVAS_HEIGHT = 1400;
|
||||
// 节点box属性
|
||||
const BOX_WIDTH = 100;
|
||||
// const BOX_HEIGHT = 64; //height通过getBoxHeight方法动态计算
|
||||
@ -149,6 +149,24 @@ const ParentNodes = [...nodeClsMap.values()]
|
||||
})
|
||||
.map((cls) => cls.name);
|
||||
|
||||
/***
|
||||
* 获取所有Composite类型的节点
|
||||
*/
|
||||
const CompositeNodes = [...nodeClsMap.values()]
|
||||
.filter((cls) => {
|
||||
let parentCls = getParentCls(cls);
|
||||
while (parentCls) {
|
||||
if (parentCls.name === Composite.name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
parentCls = getParentCls(parentCls);
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
.map((cls) => cls.name);
|
||||
|
||||
const activeCursor = () => {
|
||||
stage && (stage.container().style.cursor = "pointer");
|
||||
};
|
||||
@ -160,7 +178,7 @@ const component = Vue.extend({
|
||||
template: fs.readFileSync(path.join(__dirname, "../../../src/panels/static/template/vue/app.html"), "utf-8"),
|
||||
$: {
|
||||
tree: "#tree",
|
||||
left: "#left",
|
||||
scroll: "#scroll",
|
||||
},
|
||||
filters: {
|
||||
toUpperCase(value) {
|
||||
@ -199,6 +217,7 @@ const component = Vue.extend({
|
||||
*/
|
||||
nodeCompMethodInfo: {},
|
||||
logs: [],
|
||||
maskText: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -267,12 +286,165 @@ const component = Vue.extend({
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
this.initCanvas();
|
||||
await this.getAssets();
|
||||
await this.selectCurrentAsset();
|
||||
this.$refs.left.scroll((CANVAS_WIDTH - this.$refs.left.getBoundingClientRect().width) / 2, ROOT_Y - 50);
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
/***
|
||||
* 初始化相关
|
||||
*/
|
||||
async init() {
|
||||
// 初始化画布
|
||||
this.initCanvas();
|
||||
// 根据当前选中节点初始化行为树
|
||||
await this.initSelection();
|
||||
this.backRoot();
|
||||
},
|
||||
initCanvas() {
|
||||
// 初始化舞台
|
||||
stage = new Konva.Stage({
|
||||
container: this.$refs.tree,
|
||||
width: CANVAS_WIDTH,
|
||||
height: CANVAS_HEIGHT,
|
||||
});
|
||||
// 基础层,目前只有一层,每层都会生成一个canvas
|
||||
baseLayer = new Konva.Layer();
|
||||
|
||||
// 背景组
|
||||
bgGroup = this.generateBg();
|
||||
baseLayer.add(bgGroup);
|
||||
|
||||
// 静态箭头层
|
||||
staticArrowGroup = new Konva.Group();
|
||||
baseLayer.add(staticArrowGroup);
|
||||
|
||||
// 动态箭头层(用户手动拉出来的箭头)
|
||||
dynamicArrowGroup = this.generateDynamicArrowGroup();
|
||||
baseLayer.add(dynamicArrowGroup);
|
||||
|
||||
// Root组
|
||||
rootGroup = this.generateRoot();
|
||||
baseLayer.add(rootGroup);
|
||||
|
||||
// 节点层
|
||||
nodeGroup = new Konva.Group();
|
||||
baseLayer.add(nodeGroup);
|
||||
|
||||
// 框选层
|
||||
selectionGroup = this.generatesSelectionGroup();
|
||||
baseLayer.add(selectionGroup);
|
||||
|
||||
// 层添加到舞台
|
||||
stage.add(baseLayer);
|
||||
},
|
||||
// 获取json文件
|
||||
async initAssets() {
|
||||
const behaviorTreeComponentUuids = [];
|
||||
const dfs = (node) => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const comp of node.components) {
|
||||
if (comp.type === BTreeCompName) {
|
||||
behaviorTreeComponentUuids.push(comp.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const item of node.children) {
|
||||
dfs(item);
|
||||
}
|
||||
};
|
||||
// 获取场景节点树
|
||||
const sceneNode = await Editor.Message.request("scene", "query-node-tree");
|
||||
|
||||
// 收集场景上所有BehaviorTree组件uuid
|
||||
dfs(sceneNode);
|
||||
|
||||
// 收集BehaviorTree组件上的json路径
|
||||
const rawUrls = await Promise.all(
|
||||
behaviorTreeComponentUuids.map((uuid) =>
|
||||
Editor.Message.request("scene", "execute-component-method", {
|
||||
uuid: uuid,
|
||||
name: "getAssetUrl",
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// 过滤空的并去重
|
||||
const urls = [...new Set(rawUrls.filter(Boolean))];
|
||||
// 根据url获取所有json文件信息
|
||||
const assets = await Promise.all(urls.map((url) => Editor.Message.request("asset-db", "query-asset-info", url)));
|
||||
this.assets = assets.map(({ name, source, file }) => ({ name, url: source, file: file, content: "" }));
|
||||
},
|
||||
async initSelection() {
|
||||
// 找到当前选中的节点
|
||||
const node = await Editor.Message.request("scene", "query-node", Editor.Selection.getSelected("node"));
|
||||
|
||||
// 未选中节点或者选中的是场景节点(场景节点不能添加组件)
|
||||
if (!node?.__comps__) {
|
||||
this.maskText = "请选中一个非场景根节点非空名称节点来开始行为树制作";
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到BehaviorTree组件
|
||||
const index = node.__comps__.findIndex((v: any) => v.type === BTreeCompName);
|
||||
if (index === -1) {
|
||||
this.maskText = "要制作行为树,需要先为当前节点添加行为树组件";
|
||||
return;
|
||||
}
|
||||
|
||||
const comp = node.__comps__[index];
|
||||
// 调用BehaviorTree组件上的方法
|
||||
const url = await Editor.Message.request("scene", "execute-component-method", {
|
||||
uuid: comp.value.uuid.value,
|
||||
name: "getAssetUrl",
|
||||
});
|
||||
|
||||
// JSON文件不存在
|
||||
if (!url) {
|
||||
this.maskText = "当前行为树组件缺少JSON资源,点击BehaviorEditor组件按钮即可创建";
|
||||
return;
|
||||
}
|
||||
|
||||
this.maskText = "";
|
||||
|
||||
// 选中此文件,设置好currentAsset才能把组件nodes数据同步到JSON
|
||||
this.handleSelectAsset(url);
|
||||
},
|
||||
async handleSelectAsset(url) {
|
||||
// 实时获取当前场景所有behaviorTree组件上的json文件
|
||||
await this.initAssets();
|
||||
const json = this.assets.find((e) => e.url === url);
|
||||
if (!json) {
|
||||
this.showWarn("JSON文件不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentAsset?.url === json?.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentAsset = json;
|
||||
|
||||
try {
|
||||
const content = await fs.readJSONSync(json.file);
|
||||
this.currentAsset.content = content;
|
||||
this.render();
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
// JSON文件语法异常的情况下,初始化内容
|
||||
this.showWarn("JSON文件内容初始化成功");
|
||||
const content = { nodes: [] };
|
||||
await fs.writeJSONSync(json.file, content);
|
||||
this.currentAsset.content = content;
|
||||
this.render();
|
||||
} else {
|
||||
// 输出其他异常
|
||||
this.showWarn(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handlePanelChange(value) {
|
||||
this.currentPanel = value;
|
||||
},
|
||||
@ -289,7 +461,7 @@ const component = Vue.extend({
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
async handleEventNodeChange(lifeCycle, uuid = "") {
|
||||
async handleEventNodeChange(lifeCycle, uuid = "", shouldSave = true) {
|
||||
if (!this.currentNode) {
|
||||
this.showWarn("当前节点不存在");
|
||||
return;
|
||||
@ -301,9 +473,13 @@ const component = Vue.extend({
|
||||
} else {
|
||||
this.currentNode.event[lifeCycle].comp = "";
|
||||
this.currentNode.event[lifeCycle].method = "";
|
||||
this.currentNode.event[lifeCycle].data = "";
|
||||
// 事件参数就不手动清空了
|
||||
// this.currentNode.event[lifeCycle].data = "";
|
||||
}
|
||||
// 点击canvas某个节点的时候,会触发handleEventNodeChange获取场景节点数据,此时不保存数据
|
||||
if (shouldSave) {
|
||||
await this.saveAsset();
|
||||
}
|
||||
},
|
||||
async getNodeCompMethods(lifeCycle, uuid) {
|
||||
const [nodeInfo, compMethodInfo] = await Promise.all([
|
||||
@ -348,151 +524,18 @@ const component = Vue.extend({
|
||||
this.currentNode.event[lifeCycle].data = data;
|
||||
await this.saveAsset();
|
||||
},
|
||||
async selectCurrentAsset() {
|
||||
// 找到点击组件的节点
|
||||
const node = await Editor.Message.request("scene", "query-node", Editor.Selection.getSelected("node"));
|
||||
if (!node) {
|
||||
this.showWarn(`未选中节点`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到BehaviorTree组件
|
||||
const index = node.__comps__.findIndex((v: any) => v.type === BTreeCompName);
|
||||
if (index === -1) {
|
||||
this.showWarn(`节点未挂载【${BTreeCompName}】组件`);
|
||||
return;
|
||||
}
|
||||
|
||||
const comp = node.__comps__[index];
|
||||
// 调用BehaviorTree组件上的方法
|
||||
const url = await Editor.Message.request("scene", "execute-component-method", {
|
||||
uuid: comp.value.uuid.value,
|
||||
name: "getAssetUrl",
|
||||
});
|
||||
|
||||
// JSON文件不存在
|
||||
if (!url) {
|
||||
this.showWarn(`【${BTreeCompName}】组件未指定JSON文件`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 选中此文件,设置好currentAsset才能把组件nodes数据同步到JSON
|
||||
this.handleSelectAsset(url);
|
||||
},
|
||||
async handleSelectAsset(url) {
|
||||
const json = this.assets.find((e) => e.url === url);
|
||||
if (!json) {
|
||||
this.showWarn("JSON文件不存在");
|
||||
return;
|
||||
}
|
||||
this.currentAsset = json;
|
||||
|
||||
try {
|
||||
const content = await fs.readJSONSync(json.file);
|
||||
this.currentAsset.content = content;
|
||||
this.render();
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
// JSON文件语法异常的情况下,初始化内容
|
||||
this.showWarn("JSON文件内容初始化成功");
|
||||
const content = { nodes: [] };
|
||||
await fs.writeJSONSync(json.file, content);
|
||||
this.currentAsset.content = content;
|
||||
this.render();
|
||||
} else {
|
||||
// 输出其他异常
|
||||
this.showWarn(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 全部所有behaviorTree组件上使用到的json文件
|
||||
async getAssets() {
|
||||
const behaviorTreeComponentUuids = [];
|
||||
const dfs = (node) => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const comp of node.components) {
|
||||
if (comp.type === BTreeCompName) {
|
||||
behaviorTreeComponentUuids.push(comp.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const item of node.children) {
|
||||
dfs(item);
|
||||
}
|
||||
};
|
||||
// 获取场景节点树
|
||||
const sceneNode = await Editor.Message.request("scene", "query-node-tree");
|
||||
|
||||
// 收集场景上所有BehaviorTree组件uuid
|
||||
dfs(sceneNode);
|
||||
|
||||
// 收集BehaviorTree组件上的json路径
|
||||
const rawUrls = await Promise.all(
|
||||
behaviorTreeComponentUuids.map((uuid) =>
|
||||
Editor.Message.request("scene", "execute-component-method", {
|
||||
uuid: uuid,
|
||||
name: "getAssetUrl",
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// 过滤空的并去重
|
||||
const urls = [...new Set(rawUrls.filter(Boolean))];
|
||||
// 根据url获取所有json文件信息
|
||||
const assets = await Promise.all(urls.map((url) => Editor.Message.request("asset-db", "query-asset-info", url)));
|
||||
|
||||
this.assets = assets.map(({ name, source, file }) => ({ name, url: source, file: file, content: "" }));
|
||||
},
|
||||
async saveAsset() {
|
||||
const url = this.currentAsset?.url;
|
||||
if (!url) {
|
||||
const file = this.currentAsset?.file;
|
||||
if (!file) {
|
||||
this.showWarn("数据存储失败,未指定JSON");
|
||||
return;
|
||||
}
|
||||
const content = JSON.stringify(this.currentAsset.content, null, 2);
|
||||
const content = this.currentAsset.content;
|
||||
|
||||
// 修改json文件
|
||||
await Editor.Message.request("asset-db", "save-asset", url, content);
|
||||
fs.writeJSONSync(file, content);
|
||||
},
|
||||
initCanvas() {
|
||||
// 初始化舞台
|
||||
stage = new Konva.Stage({
|
||||
container: this.$refs.tree,
|
||||
width: CANVAS_WIDTH,
|
||||
height: CANVAS_HEIGHT,
|
||||
});
|
||||
// 基础层,目前只有一层,每层都会生成一个canvas
|
||||
baseLayer = new Konva.Layer();
|
||||
|
||||
// 背景组
|
||||
bgGroup = this.generateBg();
|
||||
baseLayer.add(bgGroup);
|
||||
|
||||
// 静态箭头层
|
||||
staticArrowGroup = new Konva.Group();
|
||||
baseLayer.add(staticArrowGroup);
|
||||
|
||||
// 动态箭头层(用户手动拉出来的箭头)
|
||||
dynamicArrowGroup = this.generateDynamicArrowGroup();
|
||||
baseLayer.add(dynamicArrowGroup);
|
||||
|
||||
// Root组
|
||||
rootGroup = this.generateRoot();
|
||||
baseLayer.add(rootGroup);
|
||||
|
||||
// 节点层
|
||||
nodeGroup = new Konva.Group();
|
||||
baseLayer.add(nodeGroup);
|
||||
|
||||
// 框选层
|
||||
selectionGroup = this.generatesSelectionGroup();
|
||||
baseLayer.add(selectionGroup);
|
||||
|
||||
// 层添加到舞台
|
||||
stage.add(baseLayer);
|
||||
},
|
||||
/***
|
||||
* 渲染画布
|
||||
* renderNode:拖拽的时候不渲染节点
|
||||
@ -616,7 +659,7 @@ const component = Vue.extend({
|
||||
type,
|
||||
abortType: this.AbortType.None,
|
||||
x: (CANVAS_WIDTH - BOX_WIDTH) / 2,
|
||||
y: CANVAS_HEIGHT / 4,
|
||||
y: ROOT_Y + 100,
|
||||
isRoot: false,
|
||||
children: [],
|
||||
event: {
|
||||
@ -895,7 +938,7 @@ const component = Vue.extend({
|
||||
if (e.evt.buttons === 4) {
|
||||
const movementX = e.evt.movementX;
|
||||
const movementY = e.evt.movementY;
|
||||
this.$refs.left.scrollBy(-movementX, -movementY);
|
||||
this.$refs.scroll.scrollBy(-movementX, -movementY);
|
||||
return;
|
||||
}
|
||||
if (!selection.visible()) {
|
||||
@ -1063,11 +1106,11 @@ const component = Vue.extend({
|
||||
selectedBoxes = [];
|
||||
this.currentNode = node;
|
||||
this.handlePanelChange(1);
|
||||
this.render();
|
||||
// 获取节点事件相关信息
|
||||
this.handleEventNodeChange(this.onStart, this.currentNode.event[this.onStart].node);
|
||||
this.handleEventNodeChange(this.onUpdate, this.currentNode.event[this.onUpdate].node);
|
||||
this.handleEventNodeChange(this.onEnd, this.currentNode.event[this.onEnd].node);
|
||||
this.handleEventNodeChange(this.onStart, this.currentNode.event[this.onStart].node, false);
|
||||
this.handleEventNodeChange(this.onUpdate, this.currentNode.event[this.onUpdate].node, false);
|
||||
this.handleEventNodeChange(this.onEnd, this.currentNode.event[this.onEnd].node, false);
|
||||
this.render();
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
@ -1349,6 +1392,13 @@ const component = Vue.extend({
|
||||
|
||||
return BOX_HEIGHT;
|
||||
},
|
||||
isComposite(node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CompositeNodes.includes(node.type);
|
||||
},
|
||||
showWarn(text) {
|
||||
let time = getTime();
|
||||
const content = `${time}:${text}`;
|
||||
@ -1358,6 +1408,10 @@ const component = Vue.extend({
|
||||
this.logs.push({ id: uuid(), content });
|
||||
console.warn(content);
|
||||
},
|
||||
// 视角回到root节点
|
||||
backRoot() {
|
||||
this.$refs?.scroll?.scroll((CANVAS_WIDTH - this.$refs.scroll.getBoundingClientRect().width) / 2, ROOT_Y - 50);
|
||||
},
|
||||
},
|
||||
});
|
||||
const panelDataMap = new WeakMap() as WeakMap<object, InstanceType<typeof component>>;
|
||||
@ -1383,7 +1437,14 @@ module.exports = Editor.Panel.define({
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {},
|
||||
methods: {
|
||||
initSelection() {
|
||||
const vm = panelDataMap.get(this);
|
||||
if (vm) {
|
||||
vm.initSelection();
|
||||
}
|
||||
},
|
||||
},
|
||||
ready() {
|
||||
if (this.$.app) {
|
||||
const vm = new component();
|
||||
|
@ -1,7 +1,8 @@
|
||||
.wrapper {
|
||||
display: flex;
|
||||
background-color: #2b2b2b;
|
||||
max-height: calc(100vh - 26px);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
color: #cccccc;
|
||||
font-family: BlinkMacSystemFont, "PingFang SC", system-ui, -apple-system, Helvetica Neue, Helvetica, sans-serif;
|
||||
@ -15,12 +16,24 @@ ui-prop[no-label] {
|
||||
padding: 0px 4px 0 4px;
|
||||
}
|
||||
|
||||
.node-type {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.life-cycle-input {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 280px;
|
||||
border-left: 1px solid #050505;
|
||||
width: 260px;
|
||||
border-left: 2px solid #050505;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.right > div {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.node-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -29,25 +42,41 @@ ui-prop[no-label] {
|
||||
|
||||
.left {
|
||||
position: relative;
|
||||
overflow: scroll;
|
||||
width: calc(100% - 260px);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.fix-left {
|
||||
position: fixed;
|
||||
left: 13px;
|
||||
top: 34px;
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 14px;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.fix-right {
|
||||
position: fixed;
|
||||
right: 300px;
|
||||
top: 34px;
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.fix-bottom {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
bottom: 24px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fix-bottom ui-button {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.asset {
|
||||
}
|
||||
|
||||
@ -55,7 +84,7 @@ ui-prop[no-label] {
|
||||
color: #e8b116;
|
||||
min-width: 200px;
|
||||
max-width: 500px;
|
||||
font-weight: bold;
|
||||
/* font-weight: bold; */
|
||||
}
|
||||
|
||||
.log div {
|
||||
@ -64,6 +93,10 @@ ui-prop[no-label] {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.log div:first-child {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
@ -102,10 +135,22 @@ ui-prop[no-label] {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.mr-4 {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.mr-6 {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.mr-8 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@ -113,3 +158,16 @@ ui-prop[no-label] {
|
||||
.flex-1 {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
background-color: rgba(2, 2, 2, 0.6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
<div>
|
||||
<div style="width: 100%; height: 100%" id="wrapper">
|
||||
<div id="app"></div>
|
||||
</div>
|
||||
|
@ -1,5 +1,9 @@
|
||||
<div class="wrapper">
|
||||
<div class="left" ref="left">
|
||||
<div class="mask" v-if="maskText">{{maskText}}</div>
|
||||
<div class="left">
|
||||
<div class="scroll" ref="scroll">
|
||||
<div id="tree" ref="tree" />
|
||||
</div>
|
||||
<div class="fix-left">
|
||||
<div class="log">
|
||||
<ui-section header="Logs" class="config" expand>
|
||||
@ -14,7 +18,9 @@
|
||||
</ui-select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tree" ref="tree" />
|
||||
<div class="fix-bottom" @click="backRoot">
|
||||
<ui-button>Root</ui-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
@ -24,7 +30,7 @@
|
||||
<div v-if="currentPanel === 0" class="node-panel">
|
||||
<div v-for="category of nodeCategory" class="node-type">
|
||||
<ui-section :header="category.id" :expand="category.expand">
|
||||
<ui-button class="mt-4 mr-6" v-for="item of category.items" :key="item.type" @click="addNode(item.type)"
|
||||
<ui-button class="mt-4" v-for="item of category.items" :key="item.type" @click="addNode(item.type)"
|
||||
>{{ item.type }}</ui-button
|
||||
>
|
||||
</ui-section>
|
||||
@ -53,6 +59,7 @@
|
||||
<ui-label slot="label">Abort Type</ui-label>
|
||||
<ui-select
|
||||
slot="content"
|
||||
:disabled="!isComposite(currentNode)"
|
||||
:value="currentNode.abortType"
|
||||
@change="handleAbortTypeChange($event.target.value)"
|
||||
>
|
||||
@ -93,9 +100,10 @@
|
||||
</ui-select>
|
||||
</div>
|
||||
</ui-prop>
|
||||
<ui-prop message="EventData">
|
||||
<ui-prop no-label>
|
||||
<ui-input
|
||||
slot="content"
|
||||
class="life-cycle-input"
|
||||
:value="currentNode.event[onStart].data"
|
||||
@input="handleEventDataChange(onStart, $event.target.value)"
|
||||
></ui-input>
|
||||
@ -107,7 +115,7 @@
|
||||
<ui-label slot="label">{{onUpdate | toUpperCase}}</ui-label>
|
||||
</ui-prop>
|
||||
<ui-prop no-label>
|
||||
<div slot="content" class="flex items-center">
|
||||
<div slot="content" class="flex items-center gap-4">
|
||||
<ui-node
|
||||
class="flex-1"
|
||||
droppable="cc.Node"
|
||||
@ -134,9 +142,10 @@
|
||||
</ui-select>
|
||||
</div>
|
||||
</ui-prop>
|
||||
<ui-prop message="EventData">
|
||||
<ui-prop no-label>
|
||||
<ui-input
|
||||
slot="content"
|
||||
class="life-cycle-input"
|
||||
:value="currentNode.event[onUpdate].data"
|
||||
@input="handleEventDataChange(onUpdate, $event.target.value)"
|
||||
></ui-input>
|
||||
@ -148,7 +157,7 @@
|
||||
<ui-label slot="label">{{onEnd | toUpperCase}}</ui-label>
|
||||
</ui-prop>
|
||||
<ui-prop no-label>
|
||||
<div slot="content" class="flex items-center">
|
||||
<div slot="content" class="flex items-center gap-4">
|
||||
<ui-node
|
||||
class="flex-1"
|
||||
droppable="cc.Node"
|
||||
@ -175,9 +184,10 @@
|
||||
</ui-select>
|
||||
</div>
|
||||
</ui-prop>
|
||||
<ui-prop message="EventData">
|
||||
<ui-prop no-label>
|
||||
<ui-input
|
||||
slot="content"
|
||||
class="life-cycle-input"
|
||||
:value="currentNode.event[onEnd].data"
|
||||
@input="handleEventDataChange(onEnd, $event.target.value)"
|
||||
></ui-input>
|
||||
@ -186,7 +196,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex justify-center mt-4">请选择节点</div>
|
||||
<div v-else class="flex justify-center mt-12">Please select a node</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user