Skip to content

sarrow104/behaviorTreeEditorSln2

Repository files navigation

行为树编辑器

开发原因以及需求说明

本APP,是仿造 behavior3editor 编写的;

这个版本的行为树编辑器,导出的行为树结构如下:

顶层结构如下:

{
    "version": "0.3.0",
    // 可能的值:node, project, tree
    "scope": "project",
    "selectedTree": "a7bd4b69-6915-438d-84b0-59ec4277fc06",
    // tree 列表
    "trees": [],
    "custom_nodes": []
}

tree 结构如下:

{
    "version": "0.3.0",
    // 如上
    "scope": "tree",
    // guid, 创建时,自动生成
    "id": "a7bd4b69-6915-438d-84b0-59ec4277fc06",
    // bt名;手动填写
    "title": "TemplateBehaviorTree",
    "description": "",
    // 根节点guid值
    "root": "065166b4-9a8c-4b3a-8329-4be5a2721382",
    "properties": {},
    // node map, key为guid, value为node
    // * key 值等于 node.id
    "nodes": {
        "$guid": {
            /*...*/
        },
    },
    "display": {
        "camera_x": 94,
        "camera_y": 247.5,
        "camera_z": 1,
        "x": 0,
        "y": 0
    }
}

node 结构如下:

{
    "id": "065166b4-9a8c-4b3a-8329-4be5a2721382",
    // 模板节点名
    "name": "Priority",
    // 节点标题——默认值等于节点模板名
    "title": "Priority",
    "description": "",
    "properties": {
        // 自定义属性,以及默认值
        "State": 2
    },
    // 节点坐标
    "display": {
        "x": 96,
        "y": 0
    },
    "children": [
        "f6092c66-d5e7-4d86-9578-90fe0b178425",
        "0dd72798-e40f-4714-8f8e-04fa4b2b2b8f"
    ],
    // 对于出度为1的结点,"children"字段将被替换为 "child"字段,其类型为 string
}

模板节点定义如下(顶层结构的 custom_nodes 属性下)

{
    // ...
    "custom_nodes": [
        {
            "version": "0.3.0",
            "scope": "node",
            // 模板节点名称 (唯一)
            "name": "Parallel",
            "category": "composite|condition|action|decorator",
            // 模板节点类型
            "title": null,
            "description": null,
            // 自定义属性 map
            "properties": {
                "Weight": 0,
                // 当前模板的自定义属性名,以及其默认值(自动识别类型为数字或字符串)
                "ContrastSymbols": "<=",
            }
        }
        // {...}
    ]
}

也就是说,其导出文件是以project(多个tree)为单位进行保存的。这不方便进行团队协作和svn管理。

比如,我们项目用到了上百个行为树,为了进行团队协作,一个文件的project里,就放了一个tree;这使得每一个json导出文件中,都有各自版本的 "custom_nodes"

这造成了一个额外的问题就是,当需要往已有的"custom_node"添加自定义属性的时候,将非常困难,使整个项目变得难于维护;

另外一个问题就是,策划需要调试(至少监控)行为树的运行;

因此,这边对上述js版本的bt编辑器,进行了仿写;目的就是在可以读取原本行为树导出文件数据时,并解决上述问题;

仿写版本编辑器的基本设计要求如下:

  1. 多tab页编辑,一个tab对应一个行为树;tab之间,可以互相黏贴结点,以及结点之间的连线
  2. 同一个项目中的行为树,共享同一份"custom_node"配置
  3. "custom_node"配置的修改,可以快速应用到已有的行为树导出文件中
  4. 与服务器进行通信,以监控行为树的执行

App配置以及行为说明

本App依赖于两个配置文件

  • ./App.json5
  • ./Config.json5

App.json5 用于控制程序行为;Config.json5 用于配置结点,以及配置结点属性会用到的枚举类型

结点分为如下5种,除root外的其他几种类型的结点,允许自定义;

结点类型,与出入度关系表

类型 入度 出度
root 0 1
composite 1 +
decorator 1 1
action 1 0
condition 1 0

说明 在保存时,如果某结点的入度、出度未满足,则会报错;

结点自定义范例

{
    "custom_enums": [
        // 转向仇恨目标方式
        {
            "name": "TurnType",
            "values": [
                "Directly",
                "WithRotation",
                "WithRotationAndMove"
            ]
        },
        //...
    ],
    "custom_nodes": [
        {
            "version": "0.3.0",
            "scope": "node",
            "name": "TurnToTarget",
            "category": "action",
            "title": null,
            // 描述字段,在行为树编辑区实例化之后,可以被编辑
            // 此处相当于定义了该字段的默认值
            "description": "转向仇恨目标",
            "properties": {
                "TurnType": "##Directly",
                "AnimRotationSpeed": 0,
                "Distance": 0,
                "IsTargetInView": false,
            }
        },
        // ...
    ]
}

以上,自定义了一个值连续的,从0开始的枚举类型 TurnType;其字面量定义在"values"

如果值不连续,可额外定义 "numbers" 属性;形如:"numbers": [1, 3, 5, 7]

需要注意,该属性长度,得等于 values 的长度不定义;长度不一致,会报错

行为树序列化为json时,通过 "IsEnumAsString": true, 字段,控制该枚举类型序列化成是字符串还是数字

custom_nodes 部分,则定义了一个分类为action,名叫 TurnToTarget 的自定义结点;

properties 属性,定义了该结点所使用的参数;支持如下运行时类型:

  1. string
  2. long
  3. bool
  4. double

属性的bnf定义形式为:

name ':' defaultValue

name 为属性名,defaultValue 为为上述支持类型的字面量; 特别的有 long和double的区别,仅在于是否包含小数。

那么,如何定义一个使用预定义枚举类型的结点属性呢?

