mcpField
U
ue-state-trees

ue-state-trees

github.com/quodsoler开发4191.1k 次安装4 周前更新
代码仓库

关于 ue-state-trees

面向 UE 5.8 开发者的 State Tree 参考技能,涵盖运行时架构、节点编写和集成方式。按具体开发问题组织内容,并标注经过验证的 API 与旧版弃用形式。

主要功能

  1. 01State Tree 运行时架构说明
  2. 02任务、条件与评估器编写
  3. 03执行上下文与异步完成
  4. 04Schema 与外部数据配置
  5. 05组件、AI 与 Mass 集成

适用场景

  1. 01编写自定义 State Tree 任务
  2. 02排查实例数据与上下文问题
  3. 03设置节点限制和状态转换
  4. 04将 State Tree 接入 Actor 或 Mass 实体

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/quodsoler/unreal-engine-skills --skill ue-state-trees

安装后在 Claude Code 中输入/ue-state-trees确认已加载。

附带文件references/state-tree-mass-integration.mdreferences/state-tree-patterns.md

详细介绍

UE State Trees

目标引擎:UE 5.8。以下 API 已根据 5.8 头文件验证;旧形式列于“已弃用 — 请勿使用”。

State Tree 是一种数据驱动的分层状态机,以 UStateTree 数据资产的形式创建,并通过执行上下文从 C++ 执行。运行时类型位于 StateTreeModule(插件 StateTree);面向 Actor 的组件和架构位于 GameplayStateTreeModule(插件 GameplayStateTree)。Mass 实体行为还需要 MassAIBehavior(插件 MassAI,在 5.8 中为实验性功能),以及 MassEntity、MassCore 和 MassSignals。请将所用模块添加到 .Build.cs 的 PublicDependencyModuleNames 中。

上下文

如果 .agents/ue-project-context.md 存在,请阅读它(模块名称、惯例、已启用的插件、GAS/网络设置)。如果该文件不存在,也不要停止。

根据请求和代码库确定相关领域。只有在两种合理的理解会导致不同代码时才提问。

请求涉及……请参阅
运行时的结构、实例数据、上下文架构、执行上下文
编写任务任务
编写条件或控制转换条件
实用性评分、“选择最佳子状态”考量项
将世界数据馈入状态树评估器、外部数据
状态何时以及如何变化转换、状态类型与选择
由标签驱动、发送到状态树的信号事件
响应 C++ 回调而不是轮询委托
限制资产可使用的节点架构
在 Actor 或 AI 控制器上运行状态树组件与 AI 设置
降低 Tick 开销/让状态树休眠计划 Tick
数千个实体Mass 实体集成
从回调或其他线程完成工作异步完成

架构

UStateTree (UDataAsset)          IsReadyToRun() must be true before execution
  ├── UStateTreeSchema           which nodes and context data are allowed
  ├── States                     hierarchy of States/Groups/Linked/Subtree
  │     ├── Tasks                work performed while the state is active
  │     ├── EnterConditions      gate on selecting the state
  │     ├── Considerations       utility score used by selection behaviours
  │     └── Transitions          rules for leaving the state
  ├── Evaluators                 global data providers, tick before transitions
  └── Parameters                 FInstancedPropertyBag global parameters

节点是 USTRUCT,而不是 UObject,并且每个节点虚函数都是 const。每个实例可变的状态存储在独立的实例数据结构中,该结构由 FStateTreeInstanceData 拥有,并会跨帧持久保留。执行上下文是在该实例数据上构造的短生命周期视图。

类型作用
UStateTree编译后的资产。IsReadyToRun() 用于报告链接是否成功
FStateTreeInstanceData持久运行时存储——持有它,而不是上下文
FStateTreeExecutionContext完整的读写上下文;每次 Tick 时构造
FStateTreeReferenceUStateTree* 加上已覆盖的全局参数
EStateTreeRunStatusRunning、Stopped、Succeeded、Failed、Unset
// MyTreeRunner.h — persistent storage on the owner
UPROPERTY(Transient)
FStateTreeInstanceData InstanceData;

// .cpp — one context per tick over the same instance data
FStateTreeExecutionContext Context(*this, *StateTreeAsset, InstanceData);
Context.SetCollectExternalDataCallback(
    FOnCollectStateTreeExternalData::CreateUObject(this, &UMyTreeRunner::CollectExternalData));

