UE-AI模块的学习

UE-AI模块

蓝图的使用

自定义Task

重载Receive Execute AI 事件,并且在Finish Execute中勾选boolea为true,表示该Task执行成功
在这里插入图片描述
在这里插入图片描述
这里 Finish Execute表示该Task任务执行完毕,其参数 Success表示该Task执行是否成功,进而影响SequenceSelector的执行方式

当该Task不执行 Finish Execute 时,Task会一直保持运行状态,行为树会卡在当前节点,知道执行 Finish Execute,所以当行为树卡住不往后执行时,查找一下是否是没有执行 Finish Execute

如果想要自定义在Task中直接获得blackboard中的数据,可以定义参数 Blackboard Key Selector,并且把右边可编辑的眼睛打开
在这里插入图片描述
随后就可以在行为树中的Task节点上编辑输入的黑板值
在这里插入图片描述
在这里插入图片描述
最后在Task中把设置的值转换成指定类型的参数即可

自定义Decorator

重载 PerformConditionCheckAI 函数,返回一个bool值即可

在这里插入图片描述

类之间的关系

AI的实体对象

  • AI模块中几个类的继承关系
    • UBTAuxiliaryNode:装饰器、Service
    • UBTCompositeNode:Selector、Sequence、SimpleParallel
    • UBTTaskNode:任务节点的基类
      在这里插入图片描述

• AAIController::RunBehaviorTree中BehaviorTree
在这里插入图片描述

• BehaviorTree的子节点(Sequent、Selector、Simple Parallel)
在这里插入图片描述

• Blackboard
在这里插入图片描述

AI的组件

在这里插入图片描述

BehaviorTree

初始化流程

AIController

• RunBehaviorTree(UBehaviorTree* BTAsset)

bool AAIController::RunBehaviorTree(UBehaviorTree* BTAsset)
{
        if (BTAsset == NULL)
        {
                UE_VLOG(this, LogBehaviorTree, Warning, TEXT("RunBehaviorTree: Unable to run NULL behavior tree"));
                return false;
        }

        bool bSuccess = true;

        UBlackboardComponent* BlackboardComp = Blackboard;
        if (BTAsset->BlackboardAsset && (Blackboard == nullptr || Blackboard->IsCompatibleWith(BTAsset->BlackboardAsset) == false))
        {
                bSuccess = UseBlackboard(BTAsset->BlackboardAsset, BlackboardComp);
        }

        if (bSuccess)
        {
                UBehaviorTreeComponent* BTComp = Cast<UBehaviorTreeComponent>(BrainComponent);
                if (BTComp == NULL)
                {
                        UE_VLOG(this, LogBehaviorTree, Log, TEXT("RunBehaviorTree: spawning BehaviorTreeComponent.."));

                        BTComp = NewObject<UBehaviorTreeComponent>(this, TEXT("BTComponent"));
                        BTComp->RegisterComponent();
                }
               
                BrainComponent = BTComp;

                check(BTComp != NULL);
                BTComp->StartTree(*BTAsset, EBTExecutionMode::Looped);
        }

        return bSuccess;
}

在这里插入图片描述

• UseBlackboard(UBlackboardData* BlackboardAsset, UBlackboardComponent*& BlackboardComponent)

bool AAIController::UseBlackboard(UBlackboardData* BlackboardAsset, UBlackboardComponent*& BlackboardComponent)
{
        if (BlackboardAsset == nullptr)
        {
                UE_VLOG(this, LogBehaviorTree, Log, TEXT("UseBlackboard: trying to use NULL Blackboard asset. Ignoring"));
                return false;
        }

        bool bSuccess = true;
        Blackboard = FindComponentByClass<UBlackboardComponent>();

        if (Blackboard == nullptr)
        {
                Blackboard = NewObject<UBlackboardComponent>(this, TEXT("BlackboardComponent"));
                REDIRECT_OBJECT_TO_VLOG(Blackboard, this);
                if (Blackboard != nullptr)
                {
                        InitializeBlackboard(*Blackboard, *BlackboardAsset);
                        Blackboard->RegisterComponent();
                }

        }
        else if (Blackboard->GetBlackboardAsset() == nullptr)
        {
                InitializeBlackboard(*Blackboard, *BlackboardAsset);
        }
        else if (Blackboard->GetBlackboardAsset() != BlackboardAsset)
        {
                // @todo this behavior should be opt-out-able.
                UE_VLOG(this, LogBehaviorTree, Log, TEXT("UseBlackboard: requested blackboard %s while already has %s instantiated. Forcing new BB.")
                        , *GetNameSafe(BlackboardAsset), *GetNameSafe(Blackboard->GetBlackboardAsset()));
                InitializeBlackboard(*Blackboard, *BlackboardAsset);
        }

        BlackboardComponent = Blackboard;

        return bSuccess;
}