"TurnType": "##Directly",

包含了两个#,并且以 # 开头,即表示该属性使用了预定义的枚举类型。

# 符号,将 defaultValue 分为两部分部分:

'#' enumName? '#' enumDefaultValue

如果,enumName 被省略了,则以属性名,在预定义的枚举类型集合中去查找;

enumDefaultValue 则为找到的枚举类型中定义好的字面量;

监听行为树执行演示

  1. 运行演示用ws-server

ws-server-managed/ws-server-managed.exe -config ws-server-managed/config.json -custom BehaviorTreeEditorApp/Custom.json5 -btsFolder ./bts

  1. 运行本App,并编辑一个行为树(若是新建的行为树,得保存到约定的文件夹中;比如 ./bts

本编辑器,默认会读取其运行目录下的 App.json5, Custom.json5 两个配置文件;

  1. Editor中,点击上方的 红色圆形按钮(或者执行菜单 File -> Monitor B-Tree Execution Start)

  2. 在监听弹窗对话框中,点击确定即可

监听的效果如上面的视频

如何配置监听相关设置

如果需要对行为树运行时行为进行监听,则需要先进行如下配置

App.json5

{
    "monitor": {
        // 监听请求的form字段列表
        "startup_fields": [
            {
                "Default": "templateId",
                "Options": [
                    "templateId",
                    "entityUid"
                ],
                "Name": "MonitorType",
                "Type": "string"
            },
            {
                "Default": "regex:\\d+$",
                "Options": null,
                "Name": "MonitorId",
                "Type": "int"
            }
        ],
        // 消息栈大小
        "stack_size": 100,
        // 监听的服务器地址模板
        "ws_url": "ws://127.0.0.1:8999/api/v1/btMonitor/{title}/{MonitorType}/{MonitorId}",
        // 监听会话的title模板
        "id_format": "{title}.{MonitorType}.{MonitorId}"
    }
}

上面为监听启动 form,定义了两个字段:

  1. MonitorId 字段是一个 long 类型, 其默认值("Default") "regex:\\d+$" 表示,他将从当前行为树title 中提取末尾数字作为当前字段的默认值;

    此处的默认值,你也可以简单写为 0

  2. MonitorType 则是 string 类型,其默认值是 "templateId";非空的 Options属性表示,该字段UI为一个下拉列表,其选项分布为 "templateId", "entityUid"

    下拉选项也可以是整数或者是浮点数字面量,如果你需要对应类型的选项的话;

ws_url/id_format 两个配置项,都是插值字符串模板;在运行时,会被替换为当时的BTree-title,或者form中,用户对应字段的输入值。

插值字符串,求值时,忽略大小写

监听消息体说明

服务器发送给编辑器的消息是json格式;对应的C#结构体为:

public class MonitorMessage
{
    // 结点的运行时状态:Running, Success, Failure,
    // 其中,Running 表示正在运行,常亮;
    //      Success 表示成功
    //      Failure 表示失败
    //      后两者均会短时间`熄灭`
    public MonitorState State { get; set; } = MonitorState.None;
    // node 唯一id,以标记处于当前状态的结点
    public string? Content { get; set; }
    // 进入退出某结点期间,所采集到的服务器日志列表
    public List<string>? Messages { get; set; }
}

其他功能与说明

批量更新行为树:

  1. Batch Refresh B-tree files

在调整了 Config.json5 进行了属性的添加和删除之后,就需要批量更新行为树文件。

  • 新增字段直接选中行为树文件,执行即可;
  • 如果是删除字段,则需要额外填写涉及到的自定义结点类型,以及属性名;然后再执行,否则批量转换时,程序发觉丢失字段,就会报错;

因为这里有一个工具的基本设计要求,那就是不丢失用户的编辑输入信息——除非用户手动确认

剪贴板:拷贝、黏贴

行为树结点的拷贝、黏贴,都发生在进程内部,并不会使用系统剪贴板

监听回放

终止监听(点击监听按钮,或者使用 MonitorMessageHistory菜单)后,可以点击MonitorMessageHistory,以查看采集到的结点运行日志;

点击focus按钮,就会重播一次当前消息

节点查找语法

使用菜单 Edit -> Find 即可进入查找

其顶部的输入框,支持简单的查找语法

  fuzzySearch := text (' ' text )*;
  nodeNameSearch := nodeName '[]';
  propertyNameSearch := '[' propertyName ']'
  propertyNameAndValueSearch := '[' propertyName '=' propertyValue ']'
  
  elementSearch := text | nodeNameSearch | propertyNameSearch | propertyNameAndValueSearch
  complexSearch := elementSearch (' ' elementSearch)*

注意: 空格间隔多个查找语素的话,表示 and 语意

范例:

  1. 10086 : 查找任意字段值、属性值包含10086字样的节点
  2. ReleaseSkill[] : 查找所有 ReleaseSkill 的custom 节点
  3. [SkillCid=1001] : 查找任意类型节点的属性包含,SkillCid 属性,并且对应属性值为1001 的节点。

reference

基于的结点库:

本app对 NodeNetwork 库的修改有:

  1. 结点、连线变化的消息通知,以支持redo/undo
  2. 连线线型,添加直线,以优化加载速度
  3. viewModel添加isReadOnly属性,以支持只读状态
  4. 更新其依赖库的版本,以避免多版本依赖问题

图标来源

App用到的图标,均来自 https://www.flaticon.com/

Buy Me a Coffee

因为感觉行为树编辑器还有一定的使用场景,特别将特定游戏业务相关的代码修改调整为可配制化后,分享到github,以方便后来者;

最后,如果你觉得对你有帮助,请不吝您的money;也希望您的money能为我带来好运。

About

another behaviorTree Editor written in Csharp

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published