一. RequestExecution()

1. 确定了

bSwitchToHigherPriority = (ContinueWithResult == EBTNodeResult::Aborted);

bAlreadyHasRequest = (ExecutionRequest.ExecuteNode != NULL);

2.分两种情况确定SearchStart和SearchEnd与ExecuteNode。

(1)

if (bSwitchToHigherPriority && RequestedByChildIndex >= 0)

的时候:

ExecuteNode是RequestBy所在节点的父节点,(因为RequestBy在这种情况是UBTDecorator),也就是与RequestOn指向一样。

SearchStart是RequestOn下面子节点索引是RequestedByChildIndex的子节点的FirstNode(因为在一个FBTCompositeChild上,可能会有多个装饰器,Task或者UBTCompositeNode,Task或者UBTCompositeNode上可能还会有多服务)。所以SearchStart代表了这个FBTCompositeChild上的第一个节点,也就是这一分支的根下第一个节点。

SearchEnd是RequestOn下面子节点索引是 RequestedByChildIndex+1 的子节点的FirstNode,也就是这个SearchStart到SearchEnd的范围就是RequestedByChildIndex这一条分支。

(2)反之:

ExecuteNode是RequestBy所在节点的父节点。

SearchStart是RequestBy所在节点。

SearchEnd是无限大。

3.  根据

if (bAlreadyHasRequest && ExecutionRequest.SearchStart.TakesPriorityOver(ExecutionIdx))

决定要不要合并请求。bAlreadyHasRequest 表示现在还存在一份执行请求未处理,并且如果未处理的那份请求SearchStart节点优先级高于现在这个请求的SearchStart节点。说明需要合并一下,合并就是把现在这个请求得到的SearchEnd节点赋给了已有的ExecutionRequest中的SearchEnd节点,然后return出去,不再接着往下执行。

4. 接着将ExecutionRequest中其他的属性进行赋值,

ExecutionRequest.ContinueWithResult = ContinueWithResult;
ExecutionRequest.bTryNextChild = !bSwitchToHigherPriority;
ExecutionRequest.bIsRestart = (RequestedBy != GetActiveNode());
PendingExecution.Lock();

这里PendingExecution.Lock();代表了Request不能被执行。

Unlock的地方在ProcessExecutionRequest中找到NextTask之后,执行ProcessPendingExecution之前。

if (!SearchData.bPostponeSearch)
	{
		// clear request accumulator
		ExecutionRequest = FBTNodeExecutionInfo();

		// unlock execution data, can get locked again if AbortCurrentTask starts any new requests
		PendingExecution.Unlock();

		if (bIsSearchValid)
		{
			// abort task if needed
			if (InstanceStack.Last().ActiveNodeType == EBTActiveNode::ActiveTask)
			{
				// prevent new execution requests for nodes inside the deactivated branch 
				// that may result from the aborted task.
				SearchData.bFilterOutRequestFromDeactivatedBranch = true;
				AbortCurrentTask();

				SearchData.bFilterOutRequestFromDeactivatedBranch = false;
			}

			// set next task to execute only when not lock for execution as everything has been cancelled/rollback
			if (!PendingExecution.IsLocked())
			{
				PendingExecution.NextTask = NextTask;
				PendingExecution.bOutOfNodes = (NextTask == NULL);
			}
		}

		ProcessPendingExecution();
	}
	else
	{
		// more important execution request was found
		// stop everything and search again in next tick
		ScheduleExecutionUpdate();
	}

5.  查看当前有没有正在进的行行为树的任务搜索

	if (SearchData.bSearchInProgress)
	{
		UE_VLOG(GetOwner(), LogBehaviorTree, Log, TEXT("> aborting current task search!"));
		SearchData.bPostponeSearch = true;
	}

如果当前正在进行行为树的任务搜索,把“延迟搜索”标志设为 true,表示当前正在进行的行为树的任务搜索会被中断,然后在因为RequestExecution最后会调用

if (bScheduleNewSearch)
	{
		ScheduleExecutionUpdate();
	}

所以本次请求会在下一帧开始处理。

6.  判断一下本次要开始的搜索是不是一个无效的

const bool bIsActiveNodeAborting = InstanceStack.Num() && InstanceStack.Last().ActiveNodeType == EBTActiveNode::AbortingTask;
const bool bInvalidateCurrentSearch = bWaitingForLatentAborts || bIsActiveNodeAborting;
const bool bScheduleNewSearch = !bWaitingForLatentAborts;

if (bInvalidateCurrentSearch)
{
    if (ExecutionRequest.SearchEnd.IsSet())
	{
		UE_VLOG(GetOwner(), LogBehaviorTree, Log, TEXT("> removing limit from end of search range because of request during task abortion!"));
		ExecutionRequest.SearchEnd = FBTNodeIndex();
	}
	RollbackSearchChanges();
}

