version:1.0.1

This commit is contained in:
sli97
2023-09-26 22:16:42 +08:00
parent 47104caa6e
commit 2ee8352e9d
12 changed files with 552 additions and 332 deletions

View File

@@ -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
View File

@@ -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);
},
};
/**

View File

@@ -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();
}
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();

View File

@@ -22,5 +22,5 @@ class Blackboard {
this.map.clear();
}
}
exports.Blackboard = Blackboard;
Blackboard.map = new Map();
exports.Blackboard = Blackboard;