|
|
5 tháng trước cách đây | |
|---|---|---|
| .. | ||
| Image | 5 tháng trước cách đây | |
| README.md | 5 tháng trước cách đây | |
https://zhuanlan.zhihu.com/p/608864183
获取当前的 BehaviorTreeManager,每个 UWorld 中都有一个 AISystem 属性,用于 AI 管理,而 AISystem 中存储着 BehaviorTreeManager
UBehaviorTreeManager* UBehaviorTreeManager::GetCurrent(UWorld* World)
{
UAISystem* AISys = UAISystem::GetCurrentSafe(World);
return AISys ? AISys->GetBehaviorTreeManager() : nullptr;
}
在 UBehaviorTreeComponent::PushInstance 中会通过 BTManager 来加载行为树
const bool bLoaded = BTManager->LoadTree(TreeAsset, RootNode, InstanceMemorySize);
通过 LoadTree 函数或者该行为树实例所占内存大小 InstanceMemorySize 和根节点 Root
通过 UBehaviorTreeManager::LoadedTemplates 缓存加载过的 UBehaviorTree 资产信息,如果加载过直接从缓存中获取 InstanceMemorySize 和 Root
如果没有加载过该 UBehaviorTree 资产,则开始加载
由于一个
World的UBehaviorTreeManager是唯一的,所以理论上来说每个资产只会被加载一次
首先通过 InitializeNodeHelper 来缓存所有节点信息
TArray<FBehaviorTreeNodeInitializationData> InitList;
uint16 ExecutionIndex = 0;
InitializeNodeHelper(NULL, TemplateInfo.Template, 0, ExecutionIndex, InitList, Asset, this);
TemplateInfo.Template就是 根节点 的复制体
然后计算每个节点占用的内存大小,计算并设置数据内存偏移
for (int32 Index = 0; Index < InitList.Num(); Index++)
{
InitList[Index].Node->InitializeNode(InitList[Index].ParentNode, InitList[Index].ExecutionIndex, InitList[Index].SpecialDataSize + MemoryOffset, InitList[Index].TreeDepth);
MemoryOffset += InitList[Index].DataSize;
}
在运行时,会将每个节点的数据都保存在一个连续内存中,通过各自节点的 MemoryOffset 直接从连续内存中获取对应的数据
虽然函数名字是 InitializeNode,其实是 复制 + 初始化,因为函数中充斥着 StaticDuplicateObject
首先,根节点一定是 UBTCompositeNode,从上图就可以看到 Root 下只能连接且只能连接一个 UBTCompositeNode
Composite: 组合,混合
作为 UBTCompositeNode 自然保存着 子节点 和挂载在自己身上的 Serve 节点
UCLASS(Abstract, MinimalAPI)
class UBTCompositeNode : public UBTNode
{
TArray<FBTCompositeChild> Children;
TArray<TObjectPtr<UBTService>> Services;
// some function else ...
}
struct FBTCompositeChild
{
TObjectPtr<UBTCompositeNode> ChildComposite = nullptr;
TObjectPtr<UBTTaskNode> ChildTask = nullptr;
TArray<TObjectPtr<UBTDecorator>> Decorators;
TArray<FBTDecoratorLogic> DecoratorOps;
};
FBTCompositeChild代表子节点,根据ChildComposite和ChildTask的属性是否有效来判断是CompositeNode还是TaskNode
由于根节点是 UBTCompositeNode,它不能也不应该存在 UBTDecorator,所以在上图中,根节点的 UBTDecorator 是深蓝色的,表示无效
回到 InitializeNodeHelper 函数中
FBehaviorTreeInstance 用于管理一个行为树实例
通过 BTManager->LoadTree 得到 RootNode 和 InstanceMemorySize,既行为树的根节点和数据所占内存大小
FBehaviorTreeInstance& NewInstance = InstanceStack.AddDefaulted_GetRef();
NewInstance.InstanceIdIndex = UpdateInstanceId(&TreeAsset, ActiveNode, InstanceStack.Num() - 1);
NewInstance.RootNode = RootNode;
NewInstance.ActiveNode = NULL;
NewInstance.ActiveNodeType = EBTActiveNode::Composite;
// initialize memory and node instances
FBehaviorTreeInstanceId& InstanceInfo = KnownInstances[NewInstance.InstanceIdIndex];
int32 NodeInstanceIndex = InstanceInfo.FirstNodeInstance;
const bool bFirstTime = (InstanceInfo.InstanceMemory.Num() != InstanceMemorySize);
if (bFirstTime)
{
InstanceInfo.InstanceMemory.AddZeroed(InstanceMemorySize);
InstanceInfo.RootNode = RootNode;
}
NewInstance.SetInstanceMemory(InstanceInfo.InstanceMemory);
NewInstance.Initialize(*this, *RootNode, NodeInstanceIndex, bFirstTime ? EBTMemoryInit::Initialize : EBTMemoryInit::RestoreSubtree);
InstanceIdIndex是为了解决嵌套行为树执行时,方便实例识别
| 参数类型 | 属性 | 作用 |
|---|---|---|
| UBTCompositeNode* | RootNode | 行为树根节点 |
| UBTNode* | ActiveNode | 当前正在执行的节点 |
| TArray | ActiveAuxNodes | 存储当前激活的辅助节点() |
| TArray | ParallelTasks | 并行节点的多个任务 |
| TArray | InstanceMemory | 节点运行实例的数据 |
| uint8 | InstanceIdIndex | 行为树实例的ID,对应 KnowInstances 数组的唯一实例 |
| FBTInstanceDeactivation | DeactivationNotify | 当前活动节点的类型 |
| FBTInstanceDeactivation | DeactivationNotify | 子树停用时的回调委托 |