UE5.8 StateTree 状态树专题系列

UE5.8 StateTree 专题(四):Evaluator、Global Task、Parameters 和数据从哪里来

解释 Evaluator、Global Task、全局参数、状态参数和任务实例数据,教你把守卫视野、玩家距离和巡逻点喂给 StateTree。

总览

StateTree 真正难的不是“画几个状态”,而是“状态判断需要的数据从哪里来”。守卫要不要追玩家,得知道是否看见玩家、玩家位置、最后看见的位置、距离、巡逻点、警戒时间。这些数据不能都藏在 Task 里乱读,否则树看起来很漂亮,调试时却不知道每个条件用的值从哪来。

UE5.8 StateTree 专题(四):Evaluator、Global Task、Parameters 和数据从哪里来 配图
StateTree 的难点常常不是状态,而是数据来源:参数、上下文、Evaluator、Task 输出要分清。

先分四类数据

数据来源 它是什么 守卫案例
Parameters 外部配置给整棵树的参数 视野距离、巡逻等待时间
State Parameters 某个状态自己的参数 PatrolA 的目标点
Evaluator 每帧计算并暴露给树的数据 CanSeePlayer、PlayerDistance
Global Task 随树长期运行的后台任务 监听感知事件、缓存最后目标

新手先记:参数偏“配置”,Evaluator 偏“计算出来的事实”,Task 偏“要做的动作”。

Parameters 怎么用

在 StateTree 资产里添加 Global Parameters,例如:

参数名 类型 默认值
SightRange Float 1500
LoseSightRange Float 2200
IdleDuration Float 2
HomeLocation Vector 岗位位置

这些值可以在不同守卫实例里覆盖。比如普通守卫视野 1500,精英守卫 2200。不要把这种可调数值硬编码在 Task 里。

Evaluator 怎么理解

Evaluator 像一个数据计算器。它不表示一个状态,也不表示一个动作。它在树运行时更新数据,供 Condition、Task、Binding 使用。例如 GuardPerceptionEvaluator 每帧算:

输出 含义
bCanSeePlayer 视线或感知是否看到玩家
PlayerLocation 玩家当前位置
LastSeenLocation 最后一次看到玩家的位置
DistanceToPlayer 守卫到玩家距离

然后 Transition 可以用 bCanSeePlayer,Move To 可以用 PlayerLocationLastSeenLocation

源码依据

FStateTreeEvaluatorBase 注释说明 Evaluator 用来计算并暴露 StateTree 决策数据,提供 TreeStartTreeStopTickUStateTree 中有 GetGlobalEvaluatorsBeginGetGlobalEvaluatorsNumGetGlobalTasksBegin 等访问运行时节点区间的接口。FStateTreeExecutionContext::FStartParameters 支持初始 Global Parameters。UE5.8 里旧的 Start(const FInstancedPropertyBag*) 已 deprecated,推荐使用 FStartParameters

架构分析

数据层可以按生命周期分三类:配置型数据放 Parameters,例如巡逻半径;每帧派生数据放 Evaluator,例如是否看见目标;执行过程中的临时状态放 Task InstanceData,例如等待剩余时间。不要把这三类混在一起,否则后期会分不清“这是设计师配置的值,还是运行时算出来的值”。

使用案例

Blueprint 版本的喂饭流程:

  1. 在 StateTree 里添加 Evaluator,命名 GuardPerception
  2. 给 Evaluator 添加输出变量:CanSeePlayerPlayerLocationLastSeenLocation
  3. 在 Patrol 的 Transition 上添加 Bool Compare。
  4. GuardPerception.CanSeePlayer 接到 Bool Compare 的 Left。
  5. Right 设为 true。
  6. Target State 设为 Alert。

C++ 概念长这样:

USTRUCT()
struct FGuardPerceptionEvaluator : public FStateTreeEvaluatorCommonBase
{
    GENERATED_BODY()

    using FInstanceDataType = FGuardPerceptionData;

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

    virtual void Tick(FStateTreeExecutionContext& Context, const float DeltaTime) const override
    {
        FGuardPerceptionData& Data = Context.GetInstanceData(*this);
        // 从感知组件或 Pawn 计算 CanSeePlayer、PlayerLocation。
    }
};

项目落地

给团队定一条规则:Transition 使用的数据,优先来自 Evaluator 或参数,不要来自某个 Task 的隐藏临时变量。Task 的输出如果要被别处用,就显式做成 InstanceData 输出,再用 Binding 接出去。这样调试时能在 StateTree 面板看到数据流。

常见坑

不要把所有变量都做成 Global Parameter。运行时计算的数据应该放 Evaluator 或组件里。不要让 Evaluator 做昂贵查询,每帧 LineTrace 一百次会很快出问题。不要在多个 Evaluator 里重复算同一个玩家距离。不要忘记默认值,编辑器里没绑定时默认值会参与判断。

源码路径索引

  • StateTreeModule/Public/StateTreeEvaluatorBase.h
  • StateTreeModule/Public/StateTreeTypes.h
  • StateTreeModule/Public/StateTreeExecutionContext.h
  • StateTreeModule/Public/StateTreeInstanceData.h