在这里插入图片描述

• InitializeBlackboard(UBlackboardComponent& BlackboardComp, UBlackboardData& BlackboardAsset)

bool AAIController::InitializeBlackboard(UBlackboardComponent& BlackboardComp, UBlackboardData& BlackboardAsset)
{
        check(BlackboardComp.GetOwner() == this);

        if (BlackboardComp.InitializeBlackboard(BlackboardAsset))
        {
                // find the "self" key and set it to our pawn
                const FBlackboard::FKey SelfKey = BlackboardAsset.GetKeyID(FBlackboard::KeySelf);
                if (SelfKey != FBlackboard::InvalidKey)
                {
                        BlackboardComp.SetValue<UBlackboardKeyType_Object>(SelfKey, GetPawn());
                }

                OnUsingBlackBoard(&BlackboardComp, &BlackboardAsset);
                return true;
        }
        return false;
}

这里的代码很简单,就是调用UBlackboardComponent的InitializeBlackboard初始化函数,将传入的UBlackboardData作为初始内容

注意:这里抛出了一个OnUsingBlackBoard的BlueprintImplementableEvent类型函数,该函数可以通过蓝图重载去监听黑板被设置的事件

UBehaviorTreeComponent

• UBehaviorTreeComponent::StartTree(UBehaviorTree& Asset, EBTExecutionMode::Type ExecuteMode)

void UBehaviorTreeComponent::StartTree(UBehaviorTree& Asset, EBTExecutionMode::Type ExecuteMode /*= EBTExecutionMode::Looped*/)
{
        // clear instance stack, start should always run new tree from root
        UBehaviorTree* CurrentRoot = GetRootTree();
        
        if (CurrentRoot == &Asset && TreeHasBeenStarted())
        {
                UE_VLOG(GetOwner(), LogBehaviorTree, Log, TEXT("Skipping behavior start request - it's already running"));
                return;
        }
        else if (CurrentRoot)
        {
                UE_VLOG(GetOwner(), LogBehaviorTree, Log, TEXT("Abandoning behavior %s to start new one (%s)"),
                        *GetNameSafe(CurrentRoot), *Asset.GetName());
        }

        StopTree(EBTStopMode::Safe);

        TreeStartInfo.Asset = &Asset;
        TreeStartInfo.ExecuteMode = ExecuteMode;
        TreeStartInfo.bPendingInitialize = true;

        ProcessPendingInitialize();
}
  1. 判断当前准备Start的树就是当前正在运行的树,不做处理直接return
  2. 如果当前有正在运行的树,且与当前准备设置的树不同,则发出打印信息
  3. 设置TreeStartInfo属性(UBehaviorTree*、EBTExecutionMode::Type、uint8)
  4. 执行ProcessPendingInitialize方法

• UBehaviorTreeComponent::ProcessPendingInitialize()

void UBehaviorTreeComponent::ProcessPendingInitialize()
{
        StopTree(EBTStopMode::Safe);
        if (bWaitingForLatentAborts)
        {
                return;
        }

        // finish cleanup
        RemoveAllInstances();

        bLoopExecution = (TreeStartInfo.ExecuteMode == EBTExecutionMode::Looped);
        bIsRunning = true;

#if USE_BEHAVIORTREE_DEBUGGER
        DebuggerSteps.Reset();
#endif

        UBehaviorTreeManager* BTManager = UBehaviorTreeManager::GetCurrent(GetWorld());
        if (BTManager)
        {
                BTManager->AddActiveComponent(*this);
        }

        // push new instance
        const bool bPushed = PushInstance(*TreeStartInfo.Asset);
        TreeStartInfo.bPendingInitialize = false;
}
  1. 首先还是StopTree(EBTStopMode::Safe)停止当前行为树
  2. 然后就是清除所有实例RemoveAllInstances()
  3. 将自己注册到UBehaviorTreeManager* BTManager中BTManager->AddActiveComponent(*this);
  4. 添加BehaviorTree的InstanceUBehaviorTreeComponent::PushInstance