FStateTreeExecutionContext::FStartParameters StartParams;
StartParams.RandomSeed = 1234;
Context.Start(StartParams);
// later frames
Context.Tick(DeltaTime);

当转换必须在其他系统运行后再评估时,可以拆分 Tick:先调用 TickUpdateTasks(DeltaTime),之后再调用 TickTriggerTransitions()。

执行上下文

5.8 按能力划分上下文。选择能够编译的最精简上下文。

上下文提供的功能典型用途
FStateTreeReadOnlyExecutionContextGetOwner、GetWorld、GetStateTree、HasEventToProcess、IsValid重写 GetDebugInfo,游戏玩法调试器
FStateTreeMinimalExecutionContext增加 SendEvent、ScheduleNextTick、计划 Tick 请求从 Tick 外部发送事件
FStateTreeExecutionContext增加 Start/Stop/Tick、实例数据、外部数据、FinishTask、BindDelegate节点内的所有操作
FStateTreeWeakExecutionContext捕获可供后续使用的弱句柄lambda、潜在操作、定时器
FStateTreeStrongExecutionContext从弱上下文解析访问权限在回调中,通过 MakeStrongExecutionContext()

FStateTreeMinimalExecutionContext 派生自 FStateTreeReadOnlyExecutionContext,而 FStateTreeExecutionContext 派生自 FStateTreeMinimalExecutionContext,因此接收完整上下文的节点方法可以调用其上列出的所有功能。

异步完成

捕获 FStateTreeWeakExecutionContext(由有效上下文构造),并在异步工作返回时完成任务。完整的潜在任务模板请参阅 state-tree-patterns.md。

const FStateTreeWeakExecutionContext WeakContext = Context.MakeWeakExecutionContext();
OnRequestFinished.AddLambda([WeakContext]()
{
    WeakContext.FinishTask(EStateTreeFinishTaskType::Succeeded);
});

在 Tick 中,任务可以通过 Context.FinishTask(*this, EStateTreeFinishTaskType::Succeeded) 自行完成,也可以从 Tick 返回完成状态。

Schema

Schema 声明资产可以使用哪些节点结构体、类和上下文数据。IsStructAllowed 决定节点是否会显示在编辑器中,因此自定义节点应派生自标准 Schema 接受的 *CommonBase 结构体:FStateTreeTaskCommonBase、FStateTreeConditionCommonBase、FStateTreeEvaluatorCommonBase、FStateTreeConsiderationCommonBase、FStateTreePropertyFunctionCommonBase。

Schema发布的上下文数据
UStateTreeComponentSchemaActor(来自 ContextActorClass 的类)
UStateTreeAIComponentSchemaActor(默认是 APawn),以及 AIController
UMassStateTreeSchemaMass 实体数据,通过 FMassStateTreeExecutionContext
// MyGameStateTreeSchema.h
#pragma once
#include "StateTreeSchema.h"
#include "StateTreeExecutionTypes.h"
#include "MyGameStateTreeSchema.generated.h"

UCLASS(BlueprintType, EditInlineNew, CollapseCategories, meta = (DisplayName = "My Game Schema"))
class MYGAME_API UMyGameStateTreeSchema : public UStateTreeSchema
{
    GENERATED_BODY()

protected:
    virtual bool IsStructAllowed(const UScriptStruct* InScriptStruct) const override;
    virtual bool IsClassAllowed(const UClass* InScriptStruct) const override;
    virtual bool IsExternalItemAllowed(const UStruct& InStruct) const override;
    virtual TConstArrayView<FStateTreeExternalDataDesc> GetContextDataDescs() const override;

#if WITH_EDITOR
    virtual bool AllowEvaluators() const override { return true; }
    virtual bool AllowMultipleTasks() const override { return true; }
    virtual bool AllowGlobalParameters() const override { return true; }
    virtual bool AllowUtilityConsiderations() const override { return true; }
#endif

    UPROPERTY()
    TArray<FStateTreeExternalDataDesc> ContextDataDescs;
};

AllowEnterConditions、AllowUtilityConsiderations、AllowEvaluators、AllowMultipleTasks、AllowGlobalParameters、AllowTasksCompletion 和 AllowQueuedCompilation 在 #if WITH_EDITOR 内声明(StateTreeSchema.h:96-138)——它们仅作用于编辑器和编译器,因此重写这些函数时也必须使用相同的条件保护,否则游戏构建会报错 “does not override”。IsStructAllowed、IsClassAllowed、IsExternalItemAllowed、IsScheduledTickAllowed、IsStateSelectionAllowed、IsStateTypeAllowed 和 GetContextDataDescs 是运行时虚函数(StateTreeSchema.h:36-79)。