可以看出判断bInvalidateCurrentSearch需要看两个条件,bWaitingForLatentAborts 或者bIsActiveNodeAborting。

(1)bWaitingForLatentAborts有两种情况会被设置为True。

会在StopTree()中,如果有异步在进行的Task,例如如果当前行为树中有UBTComposite_SimpleParallel节点,并且正在激活运行中,因为它下面的子节点是异步执行的,所以如果它下面有Task的EBTNodeResult是InProgress,就要Abort它,然后将bWaitingForLatentAborts设置为True。

----------------------------------------------------------------------------------------------------------------------------------

会在ApplySearchUpdates()中,如果SearchUpdate中记录了EBTNodeUpdateMode为Remove的Task类型节点,如果这个Task节点的EBTNodeResult是InProgress,将bWaitingForLatentAborts设置为True。

(2)bIsActiveNodeAborting首先看它的赋值

const bool bIsActiveNodeAborting = InstanceStack.Num() && InstanceStack.Last().ActiveNodeType == EBTActiveNode::AbortingTask;

说明当前行为树的活跃Task节点的EBTActiveNode为AbortingTask,bIsActiveNodeAborting即为True。

活跃Task节点被设置为AbortingTask的情况也有两种。

会在StopTree()中,当活跃Task节点的EBTActiveNode为ActiveTask时会将它的EBTActiveNode改为AbortingTask。

----------------------------------------------------------------------------------------------------------------------------------

会在AbortCurrentTask()中。

void UBehaviorTreeComponent::AbortCurrentTask()
{
	const int32 CurrentInstanceIdx = InstanceStack.Num() - 1;
	FBehaviorTreeInstance& CurrentInstance = InstanceStack[CurrentInstanceIdx];
	CurrentInstance.ActiveNodeType = EBTActiveNode::AbortingTask;

	UBTTaskNode* CurrentTask = (UBTTaskNode*)CurrentInstance.ActiveNode;

	// remove all observers before requesting abort
	UnregisterMessageObserversFrom(CurrentTask);

	// protect memory of this task from rollbacks
	// at this point, invalid search rollback already happened
	// only reason to do the rollback is restoring tree state during abort for accumulated requests
	// but this task needs to remain unchanged: it's still aborting and internal memory can be modified on AbortTask call
	SearchData.bPreserveActiveNodeMemoryOnRollback = true;

	UE_VLOG(GetOwner(), LogBehaviorTree, Log, TEXT("Abort task: %s"), *UBehaviorTreeTypes::DescribeNodeHelper(CurrentTask));

	// abort task using current state of tree
	uint8* NodeMemory = (uint8*)(CurrentTask->GetNodeMemory<uint8>(CurrentInstance));
	EBTNodeResult::Type TaskResult = CurrentTask->WrappedAbortTask(*this, NodeMemory);

	// pass task finished if wasn't already notified (FinishLatentAbort)
	if (CurrentInstance.ActiveNodeType == EBTActiveNode::AbortingTask &&
		CurrentInstanceIdx == (InstanceStack.Num() - 1))
	{
		OnTaskFinished(CurrentTask, TaskResult);
	}
}

而AbortCurrentTask会在ProcessExecutionRequest()中调用

if (!SearchData.bPostponeSearch)
	{
		// clear request accumulator
		ExecutionRequest = FBTNodeExecutionInfo();

		// unlock execution data, can get locked again if AbortCurrentTask starts any new requests
		PendingExecution.Unlock();

		if (bIsSearchValid)
		{
			// abort task if needed
			if (InstanceStack.Last().ActiveNodeType == EBTActiveNode::ActiveTask)
			{
				// prevent new execution requests for nodes inside the deactivated branch 
				// that may result from the aborted task.
				SearchData.bFilterOutRequestFromDeactivatedBranch = true;
				AbortCurrentTask();

				SearchData.bFilterOutRequestFromDeactivatedBranch = false;
			}

			// set next task to execute only when not lock for execution as everything has been cancelled/rollback
			if (!PendingExecution.IsLocked())
			{
				PendingExecution.NextTask = NextTask;
				PendingExecution.bOutOfNodes = (NextTask == NULL);
			}
		}

		ProcessPendingExecution();
	}

找到NextTask后,如果当前Task的ActiveNode是ActiveTask,那么就要打断当前当前活跃的Task。

注:1. ExecutionRequest的类型是FBTNodeExecutionInfo,结构是:

struct FBTNodeExecutionInfo
{
	/** index of first task allowed to be executed */
	FBTNodeIndex SearchStart;

	/** index of last task allowed to be executed */
	FBTNodeIndex SearchEnd;

	/** node to be executed */
	const UBTCompositeNode* ExecuteNode;

