本文档描述的是3.6及以后版本,对于3.5及以前的老版本请参考分类“3.5”。
我们知道,面向对象程序编程的定义就是使用对象来做设计,对象即是类的实例。behaviac组件是基于Agent类及其实例来运转的,Agent类的实例加载和执行行为树,而在行为树的节点中又有可能用到了Agent实例的成员属性或方法。
在编辑器的节点属性窗口中,为了给该节点配置参数,首先需要选择一个实例,然后再选择该实例的成员属性或方法,如下图所示:
这些实例来自于如下三个方面:
- Self:当前行为树根节点所配置的Agent类的实例,类似于程序语言中的this。
- 成员实例:当前行为树根节点所配置的Agent类的成员属性,或是当前行为树的局部变量,需要是Agent或其子类类型。
- 全局实例:在类型信息浏览器中编辑并生成注册代码的各种Agent或其子类的全局变量。
成员实例
对于成员实例,在上图所示的节点属性窗口中会根据当前行为树根节点所配置的Agent类型,自动列举出所有的成员实例以供选择。但在使用该成员实例之前,需要确保该实例已经赋过值,而不是空指针或引用。
在类型信息浏览器中添加新的Agent子类SecondAgent,并为其添加一个int类型的成员属性p2,如下图所示:
然后,为FirstAgent类添加SecondAgent类型的成员属性pInstance,如下图所示:
点击上图中的“确认”按钮后,可以看到FirstAgent类多了一个成员属性pInstance,如下图所示:
全局实例
对于全局实例,各种Agent或其子类实例的名字注册和绑定是为了支持单件(Singleton)或者类似确定的全局性实例(同一个类可能会有若干个实例而不是仅仅有一个实例),如player、camera、director等。
点击类型信息浏览器中部的“实例名称”右侧的“新增”按钮,添加SecondAgent类型的全局实例SecondAgentInstance,如下图所示:
点击上图中的“确认”按钮后,可以看到SecondAgent类的“实例名称”下拉列表中有了新加的全局实例SecondAgentInstance,如下图所示:
点击上图中右下方的“应用”按钮,就可以在行为树中分别使用这2个新加的成员实例和全局实例了。
应用
新建一棵行为树“InstanceBT”,依次添加序列、赋值、条件和动作节点,并为根节点选择FirstAgent类型,将动作节点配置为Self及其成员方法SayHello,如下图所示:
选中ID为1的赋值节点,在其属性窗口“左参数”的实例名中,可以为其选择Self、SecondAgentInstance和pInstance 3个实例了,如下图所示:
为其选择SecondAgentInstance及其成员属性p2,如下图所示:
类似的,选中ID为2的赋值节点,为其选择pInstance及其成员属性p2,如下图所示:
选中ID为3的条件节点,将“左参数”选择为SecondAgentInstance及其成员属性p2,将“右参数”选择为pInstance及其成员属性p2,将“操作符”选择为“>”,如下图所示:
配置完之后,得到行为树“InstanceBT”如下图所示:
导出行为树后,程序端就可以在加载和执行该行为树“InstanceBT”了。
C++版
在源码包的tutorials/tutorial_3/cpp/tutorial_3.cpp文件中,定义了3个变量,如下代码所示:
FirstAgent* g_FirstAgent = NULL; SecondAgent* g_SecondAgent = NULL; SecondAgent* g_ThirdAgent = NULL;
其中,g_FirstAgent用于加载和执行行为树“InstanceBT”,g_SecondAgent用于赋值给g_FirstAgent的成员属性pInstance,g_ThirdAgent作为全局实例供行为树“InstanceBT”中的节点使用。这3个变量的初始化,如下代码所示:
bool InitPlayer() { LOGI("InitPlayer : %s\n", "InstanceBT"); // 创建g_FirstAgent,并加载行为树“InstanceBT” g_FirstAgent = behaviac::Agent::Create<FirstAgent>(); bool bRet = g_FirstAgent->btload("InstanceBT"); g_FirstAgent->btsetcurrent("InstanceBT"); // 创建g_SecondAgent,并将该实例赋给g_FirstAgent的成员pInstance g_SecondAgent = behaviac::Agent::Create<SecondAgent>(); g_FirstAgent->SetSecondAgent(g_SecondAgent); // 创建g_ThirdAgent,并将"SecondAgentInstance"绑定给该实例 g_ThirdAgent = behaviac::Agent::Create<SecondAgent>("SecondAgentInstance"); return bRet; }
C#版
在源码包的tutorials/tutorial_3/cs/tutorial_3.cs文件中,定义了3个变量,如下代码所示:
static FirstAgent g_FirstAgent; static SecondAgent g_SecondAgent; static SecondAgent g_ThirdAgent;
其中,g_FirstAgent用于加载和执行行为树“InstanceBT”,g_SecondAgent用于赋值给g_FirstAgent的成员属性pInstance,g_ThirdAgent作为全局实例供行为树“InstanceBT”中的节点使用。这3个变量的初始化,如下代码所示:
static bool InitPlayer() { Console.WriteLine("InitPlayer"); // 创建g_FirstAgent,并加载行为树“InstanceBT” g_FirstAgent = new FirstAgent(); bool bRet = g_FirstAgent.btload("InstanceBT"); Debug.Assert(bRet); g_FirstAgent.btsetcurrent("InstanceBT"); // 创建g_SecondAgent,并将该实例赋给g_FirstAgent的成员pInstance g_SecondAgent = new SecondAgent(); g_FirstAgent._set_pInstance(g_SecondAgent); // 创建g_ThirdAgent,并将"SecondAgentInstance"绑定给该实例 g_ThirdAgent = new SecondAgent(); behaviac.Agent.BindInstance(g_ThirdAgent, "SecondAgentInstance"); return bRet; }
编译并执行,可以看到输出了“Hello Behaviac!”,说明行为树的执行结果符合我们的预期,Agent实例得到了正确的使用。
本教程相关的工作区和代码工程详见源码包的目录tutorials/tutorial_3。
使用U3D跟着教程3进行操作,到最后运行会报错找不到SecondAgentInstance,而在代码里确实已经调用过
SecondAgent SecondAgentInstance = new SecondAgent();
Agent.BindInstance(SecondAgentInstance, “SecondAgentInstance”);
请问有可能是什么原因导致的呢?
编辑器生成的胶水代码也要一起编译,里面有自动生成Agent.RegisterInstance()相关代码
感谢回复。胶水代码已经包含在工程里。经过调试runtime发现,在Util.cs的466行,
if (pAgent != null && pParent == null && !Utils.IsStaticClass(instanceName))这一句,在pParent有值的情况下,很奇怪的进入了if语句,从而在FirstAgent里寻找SecondAgentInstance实例,导致失败。而运行纯CS版本不会出现这个问题,是否因为Unity.Object重载了==操作符导致?谢谢。
可以改成这样试试:
if (!object.ReferenceEquals(pAgent, null) && object.ReferenceEquals(pParent, null) && !Utils.IsStaticClass(instanceName))
但==操作符并没有重载过
知道原因了,因为Unity版本的Agent是继承自MonoBehaviour的,如果按照纯C#版本教程那样new Agent(),在Unity的语义环境里是不允许有这种行为的,导致new出来的实例判空操作会返回true。在Unity的应用场景里应该将Agent挂到GameObject,是我自己使用方式不对:)。
再次感谢!
这里说到了一个比较重要的点,Unity版本的Agent其实建议不要继承自Mono,而是走存逻辑类。我现在的代码框架是脱离mono的,但因为Agent继承自mono,所以需要手动创建一个逻辑GameObject,非常的不优雅哟~
C#版支持Unity版和纯C#版,使用BEHAVIAC_CS_ONLY宏,纯C#版的Agent不从Mono继承