任务

每个任务都必须重写所需的虚函数,并且重写 GetInstanceDataType()。仅有 using FInstanceDataType = …; 别名不会分配任何内容;FStateTreeNodeBase::GetInstanceDataType() 默认返回 nullptr(StateTreeNodeBase.h:94),编译器也不会预留存储空间,因此 Context.GetInstanceData(*this) 会触发 check(Memory != nullptr)(PropertyBindingDataView.h:116)。

虚函数(原样签名)返回值
EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) constEStateTreeRunStatus
ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) constvoid
StateCompleted(FStateTreeExecutionContext& Context, const EStateTreeRunStatus CompletionStatus, const FStateTreeActiveStates& CompletedActiveStates) constvoid
Tick(FStateTreeExecutionContext& Context, const float DeltaTime) constEStateTreeRunStatus
TriggerTransitions(FStateTreeExecutionContext& Context) constvoid
GetDebugInfo(const FStateTreeReadOnlyExecutionContext& Context) constFString
// MyTimedTask.h
#pragma once
#include "StateTreeTaskBase.h"
#include "StateTreeExecutionContext.h"
#include "MyTimedTask.generated.h"

USTRUCT()
struct FMyTimedTaskInstanceData
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, Category = "Parameter")
    float Duration = 2.0f;

    UPROPERTY()
    float ElapsedTime = 0.f;
};

USTRUCT(meta = (DisplayName = "My Timed Task"))
struct FMyTimedTask : public FStateTreeTaskCommonBase
{
    GENERATED_BODY()

    using FInstanceDataType = FMyTimedTaskInstanceData;

    virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }

    virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context,
        const FStateTreeTransitionResult& Transition) const override
    {
        FInstanceDataType& Data = Context.GetInstanceData(*this);
        Data.ElapsedTime = 0.f;
        return EStateTreeRunStatus::Running;
    }

    virtual EStateTreeRunStatus Tick(FStateTreeExecutionContext& Context,
        const float DeltaTime) const override
    {
        FInstanceDataType& Data = Context.GetInstanceData(*this);
        Data.ElapsedTime += DeltaTime;
        return Data.ElapsedTime >= Data.Duration
            ? EStateTreeRunStatus::Succeeded
            : EStateTreeRunStatus::Running;
    }
};

可编辑参数通常保存在实例数据中,以便在编辑器中绑定;直接放在节点结构体上的属性会由所有实例共享,且无法绑定。

行为标志

在任务构造函数中设置这些标志。

标志默认值效果
bShouldStateChangeOnReselecttrue再次选中同一状态时,重新运行 Exit/Enter
bShouldCallTicktrue调用 Tick();设为 false 也会禁用属性复制
bShouldCallTickOnlyOnEventsfalse仅在有事件的帧调用 Tick(要求 bShouldCallTick 为 false)
bShouldCopyBoundPropertiesOnTicktrue在 Tick() 之前刷新绑定属性
bShouldCopyBoundPropertiesOnExitStatetrue在 ExitState() 之前刷新绑定属性
bShouldAffectTransitionsfalse在处理转换期间调用 TriggerTransitions()
bConsideredForSchedulingtrue计算计划 Tick 速率时包含此任务
bTaskEnabledtrue资产中未禁用此节点

TransitionHandlingPriority(EStateTreeTransitionPriority)用于排列同一状态中各任务的 TriggerTransitions 调用顺序。

条件

FStateTreeConditionBase::TestCondition(FStateTreeExecutionContext& Context) const 返回 bool,默认值为 false。它必须是纯函数:选择阶段可能会在每帧调用它多次。

成员用途
EStateTreeExpressionOperand OperandCopy、And、Or、Multiply(Multiply 仅用于考量条件)
int8 DeltaIndent括号层级,可在不嵌套的情况下表示 (A AND B) OR (C AND D)
EStateTreeConditionEvaluationMode EvaluationModeEvaluated、ForcedTrue、ForcedFalse

条件也会接收 EnterState、ExitState 和 StateCompleted(默认均为空操作),以便在状态的生命周期内缓存工作。

