还未查看过文档《原理和流程》的请首先阅读该文档,以便了解behaviac组件的基本原理和工作流程。

本文档有视频和文字说明。两者内容是一致的,以便按需查看。
本文档描述的是3.6及以后版本。对于3.5及以前的老版本请参考分类“3.5”。

编辑器的使用

新建工作区

工作区用于管理整个项目的配置,包括一些位置和语言等参数。

视频

打开编辑器,点击菜单项“文件”->“新建工作区”,弹出新建工作区窗口,如下图所示:

  • 工作区位置:保存工作区文件的目录,在该目录下会保存出*.workspace.xml的文件,该文件即是编辑器打开的工作区文件(或者称之为项目文件)。
  • 行为树源位置:对既有服务器,又有客户端的项目开发,可能需要共用行为树,这时候只要为服务器和客户端分别创建工作区,然后为它们设置相同的“行为树源位置”即可。这样,只需要编辑同一份行为树,就可以让服务器和客户端的AI逻辑保持一致。此外,源位置中还自动生成behaviac_meta文件夹,里面保存了跟工作区同名但后缀名为*.meta.xml的类型信息文件。
  • 行为树导出位置:在该目录下,导出编辑好的行为树,并且需要在程序端设置的加载位置,以便程序运行起来后,加载所需的行为树。
  • 代码生成位置:用于存放后面将要描述的类型信息浏览器中编辑好的类型代码文件,还包含了一些“胶水”代码文件,都需要整合到运行时(游戏端)一起编译构建。
  • 程序端开发语言:可选cpp(即C++)和cs(即C#)两种,表示程序端的代码语言。结合上面所说的情况,可以支持服务器和客户端采用不同的语言编写代码,但共用同一份行为树数据。
“胶水”代码:是指编辑器自动生成的代码文件,用于注册类型信息,并用于程序端执行时通过名字或ID调用类的成员属性或方法。

点击确认后,就可以创建自己的工作区。创建完工作区之后,后续如果需要修改工作区中的参数,可以通过菜单项“文件”->“编辑工作区”,重新打开上图所示的窗口来修改相关的参数。

另外,还可以直接打开为本教程建好的工作区。点击菜单项“文件”->“打开工作区”,找到安装或源码包目录中的tutorials/tutorial_1/workspace文件夹,打开tutorial_1_cpp.workspace.xml文件,如下图所示:

编辑类型信息

为了让行为树可以描述某个Agent类型的行为,首先需要创建自己的Agent子类。

视频

通过菜单项“视图”->“类型信息”(或快捷键Ctrl+M),打开类型信息浏览器,可以看到当前类型列表中只有一个默认生成的behaviac::Agent基类,如下图所示:

empty_types

点击右上角的“新增”按钮,弹出“新增类型”窗口,如下图所示:

first_agent

  • 将“类型”选择为“Agent”
  • 将“名称”设置为“FirstAgent”
  • 右上方的“生成代码?”默认是勾选上的,表示编辑器要自动生成该类型的代码文件,否则该类型代码需要手工编写
  • “生成位置”是生成该类型文件所在的文件夹,默认不用设置,表示默认生成在工作区中配置的“代码生成位置”里面

点击“确定”按钮后,可以看到类型列表中有了“FirstAgent”这个类,如下图所示:

first_agent_methods

选中该“FirstAgent”类,并设置中部的 “成员类型”为“Method”,可以看到目前该类有六个来自behaviac::Agent基类的成员方法,方法名前面的“~”表示该方法是从基类继承过来的,如上图所示。

点击右侧的“新增”按钮,弹出“新增方法”窗口,如下图所示:

create_sayhello

将新方法的“名字”设为“SayHello”,其他参数暂不用设置。

直接点击“确定”按钮,回到类型信息浏览器,可以看到“FirstAgent”类有了第一个自己添加的成员方法“SayHello”,其他几个带“~”的方法表示是从基类继承过来的成员方法,如下图所示:

first_method

现在,我们有了第一个Agent子类的“FirstAgent”,以及它的成员方法“SayHello”。

点击右下方的“应用”按钮,自动生成类型和其他相关的“胶水”代码文件等。

点击左下方的“打开代码生成位置”按钮,可以查看生成的代码文件,如下图所示:

generated_types

其中,behaviac_types.h需要包含到自己的代码中,以便使用自动生成的类型和“胶水”代码,如下代码所示:

#include "behaviac_generated/types/behaviac_types.h"

“internal”文件夹中包含了前面提及的类型和“胶水”代码,如下图所示:

generated_internal

其中,behaviac_agent_member_visitor.h文件生成了用于访问类的私有属性和方法的“胶水”代码,behaviac_agent_meta.h/cpp文件生成了用于注册类及其属性、方法、实例等信息的“胶水”代码,这些“胶水”代码主要是为了程序端可以通过名字自动取用到类及其成员属性、方法及其实例等。

这些生成的代码需要添加到自己的项目中一起编译构建,如下图所示:

include_project

创建行为树

有了上面创建的Agent子类“FirstAgent”后,就可以开始创建和编辑行为树了。

视频

如果对编辑器的按键操作不熟悉,请通过菜单项“帮助”->“起始页”,查看按键说明,如下图所示:

controls

点击工具栏中的“新建行为树”按钮,如下图所示:

new_bt

新建行为树后,将该行为树名字改为“FirstBT”,现在有了第一棵行为树,该树只有一个根节点,如下图所示:

first_bt

鼠标左键单击选中该根节点,为该节点的“Agent类型”设置为前面创建的“FirstAgent”,表示这棵行为树用来描述FirstAgent类的行为,如下图所示:

agent_type

设置完后,可以发现根节点已经具有类型“FirstAgent”,如下图所示:

root_node

在左侧的节点列表中(如下图所示),用鼠标左键选中“动作”节点(或其他需要的节点)后,按住鼠标左键并拖拽该节点到右侧的主视口中,并将动作节点落在根节点右侧的黑色三角箭头处。

node_tree

拖拽完毕,可以看到行为树有了叶子节点“动作”,如下图所示:

action_node

选中该动作节点,在右侧“动作的属性”窗口中,为其选择成员方法“SayHello”,并将另一参数“决定状态的选项”设置为“Success”,如下图所示:

action_properties

如果想了解动作节点的更多细节,请参考文档《动作节点》。或者选中某个节点后,按F1键或右键菜单直接打开该节点的文档。

至此,我们得到了一棵最简单但是完整的行为树,如下图所示:

first_bt_tree

导出行为树

编辑完行为树后,需要导出全部行为树,以便程序端加载使用。

视频

点击工具栏中的“导出全部”按钮,如下图所示:

export_all

弹出“导出”窗口,在导出格式中暂只用勾选“Xml”,点击右下方的“导出”按钮,如下图所示: export_bt

导出后,打开工作区中指定的导出目录,可以看到成功导出了FirstBT.xml文件和meta文件夹中的tutorial_1_cpp.meta.xml文件,如下图所示:

导出的FirstBT.xml文件就是交给程序端加载使用的行为树,tutorial_1_cpp.meta.xml文件是交给程序端加载使用的含有类型和自定义成员属性信息的文件。

运行时的使用

编写并运行程序

需要整合behaviac运行时库到自己的项目中来一起编译构建和运行行为树。如何编译构建behaviac运行时库,请参考文档《如何编译构建》。

C++版

对于自己的C++项目,需要包含behaviac库的inc目录(该inc目录位于源码压缩包的最顶级)。例如,对于Visual Studio项目,inc目录配置如下图所示:

include_headers

同时还需要包含编译behaviac生成的lib文件。例如,对于Visual Studio项目,behaviac lib文件配置如下图所示

include_lib

将编辑器生成的Agent子类的类型和相关的“胶水”代码都整合到自己的项目中,如下图所示:

在tutorial_1.cpp文件中,通过使用behaviac组件提供的API来体现行为树是如何加载和执行起来的。

首先,在InitBehaviac()方法中初始化behaviac的加载目录和文件格式等,如下代码所示:

bool InitBehavic()
{
    LOGI("InitBehavic\n");

    behaviac::Workspace::GetInstance()->SetFilePath("../tutorials/tutorial_1/cpp/exported");

    behaviac::Workspace::GetInstance()->SetFileFormat(behaviac::Workspace::EFF_xml);

    return true;
}

上面的几个接口说明如下:

  • Workspace::SetFilePath()用于设置加载编辑器导出的行为树所在的目录。
  • Workspace::SetFileFormat()用于设置加载的行为树格式,这里用的是xml格式。

接着,创建Agent子类“FirstAgent”的实例,并加载指定的行为树,这里的行为树名字为“FirstBT”,如下代码所示:

bool InitPlayer()
{
    LOGI("InitPlayer\n");

    g_FirstAgent = behaviac::Agent::Create<FirstAgent>();

    bool bRet = g_FirstAgent->btload("FirstBT");

    g_FirstAgent->btsetcurrent("FirstBT");

    return bRet;
}

上面的几个接口说明如下:

  • Agent::Create()用于创建Agent子类的实例。
  • Agent::btload()用于加载行为树,入口参数是行为树的名字,不要加后缀。
  • Agent::btsetcurrent()用于指定当前准备执行的行为树。

其次,开始执行行为树,如下代码所示:

void UpdateLoop()
{
    LOGI("UpdateLoop\n");

    int frames = 0;
    behaviac::EBTStatus status = behaviac::BT_RUNNING;

    while (status == behaviac::BT_RUNNING)
    {
        LOGI("frame %d\n", ++frames);
        status = g_FirstAgent->btexec();
    }
}

Agent::btexec()用于执行一次前面通过Agent::btsetcurrent()指定的行为树。

另外,如果不通过Agent::btexec()来单独执行行为树,也可以调用Workspace::Update()的方式来统一执行所有Agent实例的行为树,详见文章《运行时端的执行流程》。

然后,对创建的Agent实例进行销毁释放,并清理整个工作区,如下代码所示:

void CleanupPlayer()
{
    LOGI("CleanupPlayer\n");

    behaviac::Agent::Destroy(g_FirstAgent);
}

void CleanupBehaviac()
{
    LOGI("CleanupBehaviac\n");

    behaviac::Workspace::GetInstance()->Cleanup();
}

最后,打开FirstAgent.cpp文件,并修改FirstAgent::SayHello()方法如下:

///<<< THE METHOD HEAD
void FirstAgent::SayHello()
{
    ///<<< BEGIN WRITING YOUR CODE
    printf("\nHello Behaviac!\n\n");
    ///<<< END WRITING YOUR CODE
}

注意:自己的代码需要添加在“///<<< BEGIN WRITING YOUR CODE”和“///<<< END WRITING YOUR CODE”之间,以便编辑器下次生成代码的时候,可以自动合并手工添加的内容和生成的内容。

编译并运行整个程序,执行结果如下:

exec_result

可以看到,程序结果输出了“Hello Behaviac!”,也就是成功执行了我们创建的第一棵最简单的行为树,并成功执行了动作节点配置的成员方法“SayHello”。

下载源码包后,打开projects目录中的工程文件behaviac.sln(Windows平台)或make文件(Linux平台),编译并执行tutorial_1项目,就可以看到上图的结果。

纯C#版

本教程纯C#版的工作区文件是安装包目录下的tutorials/tutorial_1/workspace/tutorial_1_cs.workspace.xml,相关参数配置如下图所示:

对于自己的C#项目,需要包含behaviac库的所有运行时源码(在源码包的integration\unity\Assets\Scripts\behaviac\runtime文件夹中)整合进来,如下图所示:

将编辑器生成的Agent子类的类型和相关的“胶水”代码都整合到自己的项目中,如下图所示:

为项目添加条件编译宏BEHAVIAC_NOT_USE_UNITY,表示用的是纯C#模式,没有使用Unity相关的API,如下图所示:

cs_marco

在tutorial_1.cs文件中,通过使用behaviac组件提供的API来体现行为树是如何加载和执行起来的。

首先,在InitBehaviac()方法中初始化behaviac的加载目录和文件格式等,如下代码所示:

static bool InitBehavic()
{
    Console.WriteLine("InitBehavic");

    behaviac.Workspace.Instance.FilePath = "../../exported";
    behaviac.Workspace.Instance.FileFormat = behaviac.Workspace.EFileFormat.EFF_xml;

    return true;
}

上面的几个接口说明如下:

  • Workspace.FilePath用于设置加载编辑器导出的行为树所在的目录。
  • Workspace.FileFormat用于设置加载的行为树格式,这里用的是xml格式。

接着,创建Agent子类“FirstAgent”的实例,并加载指定的行为树,这里的行为树名字为“FirstBT”,如下代码所示:

static bool InitPlayer()
{
    Console.WriteLine("InitPlayer");

    g_FirstAgent = new FirstAgent();

    bool bRet = g_FirstAgent.btload("FirstBT");
    Debug.Assert(bRet);

    g_FirstAgent.btsetcurrent("FirstBT");

    return bRet;
}

上面的几个接口说明如下:

  • Agent.btload()用于加载行为树,入口参数是行为树的名字,不要加后缀。
  • Agent.btsetcurrent()用于指定当前准备执行的行为树。

其次,开始执行行为树,如下代码所示:

static void UpdateLoop()
{
    Console.WriteLine("UpdateLoop");

    int frames = 0;
    behaviac.EBTStatus status = behaviac.EBTStatus.BT_RUNNING;

    while (status == behaviac.EBTStatus.BT_RUNNING)
    {
        Console.WriteLine("frame {0}", ++frames);

        status = g_FirstAgent.btexec();
    }
}

Agent.btexec()用于执行一次前面通过Agent.btsetcurrent()指定的行为树。

然后,对创建的Agent实例进行销毁释放,并清理整个工作区,如下代码所示:

static void CleanupPlayer()
{
    Console.WriteLine("CleanupPlayer");

    g_FirstAgent = null;
}

static void CleanupBehaviac()
{
    Console.WriteLine("CleanupBehaviac");

    behaviac.Workspace.Instance.Cleanup();
}

最后,打开FirstAgent.cs文件,并修改SayHello()方法如下:

///<<< THE METHOD HEAD
public void SayHello()
{
    ///<<< BEGIN WRITING YOUR CODE
    Console.WriteLine();
    Console.WriteLine("Hello Behaviac!");
    Console.WriteLine();
    ///<<< END WRITING YOUR CODE
}

注意:自己的代码需要添加在“///<<< BEGIN WRITING YOUR CODE”和“///<<< END WRITING YOUR CODE”之间,以便编辑器下次生成代码的时候,可以自动合并手工添加的内容和生成的内容。

编译并运行整个程序,执行结果如下:

exec_result

可以看到,程序结果输出了“Hello Behaviac!”,也就是成功执行了我们创建的第一棵最简单的行为树,并成功执行了动作节点配置的成员方法“SayHello”。

下载源码包后,打开tutorials/CsTutorials目录中的工程文件CsTutorials.sln,编译并执行tutorial_1项目,就可以看到上图的结果。

Unity C#版

打开Unity编辑器创建一个空的Unity项目,保存该项目,或者直接打开安装目录下的tutorials/tutorial_1/unity目录中的项目。

通过behaviac编辑器创建一个工作区(或者直接打开安装包目录下的Unity工作区文件tutorials/tutorial_1/workspace/tutorial_1_unity.workspace.xml),相关参数配置如下图所示:

添加FirstAgent类及其成员方法SayHello()、创建和导出行为树FirstBT等流程跟上文相同。

导出整个工作区后,可以看到在目录tutorials\tutorial_1\unity\Assets\Scripts\behaviac\behaviac_generated\types有了自动生成的源码文件,如下图所示:

unity_export

在Unity编辑器中导入安装包目录下的behaviac运行时包integration/behaviac***.unitypackage。

打开整个项目的源码工程,可以看到有了behaviac的运行时库runtime和编辑器生成的源码behaviac_generated,如下图所示:

unity_code

打开FirstAgent.cs文件,添加代码如下:

首先,在InitBehaviac()方法中初始化behaviac的加载目录和文件格式等,如下代码所示:

private static string ExportedFilePath
{
    get
    {
        string relativePath = "/Resources/behaviac/exported";

        if (Application.platform == RuntimePlatform.WindowsEditor) {
            return Application.dataPath + relativePath;
        }
        else if (Application.platform == RuntimePlatform.WindowsPlayer) {
            return Application.dataPath + relativePath;
        }
        else {
            return "Assets" + relativePath;
        }
    }
}

private bool InitBehavic()
{
    behaviac.Debug.LogWarning("InitBehavic");

    behaviac.Workspace.Instance.FilePath = ExportedFilePath;
    behaviac.Workspace.Instance.FileFormat = behaviac.Workspace.EFileFormat.EFF_xml;

    return true;
}

接着,加载指定的行为树,这里的行为树名字为“FirstBT”,如下代码所示:

private bool InitPlayer()
{
    behaviac.Debug.LogWarning("InitPlayer");

    bool bRet = this.btload("FirstBT");
    if (bRet)
    {
        this.btsetcurrent("FirstBT");
    }

    return bRet;
}

在Awake()方法中调用InitBehavic和InitPlayer方法,如下代码所示:

void Awake()
{
    InitBehavic();

    InitPlayer();
}

其次,在Update()方法中执行行为树,如下代码所示:

behaviac.EBTStatus _status = behaviac.EBTStatus.BT_RUNNING;

void Update()
{
    if (_status == behaviac.EBTStatus.BT_RUNNING)
    {
        behaviac.Debug.LogWarning("Update");

        _status = this.btexec();
     }
}

然后,修改SayHello()方法如下:

public void SayHello()
{
    ///<<< BEGIN WRITING YOUR CODE SayHello
    behaviac.Debug.LogWarning("Hello Behaviac!");
    ///<<< END WRITING YOUR CODE
}

注意:自己的代码需要添加在“///<<< BEGIN WRITING YOUR CODE”和“///<<< END WRITING YOUR CODE”之间,以便编辑器下次生成代码的时候,可以自动合并手工添加的内容和生成的内容。

最后,在Unity编辑器中添加一个GameObject,并且为其添加脚本组件FirstAgent,如下图所示:

unity_component

在Unity编辑器中运行这个简单的程序,执行结果如下:

unity_output

下载源码包后,通过Unity编辑器打开并运行tutorials/tutorial_1/unity目录中的项目,就可以看到上图的结果。

本教程相关的工作区和代码工程详见源码包的目录tutorials/tutorial_1

2 thoughts on “教程1:Hello Behaviac

发表评论

电子邮件地址不会被公开。 必填项已用*标注