	/** subtree index */
	uint16 ExecuteInstanceIdx;

	/** result used for resuming execution */
	TEnumAsByte<EBTNodeResult::Type> ContinueWithResult;

	/** if set, tree will try to execute next child of composite instead of forcing branch containing SearchStart */
	uint8 bTryNextChild : 1;

	/** if set, request was not instigated by finishing task/initialization but is a restart (e.g. decorator) */
	uint8 bIsRestart : 1;

	FBTNodeExecutionInfo() : ExecuteNode(NULL), bTryNextChild(false), bIsRestart(false) { }
};

FBTNodeIndex的结构是

struct FBTNodeIndex
{
	static constexpr uint16 InvalidIndex = TNumericLimits<uint16>::Max(); // (This is also the same as INDEX_NONE assigned to IndexType!)

	/** index of instance of stack */
	uint16 InstanceIndex;

	/** execution index within instance */
	uint16 ExecutionIndex;

	FBTNodeIndex() : InstanceIndex(InvalidIndex), ExecutionIndex(InvalidIndex) {}
	FBTNodeIndex(uint16 InInstanceIndex, uint16 InExecutionIndex) : InstanceIndex(InInstanceIndex), ExecutionIndex(InExecutionIndex) {}

	bool TakesPriorityOver(const FBTNodeIndex& Other) const;
	bool IsSet() const { return InstanceIndex < InvalidIndex; }

	FORCEINLINE bool operator==(const FBTNodeIndex& Other) const { return Other.ExecutionIndex == ExecutionIndex && Other.InstanceIndex == InstanceIndex; }
	FORCEINLINE bool operator!=(const FBTNodeIndex& Other) const { return !operator==(Other); }
	FORCEINLINE friend uint32 GetTypeHash(const FBTNodeIndex& Other) { return Other.ExecutionIndex ^ Other.InstanceIndex; }

	FORCEINLINE FString Describe() const { return FString::Printf(TEXT("[%d:%d]"), InstanceIndex, ExecutionIndex); }
};

注:2.RequestExecution传进来的参数是:

void UBehaviorTreeComponent::RequestExecution(const UBTCompositeNode* RequestedOn, const int32 InstanceIdx, const UBTNode* RequestedBy,
											  const int32 RequestedByChildIndex, const EBTNodeResult::Type ContinueWithResult, const bool bStoreForDebugger)

传进来的RequestOn类型是UBTCompositeNode*,代表它只能是复合节点,也就是发起节点所在节点的父节点。而RequestBy的类型是UBTNode*,代表了它可以是任意类型的节点,就是Task节点或者装饰器节点。

注:3. AbortCurrentTask()方法虽然是打断Task的方法,但是方法最后调用的是OnTaskFinished()方法,只不过传的参数中类型是Abort。

注:4. 

USTRUCT()
struct FBTCompositeChild
{
	GENERATED_USTRUCT_BODY()

	/** child node */
	UPROPERTY()
	TObjectPtr<UBTCompositeNode> ChildComposite = nullptr;

	UPROPERTY()
	TObjectPtr<UBTTaskNode> ChildTask = nullptr;

	/** execution decorators */
	UPROPERTY()
	TArray<TObjectPtr<UBTDecorator>> Decorators;

	/** logic operations for decorators */
	UPROPERTY()
	TArray<FBTDecoratorLogic> DecoratorOps;
};

UCLASS(Abstract, MinimalAPI)
class UBTCompositeNode : public UBTNode 
{
	GENERATED_UCLASS_BODY()

	/** child nodes */
	UPROPERTY()
	TArray<FBTCompositeChild> Children;

	/** service nodes */
	UPROPERTY()
	TArray<TObjectPtr<UBTService>> Services;

    。。。。。。
}
namespace EBTActiveNode
{
	// keep in sync with DescribeActiveNode()
	enum Type
	{
		Composite,
		ActiveTask,
		AbortingTask,
		InactiveTask,
	};
}
namespace EBTTaskStatus
{
	// keep in sync with DescribeTaskStatus()
	enum Type
	{
		Active,
		Aborting,
		Inactive,
	};
}
namespace EBTNodeUpdateMode
{
	// keep in sync with DescribeNodeUpdateMode()
	enum Type
	{
		Unknown,
		Add,				// add node
		Remove,				// remove node
	};
}
UENUM()
enum class EBTChildIndex : uint8
{
	FirstNode,
	TaskNode,
};
namespace BTSpecialChild
{
	inline constexpr int32 NotInitialized = -1;	// special value for child indices: needs to be initialized
	inline constexpr int32 ReturnToParent = -2;	// special value for child indices: return to parent node
	
	inline constexpr uint8 OwnedByComposite = MAX_uint8;	// special value for aux node's child index: owned by composite node instead of a task
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