内置条件:FStateTreeCompareIntCondition、FStateTreeCompareFloatCondition、FStateTreeCompareBoolCondition、FStateTreeCompareEnumCondition、FStateTreeCompareNameCondition、FStateTreeCompareDistanceCondition、FStateTreeRandomCondition(Conditions/StateTreeCommonConditions.h)、FStateTreeObjectIsValidCondition(Conditions/StateTreeObjectConditions.h),以及 Conditions/StateTreeGameplayTagConditions.h 中的游戏玩法标签条件系列。比较条件使用 UE::StateTree::EComparisonOperator。

注意事项

注意事项为状态评分,以便父级在子级之间进行选择。该 API 在头文件中标记为实验性,因此自定义注意事项应尽量精简。

USTRUCT(meta = (DisplayName = "My Threat Score"))
struct FMyThreatConsideration : public FStateTreeConsiderationCommonBase
{
    GENERATED_BODY()

    using FInstanceDataType = FMyThreatConsiderationInstanceData;

    virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }

protected:
    virtual float GetScore(FStateTreeExecutionContext& Context) const override
    {
        const FInstanceDataType& Data = Context.GetInstanceData(*this);
        return FMath::Clamp(Data.ThreatLevel / 100.f, 0.f, 1.f);
    }
};

GetScore 是 protected;框架会调用公开的 GetNormalizedScore。Operand 和 DeltaIndent 用于组合同级注意事项(And = 最小值,Or = 最大值,Multiply = 乘积)。分数仅对两种效用选择行为有影响,并且架构必须从 AllowUtilityConsiderations() 返回 true。

评估器

评估器是全局的(不属于某个状态),并且会在转换和任务 Tick 之前执行。对于许多节点都会读取的数据,请使用评估器;对于稳定引用,请使用外部数据。

virtual void TreeStart(FStateTreeExecutionContext& Context) const;
virtual void TreeStop(FStateTreeExecutionContext& Context) const;
virtual void Tick(FStateTreeExecutionContext& Context, const float DeltaTime) const;
virtual FString GetDebugInfo(const FStateTreeReadOnlyExecutionContext& Context) const;

在状态预选期间执行评估器时,DeltaTime 为 0。评估器需要像任务一样实现 GetInstanceDataType() — 完整模板见 state-tree-patterns.md。

转换

EStateTreeTransitionTrigger 是位掩码(ENUM_CLASS_FLAGS)。

触发器值触发时机
OnStateSucceeded0x1状态以 Succeeded 完成
OnStateFailed0x2状态以 Failed 完成
OnStateCompleted0x1|0x2上述任一情况
OnTick0x4每个 Tick — 始终使用条件进行限制
OnEvent0x8排队事件与标签匹配
OnDelegate0x10已绑定的委托被广播

EStateTreeTransitionPriority:Low、Normal、Medium、High、Critical。优先级最高的已触发转换中,第一个转换生效。转换类型(EStateTreeTransitionType):None、Succeeded、Failed、GotoState、Parent、NextState、NextSelectableState、NextParent、NextSelectableParent(StateTreeTypes.h:76)。每个转换的标志 bTransitionEnabled 和 bConsumeEventOnSelect 默认均为 true。

在 C++ 中,使用 Context.RequestTransition(TargetState, Priority, Fallback) 请求转换,其中 Fallback 是一个 EStateTreeSelectionFallback。

委托

当外部系统能够准确告知树何时作出响应时,委托可以替代轮询。一个节点在其实例数据上发布 FStateTreeDelegateDispatcher,另一个节点发布 FStateTreeDelegateListener,然后由编辑器将两者连接起来。

// On the listening node's instance data
UPROPERTY(EditAnywhere, Category = "Input")
FStateTreeDelegateListener ArrivedListener;

// EnterState — bind
Context.BindDelegate(Data.ArrivedListener, FSimpleDelegate::CreateLambda([]()
{
    UE_LOG(LogMyGame, Verbose, TEXT("Arrived"));
}));

// ExitState — always unbind
Context.UnbindDelegate(Data.ArrivedListener);

// On the dispatching node
Context.BroadcastDelegate(Data.ArrivedDispatcher);

当绑定的分发器广播时,带有 OnDelegate 触发器的转换会触发,并与其余转换一同进行评估。

状态类型与选择