• UBehaviorTreeComponent::PushInstance(UBehaviorTree& TreeAsset)

bool UBehaviorTreeComponent::PushInstance(UBehaviorTree& TreeAsset)
{
        // check if blackboard class match
        if (TreeAsset.BlackboardAsset && BlackboardComp && !BlackboardComp->IsCompatibleWith(TreeAsset.BlackboardAsset))
        {
                UE_VLOG(GetOwner(), LogBehaviorTree, Warning, TEXT("Failed to execute tree %s: blackboard %s is not compatibile with current: %s!"),
                        *TreeAsset.GetName(), *GetNameSafe(TreeAsset.BlackboardAsset), *GetNameSafe(BlackboardComp->GetBlackboardAsset()));

                return false;
        }

        UBehaviorTreeManager* BTManager = UBehaviorTreeManager::GetCurrent(GetWorld());
        if (BTManager == NULL)
        {
                UE_VLOG(GetOwner(), LogBehaviorTree, Warning, TEXT("Failed to execute tree %s: behavior tree manager not found!"), *TreeAsset.GetName());
                return false;
        }

        // check if parent node allows it
        const UBTNode* ActiveNode = GetActiveNode();
        const UBTCompositeNode* ActiveParent = ActiveNode ? ActiveNode->GetParentNode() : NULL;
        if (ActiveParent)
        {
                uint8* ParentMemory = GetNodeMemory((UBTNode*)ActiveParent, InstanceStack.Num() - 1);
                int32 ChildIdx = ActiveNode ? ActiveParent->GetChildIndex(*ActiveNode) : INDEX_NONE;

                const bool bIsAllowed = ActiveParent->CanPushSubtree(*this, ParentMemory, ChildIdx);
                if (!bIsAllowed)
                {
                        UE_VLOG(GetOwner(), LogBehaviorTree, Warning, TEXT("Failed to execute tree %s: parent of active node does not allow it! (%s)"),
                                *TreeAsset.GetName(), *UBehaviorTreeTypes::DescribeNodeHelper(ActiveParent));
                        return false;
                }
        }

        UBTCompositeNode* RootNode = NULL;
        uint16 InstanceMemorySize = 0;

        const bool bLoaded = BTManager->LoadTree(TreeAsset, RootNode, InstanceMemorySize);
        if (bLoaded)
        {
                FBehaviorTreeInstance NewInstance;
                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);

                InstanceStack.Push(NewInstance);
                ActiveInstanceIdx = InstanceStack.Num() - 1;

                // start root level services now (they won't be removed on looping tree anyway)
                for (int32 ServiceIndex = 0; ServiceIndex < RootNode->Services.Num(); ServiceIndex++)
                {
                        UBTService* ServiceNode = RootNode->Services[ServiceIndex];
                        uint8* NodeMemory = (uint8*)ServiceNode->GetNodeMemory<uint8>(InstanceStack[ActiveInstanceIdx]);

                        // send initial on search start events in case someone is using them for init logic
                        ServiceNode->NotifyParentActivation(SearchData);

                        InstanceStack[ActiveInstanceIdx].AddToActiveAuxNodes(ServiceNode);
                        ServiceNode->WrappedOnBecomeRelevant(*this, NodeMemory);
                }

                FBehaviorTreeDelegates::OnTreeStarted.Broadcast(*this, TreeAsset);

                // start new task
                RequestExecution(RootNode, ActiveInstanceIdx, RootNode, 0, EBTNodeResult::InProgress);
                return true;
        }

        return false;
}
  1. 前面看不懂,ActiveParent的作用是啥
  2. BTManager->LoadTree(TreeAsset, RootNode, InstanceMemorySize)加载行为树

TickComponent

从上述代码中可以发现:BrainComponen是一个指向UBehaviorTreeComponent的智能指针,所以行为树的真正执行只用查看UBehaviorTreeComponent::TickComponent的代码即可

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值