本人也是第一次使用behaviac做开发,可能有很多不对的地方和不明确的地方。希望各路大神能指点迷津或者指出错误。感谢!!!
本篇主要讲了: 使用behaviac编辑的行为树可以导出数据:xml文件和胶水文件(文章后面会解析),Unity通过XML和胶水文件来实现其功能。
一:使用Behaviac创建cs文件(行为脚本)
在behaviac升到3.6.x之后,我们就可以在behaviac中船舰cs文件了,如下图所示在类型信息面板中创建一个基于Agent的行为类的时候,可以勾选“生成代码”的勾选框,然后点击 “打开代码生成位置” 就能看到一个cs文件。如果你不勾选 “生成代码” 的话,就只能在工程里面自己建一个behaviac中的Agent类。再自己绑定;
下面我们看一下勾选“生成代码”和不勾选时,Agent类的区别:
(一)勾选生成代码后创建的类:
using System;
using System.Collections;
using System.Collections.Generic;
public class DebugAgent : behaviac.Agent
{
public string name = "";
public behaviac.EBTStatus PrintName()
{
///<<< BEGIN WRITING YOUR CODE PrintName
return behaviac.EBTStatus.BT_INVALID;
///<<< END WRITING YOUR CODE
}
}
(二)不勾选生成代码时,自己创建的类;需要绑定
using UnityEngine;
using System.Collections;
using behaviac;
[behaviac.TypeMetaInfo("DebugAgent", "DebugAgent->Agent")]
public class DebugAgent : Agent
{
[behaviac.MemberMetaInfo()]//标记成员变量
public string name;
[behaviac.MethodMetaInfo()]//标记成员函数
public EBTStatus PrintName()
{
UnityEngine.Debug.Log(name);
return EBTStatus.BT_SUCCESS;
}
}
以上两种方式都可以实现,不过为了方便,我是用behaviac自动生成的cs文件;
二:搭建行为树,导出胶水文件、cs和xml文件
然后我们简单的搭建一个行为树:
先看一下我新建的Agent实例(有个mName变量和一个PrintName的函数)
然后是我的行为树;(循环打印十次mName的变量值)
ok;行为树搭建完了,导出行为树DebugTree;勾选XML和cs
在导出的目录下(项目根目录的exported文件夹下)会有以下两个文件夹和一个xml文件;meta是元数据,用不到可以不用管;behaviac_generated文件夹下就是胶水文件。然后就是DebugTree.xml用来让unity的程序实现运行树的逻辑。
三:胶水文件、xml、资源包导入unity工程
我们需要将三个东西导入到项目中:
(一)behaviac资源包导入:
将behaviac3.6.39.unitypackage资源包导入工程,可在官网下载;点击跳转下载
(二)胶水文件导入:
将“步骤二”中导出的胶水文件所在的整个behaviac_generated文件夹拖到项目中;
(三)xml文件导入
将“步骤二”导出的xml文件拖到工程中
ok;我的项目结构如下
四:脚本注册绑定xml行为树
(一)首先我们需要创建一个和behaviac注册绑定的类来建立连接,能让程序按照树的逻辑运行。创建一个BehaviacHelper脚本让它用来加载行为树:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BehaviacHelper : MonoBehaviour
{
//xml的名称
public static string mBTName = "DebugTree";
private void Start()
{
//注册
InitRegister();
//加载
LaodBT();
//执行
DebugAgent[] agents = FindObjectsOfType(typeof(DebugAgent)) as DebugAgent[];
for (int i = 0; i < agents.Length; i++)
{
agents[i].ExcuteBT();
}
}
/// <summary>
/// 加载行为树
/// </summary>
void LaodBT()
{
// ① 第一种加载的方式:用Agent类加载
behaviac.Agent[] agents = FindObjectsOfType(typeof(behaviac.Agent)) as behaviac.Agent[];
for (int i = 0; i < agents.Length; i++)
{
agents[i].btload(mBTName , true);
}
/*
* ② 第二种加载的方式:用WorkSpace加载。一次性加载(这个没测试过,不知道有没有问题)
* bool btloadResult = behaviac.Workspace.Instance.Load(mBTName, true);
* if (!btloadResult)
* Debug.LogError("Behavior tree data load failed! " + mBTName);
*/
}
/// <summary>
/// 行为注册
/// </summary>
void InitRegister()
{
//设置调试功能
behaviac.Config.IsHotReload = true;
behaviac.Config.IsLogging = true;
behaviac.Config.IsSocketing = true;
behaviac.Config.IsProfiling = true;
// ↑↑↑↑↑↑↑ 上面设置的是开发功能相关的调试功能,想要了解更多可以到官网查看资料:https://www.behaviac.com/language/zh/config/
//指定xml文件的加载路径;和加载方式:使用XML方式加载
//###behaviac默认路径是resources下,要想改成其他路径,要同时改动它的FileManger脚本中的FileOpen方法
behaviac.Workspace.Instance.FilePath = Application.streamingAssetsPath;
behaviac.Workspace.Instance.FileFormat = behaviac.Workspace.EFileFormat.EFF_xml;
//根据Agent名称,注册
behaviac.Agent.RegisterInstanceName<DebugAgent>("DebugAgent");
/* 你可以注册多棵树:
* behaviac.Agent.RegisterInstanceName<DebugAgent_2>("DebugAgent_2");
*/
}
private void OnDestroy()
{
behaviac.Workspace.Instance.UnLoadAll();
behaviac.Workspace.Instance.Cleanup();
}
}
(二)完善DebugAgent类:
如下图目录所示可以找到我们导入到unity中的DebugAgent脚本;
打开这个脚本,完善它
// -------------------------------------------------------------------------------
// THIS FILE IS ORIGINALLY GENERATED BY THE DESIGNER.
// YOU ARE ONLY ALLOWED TO MODIFY CODE BETWEEN '///<<< BEGIN' AND '///<<< END'.
// PLEASE MODIFY AND REGENERETE IT IN THE DESIGNER FOR CLASS/MEMBERS/METHODS, ETC.
// -------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
public class DebugAgent : behaviac.Agent
{
public string mName = "";
/// <summary>
/// 执行行为树回调
/// </summary>
public void ExcuteBT()
{
this.btsetcurrent(BehaviacHelper.mBTName);
behaviac.Workspace.Instance.DebugUpdate();
this.btexec();
}
public behaviac.EBTStatus PrintName()
{
print(mName);
return behaviac.EBTStatus.BT_SUCCESS;
}
}
ok,因为我们在上面的脚本中写的加载的路径是在StreamingAssest文件夹下,所以,在工程中创建StreamingAssets文件夹,并将xml文件放进来:
OK,这样我们注册的完成了,执行回调也写了;接下来就是Unity编译器的工作了
五:unity中创建测试运行
(一)创建一个空物体,命名为BehaviacHelper。并挂载BehaviacHelper脚本。
(二)再次创建两个空物体,为其挂载DebugAgent脚本,并各设置mName变量为不同的值。
(三)运行程序,结果如下,不同的mName变量的值都打印了10次
以上便是我近期研究的behaviac和unity整合使用的总结,如有写错或者不准确的地方还请各位路过的大佬指点明津。抱拳。告辞。
.
补充一点:很重要,拿笔记下来:
当你把导出的xml放到项目中了,behaviac默认读取的路径是Resources文件夹,如果你想要将xml放到自定义的路径中,在behaviac中有个FileManager脚本,你需要重写里面的FileOpen、DirOpen和FileClose方法。而且人家腾讯在函数注释上声明的很明确了已经,如果你自定义了一个路径,你需要重写此函数。
例如:本篇文章就将xml文件放到了StreamingAssets下,下面是我FileManager.cs改动后的代码:
using System.IO;
using System.Collections;
using System.Collections.Generic;
// please define BEHAVIAC_NOT_USE_UNITY in your project file if you are not using unity
#if !BEHAVIAC_NOT_USE_UNITY
// if you have compiling errors complaining the following using 'UnityEngine',
//usually, you need to define BEHAVIAC_NOT_USE_UNITY in your project file
using UnityEngine;
#endif//!BEHAVIAC_NOT_USE_UNITY
namespace behaviac
{
public class FileManager
{
#region Singleton
private static FileManager ms_instance = null;
public FileManager()
{
Debug.Check(ms_instance == null);
ms_instance = this;
}
//~FileManager()
//{
// ms_instance = null;
//}
#endregion Singleton
public static FileManager Instance
{
get
{
if (ms_instance == null)
{
ms_instance = new FileManager();
}
return ms_instance;
}
}
/// <summary>
/// open the specified file, this function should be consistent with
/// Workspace.SetWorkspaceSettings's first param 'workspaceExportPath' and Workspace.Load's first param 'relativePath'
/// as 'filePath' is the conbination of workspaceExportPath and relativePath
///
/// you may need to override this function if you gave a customized 'workspaceExportPath' or used a AssetBundle.
/// </summary>
/// <returns>The open.</returns>
/// <param name="filePath">without extension</param>
/// <param name="ext">'ext' coult be .xml or .bson</param>
public virtual byte[] FileOpen(string filePath, string ext)
{
try
{
if (ext == ".bson")
{
ext += ".bytes";
}
filePath += ext;
byte[] pBuffer = File.ReadAllBytes(filePath);
// ***经过测试,当你exe包打出来,只要filePath以及xml文件没有问题;这里就直接跳出了
return pBuffer;
}
catch (System.Exception e)
{
string msg = string.Format("FileManager::FileOpen exception:'{0}'", filePath);
behaviac.Debug.LogWarning(msg + e.Message + e.StackTrace);
}
try
{
#if !BEHAVIAC_NOT_USE_UNITY
if (Application.platform == RuntimePlatform.WindowsEditor ||
Application.platform == RuntimePlatform.OSXEditor)
#endif
{
if (ext == ".bson")
{
ext += ".bytes";
}
filePath += ext;
byte[] pBuffer = File.ReadAllBytes(filePath);
return pBuffer;
}
#if !BEHAVIAC_NOT_USE_UNITY
else
{
if (ext == ".bson")
{
filePath += ext;
}
#region 设置xml文件位置
//skip 'Resources/'
//int k0 = filePath.IndexOf("Resources");
int k0 = filePath.IndexOf("StreamingAssets");
#endregion
if (k0 != -1)
{
k0 += /*10*/16;
string filePathInResources = filePath.Substring(k0);
TextAsset ta = Resources.Load(filePathInResources) as TextAsset;
if (ta == null)
{
string msg = string.Format("FileManager::FileOpen failed:'{0}' not loaded", filePath);
behaviac.Debug.LogWarning(msg);
return null;
}
else
{
byte[] pBuffer = ta.bytes;
return pBuffer;
}
}
else
{
string msg = string.Format("FileManager::FileOpen failed:'{0}' should be in /Resources", filePath);
behaviac.Debug.LogWarning(msg);
}
}
#endif
}
catch (System.Exception e)
{
string msg = string.Format("FileManager::FileOpen exception:'{0}'", filePath);
behaviac.Debug.LogWarning(msg + e.Message + e.StackTrace);
}
return null;
}
public virtual void FileClose(string filePath, string ext, byte[] pBuffer)
{
}
public virtual List<byte[]> DirOpen(string szDir, string ext)
{
List<byte[]> buffers = new List<byte[]>();
try
{
//#if BEHAVIAC_CS_ONLY
//#endif
#if !BEHAVIAC_NOT_USE_UNITY
if (Application.platform == RuntimePlatform.WindowsEditor ||
Application.platform == RuntimePlatform.OSXEditor)
#endif//
{
string searchPattern = (ext == ".bson") ? "*.bson.bytes" : "*.xml";
string[] allFiles = Directory.GetFiles(szDir, searchPattern, SearchOption.TopDirectoryOnly);
foreach (string filePath in allFiles)
{
//string filePath = file.Replace('\\', '/');
byte[] pBuffer = FileOpen(filePath, "");
buffers.Add(pBuffer);
}
return buffers;
}
#if !BEHAVIAC_NOT_USE_UNITY
else
{
//Debug.LogWarning("szDir.ext:" + szDir + " " + ext);
#region 设置xml文件位置
//int resourceIndex = szDir.IndexOf("Resources");
int resourceIndex = szDir.IndexOf("StreamingAssets");
#endregion
if (resourceIndex != -1)
{
string localPath = szDir.Substring(resourceIndex + /*10*/16);
//Debug.LogWarning("localPath: " + localPath);
//TextAsset[] as2 = Resources.LoadAll<TextAsset>("behaviac/exported/meta");
TextAsset[] metaFiles = Resources.LoadAll<TextAsset>(localPath);
foreach (TextAsset mf in metaFiles)
{
//Debug.LogWarning("mf.name: " + mf.name);
if (!string.IsNullOrEmpty(mf.name))
{
//skip
bool bIsBson = mf.name.IndexOf(".bson") > -1;
bool bAskBson = (ext == ".bson");
if ((bIsBson && bAskBson) || (!bIsBson && !bAskBson))
{
byte[] pBuffer = mf.bytes;
buffers.Add(pBuffer);
}
}
}
return buffers;
}
}
#endif
}
catch (System.Exception e)
{
string msg = string.Format("FileManager::DirOpen exception:'{0}'", szDir);
behaviac.Debug.LogWarning(msg + e.Message + e.StackTrace);
}
return null;
}
public virtual bool FileExist(string filePath, string ext)
{
return File.Exists(filePath + ext);
}
}
}