EStateTreeStateType:State、Group、Linked、LinkedAsset、Subtree。LinkedAsset 状态是共享机制 — 可通过 FStateTreeReferenceOverrides 对每个实例进行覆盖。

EStateTreeStateSelectionBehavior:

行为效果
None无法直接选择该状态
TryEnterState即使该状态有子级,也进入该状态
TrySelectChildrenInOrder选择第一个通过进入条件的子级
TrySelectChildrenAtRandom打乱子级顺序,选择第一个通过条件的子级
TrySelectChildrenWithHighestUtility选择注意事项评分最高的子级,平分时按顺序决定
TrySelectChildrenAtRandomWeightedByUtility按归一化评分进行随机加权选择
TryFollowTransitions评估该状态的转换,而不是进入该状态

FStateTreeActiveStates::MaxStates = 8 限制了活动状态路径的深度。对于更深的设计,请使用子树或链接资产进行扁平化。

事件

事件是带有可选载荷的游戏玩法标签消息:FStateTreeEvent { FGameplayTag Tag; FInstancedStruct Payload; FName Origin; }。

// From outside the tree
StateTreeComp->SendStateTreeEvent(MyTag_AiAlert, FConstStructView::Make(AlertData), TEXT("Perception"));

// From inside a node (available on the minimal context and above)
Context.SendEvent(MyTag_AiAlert, FConstStructView::Make(ResultData), TEXT("MyTask"));

每个实例的 FStateTreeEventQueue::MaxActiveEvents = 64。使用 Context.ForEachEvent(Lambda) 进行迭代,其 Lambda 返回 EStateTreeLoopEvents(Next、Break、Consume — StateTreeEvents.h:35);使用 Context.ConsumeEvent(SharedEvent) 移除单个事件(返回 void)。Context.HasEventToProcess(Tag) 是一种开销很低的存在性检查。有关游戏玩法标签的声明位置,请参阅 ue-gameplay-tags-messaging。

外部数据

外部数据为节点提供对架构所有者提供的对象或结构体的类型化引用,例如子系统、所有者 Actor 和组件。

TStateTreeExternalDataHandle<UMyWorldSubsystem> SubsystemHandle;
TStateTreeExternalDataHandle<AActor, EStateTreeExternalDataRequirement::Optional> ActorHandle;

virtual bool Link(FStateTreeLinker& Linker) override
{
    Linker.LinkExternalData(SubsystemHandle);
    Linker.LinkExternalData(ActorHandle);
    return true;
}

// Required handles return a reference, Optional handles a pointer
UMyWorldSubsystem& Subsystem = Context.GetExternalData(SubsystemHandle);
AActor* Actor = Context.GetExternalDataPtr(ActorHandle);

Link 返回 bool(基类中标记为 [[nodiscard]],见 StateTreeNodeBase.h:116);链接成功时返回 true,失败时返回 false。

UStateTreeComponentSchema::IsExternalItemAllowed 仅接受 AActor、UActorComponent 和 UWorldSubsystem 的子类。所有者 Actor 会单独作为名为 Actor 的上下文数据发布(AI 架构还会发布 AIController);访问它的惯用方式是使用由编辑器自动绑定的实例数据属性:

UPROPERTY(EditAnywhere, Category = "Context")
TObjectPtr<AActor> Actor = nullptr;

引擎中没有 FStateTreeActorContext 类型——请使用上述方式。

组件和 AI 设置

UStateTreeComponent : UBrainComponent, IGameplayTaskOwnerInterface, IStateTreeSchemaProvider 在 Actor 上运行一棵树。UStateTreeAIComponent 继承自该组件,并返回 UStateTreeAIComponentSchema,后者保证提供一个 AAIController 上下文值——请在 AI 控制器上使用它。

// MyAIController.h — inside the AMyAIController class body
UPROPERTY(VisibleAnywhere, Category = "AI")
TObjectPtr<UStateTreeAIComponent> StateTreeComp;

// MyAIController.cpp
AMyAIController::AMyAIController()
{
    StateTreeComp = CreateDefaultSubobject<UStateTreeAIComponent>(TEXT("StateTree"));
    StateTreeComp->SetStartLogicAutomatically(true);
}

bStartLogicAutomatically 是 protected;请通过 SetStartLogicAutomatically(const bool) 写入它。公开接口包括:SetStateTree、SetStateTreeReference、AddLinkedStateTreeOverrides(FGameplayTag, FStateTreeReference)、RemoveLinkedStateTreeOverrides(FGameplayTag)、SendStateTreeEvent、GetStateTreeRunStatus,以及 BlueprintAssignable 属性 OnStateTreeRunStatusChanged。StartLogic、StopLogic、PauseLogic 和 ResumeLogic 来自 UBrainComponent。

