新建行为树
This commit is contained in:
1 parent
4bb083f736
commit
5653ff7ff6
8 files changed
+73
-11
No files matched your search
@@ -2,12 +2,6 @@
|
|||||||
这是一个直观、可视化的行为树编辑器,行为树的保存格式为json,可以让策划自行去实现AI,技能,buff等复杂的游戏逻辑,从而减少不必要的沟通成本和提升开发效率。
|
这是一个直观、可视化的行为树编辑器,行为树的保存格式为json,可以让策划自行去实现AI,技能,buff等复杂的游戏逻辑,从而减少不必要的沟通成本和提升开发效率。
|
||||||

|

|
||||||
|
|
||||||
## 技术栈
|
|
||||||
+ react + ts
|
|
||||||
+ g6
|
|
||||||
+ antd
|
|
||||||
+ electron
|
|
||||||
|
|
||||||
## 使用方法
|
## 使用方法
|
||||||
+ 打开编辑器
|
+ 打开编辑器
|
||||||
+ 工作区->节点定义->选择文件(json)
|
+ 工作区->节点定义->选择文件(json)
|
||||||
@@ -37,13 +31,28 @@ interface BehaviorNodeTypeModel {
|
|||||||
doc?: string; //文档说明(markdown格式)
|
doc?: string; //文档说明(markdown格式)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
节点定义也是json格式,参照[sample/node-config.json](sample/node-config.json),编辑器不提供节点定义的编辑,强烈建议节点定义文件由代码生成。
|
||||||
|
|
||||||
|
## 编译与构建
|
||||||
|
```shell
|
||||||
|
npm install # 安装依赖
|
||||||
|
npm start # 运行测试
|
||||||
|
npm run build # 编译exe可执行文件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
+ react + ts
|
||||||
|
+ g6
|
||||||
|
+ antd
|
||||||
|
+ electron
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
+ 复制粘贴功能(跨工作区)
|
+ 复制粘贴功能(跨工作区)
|
||||||
+ 历史记录(撤销/恢复)
|
+ 历史记录(撤销/恢复)
|
||||||
+ 资源面板支持树型结构
|
+ 资源面板支持树型结构
|
||||||
+ 快捷键功能
|
+ 快捷键功能
|
||||||
|
+ 右键菜单
|
||||||
+ 联调功能
|
+ 联调功能
|
||||||
|
|
||||||
## About
|
## About
|
||||||
目前编辑器还处于非常简陋的阶段,本项目将长期维护,望前端大佬们多提点~
|
目前编辑器还处于非常简陋的阶段,有问题可以联系作者(QQ1013299930),本项目将长期维护,望前端大佬们多提点~
|
||||||
@@ -2,6 +2,7 @@ export default {
|
|||||||
OPEN_FILE: "OPEN_FILE",
|
OPEN_FILE: "OPEN_FILE",
|
||||||
OPEN_DIR: "OPEN_DIR",
|
OPEN_DIR: "OPEN_DIR",
|
||||||
OPEN_WORKSPACE: "OPEN_WORKSPACE",
|
OPEN_WORKSPACE: "OPEN_WORKSPACE",
|
||||||
|
CREATE_TREE: 'CREATE_TREE',
|
||||||
CREATE_NODE: 'CREATE_NODE',
|
CREATE_NODE: 'CREATE_NODE',
|
||||||
SAVE: 'SAVE',
|
SAVE: 'SAVE',
|
||||||
SAVE_ALL: 'SAVE_ALL',
|
SAVE_ALL: 'SAVE_ALL',
|
||||||
|
|||||||
+18
-1
@@ -1,7 +1,8 @@
|
|||||||
import { TreeGraphData } from "@antv/g6/lib/types"
|
import { TreeGraphData } from "@antv/g6/lib/types"
|
||||||
import { BehaviorNodeModel, GraphNodeModel } from "./BehaviorTreeModel";
|
import { BehaviorNodeModel, BehaviorTreeModel, GraphNodeModel } from "./BehaviorTreeModel";
|
||||||
import Settings from "../main-process/Settings";
|
import Settings from "../main-process/Settings";
|
||||||
import { remote } from "electron";
|
import { remote } from "electron";
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
export const cloneNodeData = (nodeData: GraphNodeModel) => {
|
export const cloneNodeData = (nodeData: GraphNodeModel) => {
|
||||||
const newData: GraphNodeModel = {
|
const newData: GraphNodeModel = {
|
||||||
@@ -112,3 +113,19 @@ export const findFromAllChildren = (node: TreeGraphData, id: string): TreeGraphD
|
|||||||
export const getRemoteSettings = () => {
|
export const getRemoteSettings = () => {
|
||||||
return remote.getGlobal("settings") as Settings;
|
return remote.getGlobal("settings") as Settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const fileName2treeName = (filename: string) => {
|
||||||
|
return path.basename(filename).slice(0, -5);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createNewTree = (filename: string) => {
|
||||||
|
const tree: BehaviorTreeModel = {
|
||||||
|
name: fileName2treeName(filename),
|
||||||
|
root: {
|
||||||
|
id: 1,
|
||||||
|
name: 'Sequence',
|
||||||
|
desc: '新建行为树'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Menu, app, dialog, BrowserWindow, MenuItem, WebContents, MenuItemConstructorOptions } from 'electron';
|
import { Menu, app, dialog, BrowserWindow, MenuItem, WebContents, MenuItemConstructorOptions } from 'electron';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as Utils from '../common/Utils';
|
||||||
import MainEventType from '../common/MainEventType';
|
import MainEventType from '../common/MainEventType';
|
||||||
import { MainProcess } from './MainProcess';
|
import { MainProcess } from './MainProcess';
|
||||||
import Settings from './Settings';
|
import Settings from './Settings';
|
||||||
@@ -40,6 +42,24 @@ export default class AppMenu {
|
|||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
label: "行为树",
|
label: "行为树",
|
||||||
submenu: [
|
submenu: [
|
||||||
|
{
|
||||||
|
label: "新建",
|
||||||
|
click: () => {
|
||||||
|
(async () => {
|
||||||
|
const res = await dialog.showSaveDialog({
|
||||||
|
properties: ['showOverwriteConfirmation'],
|
||||||
|
filters: [
|
||||||
|
{ name: "Json", extensions: ['json'] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
if (!res.canceled) {
|
||||||
|
const path = res.filePath;
|
||||||
|
fs.writeFileSync(path, JSON.stringify(Utils.createNewTree(path), null, 2));
|
||||||
|
this.webContents.send(MainEventType.CREATE_TREE, path);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "打开文件",
|
label: "打开文件",
|
||||||
accelerator: "Ctrl+O",
|
accelerator: "Ctrl+O",
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ const sampleNodeClassify: BehaviorNodeClassify[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default class Settings {
|
export default class Settings {
|
||||||
private settings: SettingsModel;
|
private settings: SettingsModel;
|
||||||
curWorkspace?: Workspace;
|
curWorkspace?: Workspace;
|
||||||
@@ -61,7 +60,7 @@ export default class Settings {
|
|||||||
this.settings = JSON.parse(str);
|
this.settings = JSON.parse(str);
|
||||||
} else {
|
} else {
|
||||||
this.settings = {
|
this.settings = {
|
||||||
recentWorkspaces: [],
|
recentWorkspaces: ['sample/workspace.json'],
|
||||||
recentFiles: [],
|
recentFiles: [],
|
||||||
nodeClassify: sampleNodeClassify,
|
nodeClassify: sampleNodeClassify,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React, { Component } from "react";
|
|||||||
import { Menu, Input } from "antd";
|
import { Menu, Input } from "antd";
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import { ipcRenderer } from "electron";
|
||||||
|
import MainEventType from "../common/MainEventType";
|
||||||
|
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
|
|
||||||
@@ -33,6 +35,12 @@ export default class Properties extends Component<PropertiesProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => {
|
||||||
|
console.log("on Create tree", path);
|
||||||
|
this.props.onOpenTree(path);
|
||||||
|
this.forceUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
var workspace = this.props.workspace;
|
var workspace = this.props.workspace;
|
||||||
if (workspace == '') {
|
if (workspace == '') {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"root": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Sequence",
|
||||||
|
"desc": "新建行为树"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"nodeConfPath": "sample\\node-config.json",
|
"nodeConfPath": "sample\\node-config.json",
|
||||||
"workdir": "sample\\workdir"
|
"workdir": "E:\\code\\behavior3editor\\sample\\workdir"
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user