创建子类并重写受保护的方法 SetContextRequirements(FStateTreeExecutionContext& Context, bool bLogErrors) / CollectExternalData(const FStateTreeExecutionContext& Context, const UStateTree* StateTree, TArrayView<const FStateTreeExternalDataDesc> Descs, TArrayView<FStateTreeDataView> OutDataViews) const,以发布额外的上下文或外部数据。

FStateTreeReference
FStateTreeReference TreeRef;
TreeRef.SetStateTree(MyStateTreeAsset);          // also calls SyncParameters()
TreeRef.GetMutableParameters().SetValueFloat(TEXT("AggroRange"), 1500.f);
StateTreeComp->SetStateTreeReference(TreeRef);

GetParameters() 返回 const FInstancedPropertyBag&——要修改参数,需使用 GetMutableParameters()。

调度 Tick

如果一棵树的所有活动任务都选择不进行 Tick,它可以休眠,而不是每帧运行。FStateTreeScheduledTick::MakeSleep()、MakeNextFrame()、MakeEveryFrames() 和 MakeCustomTickRate(DeltaTime) 用于描述所需的运行频率;FStateTreeMinimalExecutionContext::AddScheduledTickRequest / UpdateScheduledTickRequest / RemoveScheduledTickRequest 用于管理请求,而 ScheduleNextTick() 会唤醒这棵树。UStateTreeComponent 通过 FStateTreeComponentExecutionExtension 实现此功能,并受 UStateTreeComponentSchema::IsScheduledTickAllowed() 和控制台变量 StateTree.Component.DefaultScheduledTickAllowed 控制。若某个任务不应计入调度,请设置 bConsideredForScheduling = false。

要在自己的运行器上提供实例描述和自定义 Tick 调度,请实现 FStateTreeExecutionExtension(GetInstanceDescription、ScheduleNextTick、OnLinkedStateTreeOverridesSet、OnBeginApplyTransition),并将其传入 FStartParameters::ExecutionExtension。

Mass Entity 集成

Mass 行为使用 UMassStateTreeSchema 以及 FMassStateTreeTaskBase、FMassStateTreeConditionBase 和 FMassStateTreeEvaluatorBase 节点类型。这些类型添加了 GetDependencies(UE::MassBehavior::FStateTreeDependencyBuilder&) const,使动态创建的 UMassStateTreeProcessor(一个 UMassSignalProcessorBase)能够构建正确的片段查询。完整的设置、片段表、信号名称和处理器流程请参阅 state-tree-mass-integration.md。关于 Mass 架构本身,请参阅 ue-mass-entity。

已弃用 — 请勿使用

不要使用在 5.8 中使用来源
Start(const FInstancedPropertyBag*, int32 RandomSeed)Start(FStartParameters)StateTreeExecutionContext.h:492 中的 UE_DEPRECATED(5.8)
AppendDebugInfoString(FString&, const FStateTreeExecutionContext&)GetDebugInfo(const FStateTreeReadOnlyExecutionContext&)StateTreeEvaluatorBase.h:44 中的 UE_DEPRECATED(5.8)
比较条件中的 EGenericAICheck 运算符UE::StateTree::EComparisonOperatorConditions/StateTreeCommonConditions.h:47 中的 UE_DEPRECATED(5.8)
STATETREE_POD_INSTANCEDATA(Type)UE_STATETREE_ZEROED_TRIVIALLY_COPIED_NO_DESTRUCTOR_INSTANCEDATA(Type)(或 _CONSTRUCTED_ 变体)StateTreeTypes.h:1388 中的 UE_DEPRECATED_MACRO(5.8)
FStateTreePropertyRefExternalHandle通过执行上下文读取的 FStateTreePropertyRefStateTreePropertyRef.h:295 中的 UE_DEPRECATED(5.8)
AddDelegateListener(Listener, Delegate)BindDelegate(Listener, Delegate)StateTreeExecutionContext.h:530 中的 UE_DEPRECATED(5.6)
RemoveDelegateListener(Listener)UnbindDelegate(Listener)StateTreeExecutionContext.h:541 中的 UE_DEPRECATED(5.6)
FinishTask(const UE::StateTree::FFinishedTask&, EStateTreeFinishTaskType)FinishTask(const FStateTreeTaskBase&, EStateTreeFinishTaskType)StateTreeExecutionContext.h:790 中的 UE_DEPRECATED(5.6)
FStateTreeWeakTaskRef / FStateTreeStrongTaskRefFStateTreeWeakExecutionContextStateTreeNodeRef.h:15,51 中的 UE_DEPRECATED(5.6)
OnBindingChanged(…, const FStateTreePropertyPath&, …)FPropertyBindingPath 重载StateTreeNodeBase.h:186 中的 UE_DEPRECATED(5.6)
TrySelectChildrenAtUniformRandomTrySelectChildrenAtRandomStateTreeTypes.h:196 中的 UE_DEPRECATED(all)
TrySelectChildrenBasedOnRelativeUtilityTrySelectChildrenAtRandomWeightedByUtilityStateTreeTypes.h:197 中的 UE_DEPRECATED(all)
Compile(FStateTreeDataView, TArray<FText>&)Compile(UE::StateTree::ICompileNodeContext&)StateTreeNodeBase.h:141 中的 UE_DEPRECATED(5.6) final 存根
StateTreeComp->bStartLogicAutomatically = true;StateTreeComp->SetStartLogicAutomatically(true);Components/StateTreeComponent.h:187 中为 protected
TreeRef.GetParameters().SetValueFloat(…)TreeRef.GetMutableParameters().SetValueFloat(…)StateTreeReference.h:61 中的 const 访问器
FStateTreeActorContextTStateTreeExternalDataHandle<AActor> 或 Category = "Context" 实例数据属性引擎中不存在此类型

常见错误

遗漏 GetInstanceDataType(): 这是最常见的 State Tree 错误。using FInstanceDataType = …; 只是说明类型;只有重写该函数才能注册存储。

using FInstanceDataType = FMyTaskInstanceData;
virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }

存储执行上下文: 它是 FStateTreeInstanceData 的逐帧视图。将实例数据作为 UPROPERTY(Transient) 保存在所属对象上,并在每帧重建上下文;如果之后还要使用上下文,则捕获 FStateTreeWeakExecutionContext。

修改节点结构体: 所有节点虚函数都是 const,因此在成员上执行 Timer += DeltaTime; 无法编译。请写入 Context.GetInstanceData(*this)。

直接继承 FStateTreeTaskBase: 内置架构只允许使用 FStateTreeTaskCommonBase 及其同级类型,因此该节点不会出现在编辑器选择器中。请继承架构所接受的 *CommonBase 结构体(或 Mass/AI 基类)。

未使用 #if WITH_EDITOR 保护架构的 Allow* 重写: 基类仅在编辑器中声明这些函数(StateTreeSchema.h:96),因此未加保护的 override 在编辑器中可以编译,但在 Game/Shipping 目标中会失败。

在 TestCondition 中产生副作用: 在一帧内可能会多次测试同一个条件。请保持其纯粹;改为在条件的 EnterState 中缓存。

未解绑委托: 在 EnterState 中调用 BindDelegate,就必须在 ExitState 中调用 UnbindDelegate,否则监听器会在状态不再活动时触发。

超过 MaxStates: 活动状态路径最多为 8 层。层级过深时,选择会失败,但不会明确给出警告。

假设 Tick 一定会运行: 当 bShouldCallTick = false,或计划的 Tick 使树进入休眠时,任务不会执行 Tick,绑定的属性也不会被复制。请通过事件或委托驱动这些任务。

相关技能

  • ue-ai-navigation — 行为树、感知、EQS、导航和 Smart Objects
  • ue-mass-entity — Mass 处理器、片段、特征、实体查询
  • ue-gameplay-abilities — 驱动状态变化或对状态变化作出响应的能力与效果
  • ue-gameplay-framework — 树周边的控制器、Pawn 和游戏模式生命周期
  • ue-actor-component-architecture — 组件所有权、Tick 和复制设置
  • ue-cpp-foundations — USTRUCT 规则、委托、子系统访问
  • ue-gameplay-tags-messaging — 声明 State Tree 事件和转换所用的游戏玩法标签
  • ue-gameplay-cameras — 弹簧臂、视图目标、摄像机修饰器、抖动和 Gameplay Camera System

评论

支持 Markdown