ue-blueprint-cpp-interop

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

UE Blueprint / C++ Interop

UE Blueprint / C++ 互操作

Target engine: UE 5.8. APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated — do not use".
This skill covers the boundary between native code and the Blueprint VM: which
UFUNCTION
/
UPROPERTY
specifiers and
meta=
keys produce which node shapes, function libraries, latent and async nodes, Blueprint-implementable interfaces, and how C++ calls back into a Blueprint graph. Everything here lives in
Core
,
CoreUObject
and
Engine
(
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine" });
); Project Settings pages also need
DeveloperSettings
. The authority for every specifier is
Runtime/CoreUObject/Public/UObject/ObjectMacros.h
— anything not declared in its
UC
,
UI
,
UF
,
UP
,
US
or
UM
enumerations is not a specifier.
目标引擎版本:UE 5.8。以下API均已对照5.8版本头文件验证;旧版用法列在「已弃用 — 请勿使用」章节中。
本技能覆盖原生代码与Blueprint VM之间的交互边界:哪些
UFUNCTION
/
UPROPERTY
说明符和
meta=
键会生成对应类型的节点、函数库、latent与异步节点、Blueprint可实现接口,以及C++如何回调Blueprint图表。本文涉及的所有内容均位于
Core
、
CoreUObject
和
Engine
模块中(
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine" });
);项目设置页面还需要
DeveloperSettings
模块。所有说明符的权威定义位于
Runtime/CoreUObject/Public/UObject/ObjectMacros.h
——任何未在其
UC
、
UI
、
UF
、
UP
、
US
或
UM
枚举中声明的内容都不是合法说明符。

Context

上下文

Read
.agents/ue-project-context.md
if it exists (module names, conventions, enabled plugins, GAS/networking setup). Do not stop if it is missing.
Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.
Request is about…Go to
Making a C++ function callable or overridable in BlueprintUFUNCTION Exposure
Making data visible or editable in Blueprint, getters/settersUPROPERTY Exposure
Static helpers,
WorldContext
,
DefaultToSelf
Function Libraries
A node with an exec pin that finishes later (Delay-style)Latent Nodes
A node with several output exec pins that fire on eventsAsync Action Nodes
Interfaces Blueprint can implement,
Execute_
,
TScriptInterface
Blueprint Interfaces
UENUM
,
USTRUCT
, containers as Blueprint types
Enums and Structs in Blueprint
BlueprintAssignable
events, binding Blueprint to a C++ delegate
Delegates and Events
Running Blueprint logic from native code, Blueprint classesCalling Blueprint from Native Code
Subsystems,
UDeveloperSettings
,
Exec
commands
Subsystems and Settings in Blueprint
A button in the Details panel, editor-only actionsEditor-Callable Functions
如果存在
.agents/ue-project-context.md
文件请先阅读(包含模块名、编码规范、启用的插件、GAS/网络配置等信息)。若文件不存在也无需中断。
根据用户请求和代码库确定具体涉及的领域。仅当两种合理的解读会生成完全不同的代码时,才向用户提问确认。
用户请求涉及…前往
让C++函数可在Blueprint中调用或重写UFUNCTION 暴露
让数据在Blueprint中可见或可编辑、getter/setterUPROPERTY 暴露
静态辅助函数、
WorldContext
、
DefaultToSelf
函数库
带有exec引脚且延迟完成的节点(Delay风格)Latent节点
带有多个输出exec引脚、在事件触发时执行的节点异步动作节点
Blueprint可实现的接口、
Execute_
、
TScriptInterface
Blueprint接口
UENUM
、
USTRUCT
、容器作为Blueprint类型
Blueprint中的枚举与结构体
BlueprintAssignable
事件、将Blueprint绑定到C++委托
委托与事件
从原生代码运行Blueprint逻辑、Blueprint类从原生代码调用Blueprint
子系统、
UDeveloperSettings
、
Exec
命令
Blueprint中的子系统与设置
详情面板中的按钮、仅编辑器可用的操作编辑器可调用函数

UFUNCTION Exposure

UFUNCTION 暴露 {#ufunction-exposure}

SpecifierNode shapeC++ body
BlueprintCallable
Exec pins in and outYours
BlueprintPure
No exec pins; implies
BlueprintCallable
Yours, side-effect free
BlueprintImplementableEvent
Event the Blueprint implementsNone — UHT generates the thunk
BlueprintNativeEvent
Event the Blueprint may override
Name_Implementation
only
BlueprintGetter
/
BlueprintSetter
Hidden; reached through the propertyYours
BlueprintAuthorityOnly
Blueprint calls are skipped without network authority (direct C++ calls still run)Yours
BlueprintCosmetic
Blueprint calls are skipped on a dedicated server (direct C++ calls still run)Yours
BlueprintInternalUseOnly
Not offered in the palette; only generated nodes call itYours
CallInEditor
Button on the Details panelYours
Exec
Console commandYours
Category="A\|B"
builds a nested palette category. The node-shaping
meta=
keys —
DisplayName
,
ToolTip
,
Keywords
,
CompactNodeTitle
,
AdvancedDisplay
,
ReturnDisplayName
,
HidePin
,
DeterminesOutputType
,
DynamicOutputParam
,
ArrayParm
,
ExpandEnumAsExecs
,
WorldContext
,
UnsafeDuringActorConstruction
,
DevelopmentOnly
,
BlueprintThreadSafe
and the rest — are tabled with header citations in references/blueprint-meta-specifiers.md.
cpp
// MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

UCLASS(Blueprintable)
class MYGAME_API AMyActor : public AActor
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable, Category="MyGame|Combat", meta=(Keywords="damage hurt"))
    void ApplyDamage(float Amount);

    UFUNCTION(BlueprintPure, Category="MyGame|Combat", meta=(CompactNodeTitle="HP", ReturnDisplayName="Health"))
    float GetHealth() const;

    /** Blueprint supplies the whole body; C++ never defines this. */
    UFUNCTION(BlueprintImplementableEvent, Category="MyGame|Combat")
    void OnDamaged(float Amount);

    /** C++ supplies the default in OnDied_Implementation; Blueprint may override it. */
    UFUNCTION(BlueprintNativeEvent, Category="MyGame|Combat")
    void OnDied();
    virtual void OnDied_Implementation();

    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="MyGame|Combat")
    void ResetOnServer() { Health = MaxHealth; }     // Blueprint calls skipped on clients

    UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category="MyGame|FX")
    void PlayHitFlash() {}                           // Blueprint calls skipped on a dedicated server

    UFUNCTION(CallInEditor, Category="MyGame|Tools")
    void SnapToGround() {}                           // button in the Details panel

private:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MyGame|Combat", meta=(AllowPrivateAccess="true", ClampMin="0.0"))
    float Health = 100.f;

    float MaxHealth = 100.f;
};
cpp
// MyActor.cpp
#include "MyActor.h"

void AMyActor::ApplyDamage(float Amount)
{
    Health = FMath::Max(0.f, Health - Amount);
    OnDamaged(Amount);                  // runs the Blueprint graph
    if (Health <= 0.f) { OnDied(); }    // runs the BP override if any, else OnDied_Implementation
}

float AMyActor::GetHealth() const { return Health; }
void AMyActor::OnDied_Implementation() {}            // the C++ default, overridable in Blueprint
Raise an event by its plain name (
OnDied()
), never
OnDied_Implementation()
— the plain name is the generated thunk that dispatches to Blueprint. A subclass overrides
virtual void OnDied_Implementation() override;
and chains with
Super::OnDied_Implementation();
, never
Super::OnDied();
.
说明符节点形态C++实现
BlueprintCallable
带输入输出exec引脚用户自行实现
BlueprintPure
无exec引脚;隐含
BlueprintCallable
属性
用户自行实现,无副作用
BlueprintImplementableEvent
Blueprint实现的事件无需实现 —— 由UHT生成转接函数
BlueprintNativeEvent
Blueprint可重写的事件仅需实现
Name_Implementation
BlueprintGetter
/
BlueprintSetter
隐藏;通过属性访问用户自行实现
BlueprintAuthorityOnly
无网络权限时跳过Blueprint调用(直接C++调用仍会执行)用户自行实现
BlueprintCosmetic
在专用服务器上跳过Blueprint调用(直接C++调用仍会执行)用户自行实现
BlueprintInternalUseOnly
不在节点面板中显示;仅生成的节点可调用用户自行实现
CallInEditor
详情面板中的按钮用户自行实现
Exec
控制台命令用户自行实现
Category="A\|B"
用于创建嵌套的节点面板分类。影响节点形态的
meta=
键——包括
DisplayName
、
ToolTip
、
Keywords
、
CompactNodeTitle
、
AdvancedDisplay
、
ReturnDisplayName
、
HidePin
、
DeterminesOutputType
、
DynamicOutputParam
、
ArrayParm
、
ExpandEnumAsExecs
、
WorldContext
、
UnsafeDuringActorConstruction
、
DevelopmentOnly
、
BlueprintThreadSafe
等——已在references/blueprint-meta-specifiers.md中整理成表格并附带头文件引用。
cpp
// MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

UCLASS(Blueprintable)
class MYGAME_API AMyActor : public AActor
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable, Category="MyGame|Combat", meta=(Keywords="damage hurt"))
    void ApplyDamage(float Amount);

    UFUNCTION(BlueprintPure, Category="MyGame|Combat", meta=(CompactNodeTitle="HP", ReturnDisplayName="Health"))
    float GetHealth() const;

    /** Blueprint supplies the whole body; C++ never defines this. */
    UFUNCTION(BlueprintImplementableEvent, Category="MyGame|Combat")
    void OnDamaged(float Amount);

    /** C++ supplies the default in OnDied_Implementation; Blueprint may override it. */
    UFUNCTION(BlueprintNativeEvent, Category="MyGame|Combat")
    void OnDied();
    virtual void OnDied_Implementation();

    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="MyGame|Combat")
    void ResetOnServer() { Health = MaxHealth; }     // Blueprint calls skipped on clients

    UFUNCTION(BlueprintCallable, BlueprintCosmetic, Category="MyGame|FX")
    void PlayHitFlash() {}                           // Blueprint calls skipped on a dedicated server

    UFUNCTION(CallInEditor, Category="MyGame|Tools")
    void SnapToGround() {}                           // button in the Details panel

private:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="MyGame|Combat", meta=(AllowPrivateAccess="true", ClampMin="0.0"))
    float Health = 100.f;

    float MaxHealth = 100.f;
};
cpp
// MyActor.cpp
#include "MyActor.h"

void AMyActor::ApplyDamage(float Amount)
{
    Health = FMath::Max(0.f, Health - Amount);
    OnDamaged(Amount);                  // runs the Blueprint graph
    if (Health <= 0.f) { OnDied(); }    // runs the BP override if any, else OnDied_Implementation
}

float AMyActor::GetHealth() const { return Health; }
void AMyActor::OnDied_Implementation() {}            // the C++ default, overridable in Blueprint
触发事件时请使用其原始名称(
OnDied()
),绝对不要调用
OnDied_Implementation()
——原始名称对应的是UHT生成的、负责调度到Blueprint的转接函数。子类重写时应使用
virtual void OnDied_Implementation() override;
,并通过
Super::OnDied_Implementation();
调用父类实现,绝不要调用
Super::OnDied();
。

Parameters and pins

参数与引脚 {#parameters-and-pins}

C++ parameterBlueprint pin
float Amount
Input
const FVector& Base
Input, passed by reference
FVector& Out
Output pin, not an input
UPARAM(ref) FVector& InOut
By-reference input that is also an output
UPARAM(DisplayName="Event") FMyReadySignature Callback
Input renamed on the node
A bare
T&
gets the
OutParm
flag from UHT;
UPARAM(ref)
adds
ReferenceParm
so the pin stays an input.
meta=(AutoCreateRefTerm="Offset")
gives a reference input an inline literal when nothing is connected.
C++参数Blueprint引脚
float Amount
输入引脚
const FVector& Base
输入引脚,按引用传递
FVector& Out
输出引脚,而非输入
UPARAM(ref) FVector& InOut
按引用传递的输入引脚,同时也是输出
UPARAM(DisplayName="Event") FMyReadySignature Callback
输入引脚在节点上重命名
裸的
T&
参数会被UHT标记为
OutParm
(输出参数);
UPARAM(ref)
会添加
ReferenceParm
标记,使引脚保持为输入类型。
meta=(AutoCreateRefTerm="Offset")
可以让引用输入引脚在未连接任何内容时显示内联字面量输入框。

UPROPERTY Exposure

UPROPERTY 暴露 {#uproperty-exposure}

SpecifierEffect in Blueprint
BlueprintReadWrite
Get and Set nodes
BlueprintReadOnly
Get node only
BlueprintGetter=Fn
Get routes through
UFUNCTION(BlueprintGetter)
BlueprintSetter=Fn
Set routes through
UFUNCTION(BlueprintSetter)
; implies
BlueprintReadWrite
BlueprintAssignable
Bind Event node (multicast dynamic delegates only)
BlueprintCallable
Broadcast node (multicast dynamic delegates only)
BlueprintAuthorityOnly
Delegate accepts only
BlueprintAuthorityOnly
events
BlueprintReadWrite
/
BlueprintReadOnly
on a
private
member is a UHT error ("BlueprintReadWrite should not be used on private members") unless the property carries
meta=(AllowPrivateAccess="true")
. An editor-only property cannot be a Blueprint-exposed struct member. Editor
meta=
keys (
ClampMin
/
ClampMax
,
UIMin
/
UIMax
,
EditCondition
,
EditConditionHides
,
InlineEditConditionToggle
,
TitleProperty
,
ShowOnlyInnerProperties
,
MultiLine
,
GetOptions
,
AllowedClasses
,
MustImplement
,
Categories
,
Bitmask
/
BitmaskEnum
,
ExposeOnSpawn
): references/blueprint-meta-specifiers.md.
C++ member typeBlueprint pin
double
/
float
Real
TObjectPtr<UStaticMesh>
Object reference
TSubclassOf<AActor>
Class (filtered by
meta=(MustImplement=…)
,
AllowedClasses
,
BlueprintBaseOnly
)
TSoftObjectPtr<T>
/
TSoftClassPtr<T>
Soft reference; Blueprint resolves or loads on demand
FInstancedStruct
Struct pin whose concrete type is chosen in the editor
TScriptInterface<IMyInteractable>
Interface reference
FMyHealthChangedSignature
(multicast dynamic)
Bind Event / Broadcast nodes via
BlueprintAssignable
FTimerHandle
USTRUCT(BlueprintType)
handle passed straight through
A worked component showing
ExposeOnSpawn
,
EditCondition
/
EditConditionHides
,
InlineEditConditionToggle
,
MustImplement
and a
BlueprintGetter
/
BlueprintSetter
pair: references/blueprint-meta-specifiers.md.
Floats and doubles. Both
float
and
double
properties show as a Blueprint Real pin (
UEdGraphSchema_K2
maps
FFloatProperty
and
FDoubleProperty
to
PC_Real
with sub-category
PC_Float
/
PC_Double
), and Blueprint-authored variables are doubles. Declare
double
for scalars that flow through Blueprint math so no narrowing conversion is inserted; keep
float
only where the engine type is already float.
说明符在Blueprint中的效果
BlueprintReadWrite
生成Get和Set节点
BlueprintReadOnly
仅生成Get节点
BlueprintGetter=Fn
Get操作通过
UFUNCTION(BlueprintGetter)
指定的函数执行
BlueprintSetter=Fn
Set操作通过
UFUNCTION(BlueprintSetter)
指定的函数执行;隐含
BlueprintReadWrite
属性
BlueprintAssignable
生成Bind Event节点(仅适用于多播动态委托)
BlueprintCallable
生成Broadcast节点(仅适用于多播动态委托)
BlueprintAuthorityOnly
委托仅接受标记为
BlueprintAuthorityOnly
的事件
在
private
成员上使用
BlueprintReadWrite
/
BlueprintReadOnly
会触发UHT错误(提示「BlueprintReadWrite should not be used on private members」),除非该属性添加了
meta=(AllowPrivateAccess="true")
标记。仅编辑器可用的属性不能作为Blueprint可暴露的结构体成员。编辑器相关的
meta=
键(
ClampMin
/
ClampMax
、
UIMin
/
UIMax
、
EditCondition
、
EditConditionHides
、
InlineEditConditionToggle
、
TitleProperty
、
ShowOnlyInnerProperties
、
MultiLine
、
GetOptions
、
AllowedClasses
、
MustImplement
、
Categories
、
Bitmask
/
BitmaskEnum
、
ExposeOnSpawn
)请参见:references/blueprint-meta-specifiers.md。
C++成员类型Blueprint引脚
double
/
float
Real(实数)
TObjectPtr<UStaticMesh>
对象引用
TSubclassOf<AActor>
类(可通过
meta=(MustImplement=…)
、
AllowedClasses
、
BlueprintBaseOnly
过滤)
TSoftObjectPtr<T>
/
TSoftClassPtr<T>
软引用;Blueprint按需解析或加载
FInstancedStruct
结构体引脚,具体类型可在编辑器中选择
TScriptInterface<IMyInteractable>
接口引用
FMyHealthChangedSignature
(多播动态)
通过
BlueprintAssignable
生成Bind Event / Broadcast节点
FTimerHandle
标记为
USTRUCT(BlueprintType)
的句柄直接透传
展示
ExposeOnSpawn
、
EditCondition
/
EditConditionHides
、
InlineEditConditionToggle
、
MustImplement
以及
BlueprintGetter
/
BlueprintSetter
配对用法的完整组件示例请参见:references/blueprint-meta-specifiers.md。
float与double。
float
和
double
属性在Blueprint中都会显示为Real(实数)引脚(
UEdGraphSchema_K2
会将
FFloatProperty
和
FDoubleProperty
映射为
PC_Real
类型,子类别分别为
PC_Float
/
PC_Double
),且Blueprint中创建的变量默认是double类型。对于会在Blueprint中参与数学运算的标量,请声明为
double
以避免窄化转换;仅在引擎本身使用float的场景下保留
float
类型。

Function Libraries

函数库 {#function-libraries}

UBlueprintFunctionLibrary
(
Kismet/BlueprintFunctionLibrary.h
) is
UCLASS(Abstract)
and every member is
static
.
GetWorld()
returns null on its CDO, so take
const UObject* WorldContextObject
, tag it
meta=(WorldContext="WorldContextObject")
and resolve with
GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
;
meta=(DefaultToSelf="Param")
pre-wires a pin to the calling object.
cpp
// MyBlueprintLibrary.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Templates/SubclassOf.h"
#include "MyBlueprintLibrary.generated.h"

class AActor;

UENUM(BlueprintType)
enum class EMyConsumeResult : uint8
{
    Consumed,
    NotEnough
};

UCLASS()
class MYGAME_API UMyBlueprintLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()
public:
    /** WorldContextObject is hidden on the node and filled in by the graph. */
    UFUNCTION(BlueprintCallable, Category="MyGame|Utility", meta=(WorldContext="WorldContextObject"))
    static int32 CountPawns(const UObject* WorldContextObject);

    /** The return pin takes the type plugged into ActorClass, so Blueprint needs no cast. */
    UFUNCTION(BlueprintPure, Category="MyGame|Utility", meta=(DefaultToSelf="Target", DeterminesOutputType="ActorClass"))
    static AActor* FindAttachedActorOfClass(AActor* Target, TSubclassOf<AActor> ActorClass);

    /** One input enum becomes one output exec pin per enumerator. */
    UFUNCTION(BlueprintCallable, Category="MyGame|Utility", meta=(ExpandEnumAsExecs="Result"))
    static void TryConsume(int32 Available, int32 Amount, EMyConsumeResult& Result);
};
The matching
.cpp
: references/blueprint-node-patterns.md.
ExpandEnumAsExecs
accepts exactly one input parameter (UHT errors on a second); name
ReturnValue
to expand the return value, and use
ExpandBoolAsExecs
for a bool. Wildcard array nodes use
meta=(ArrayParm="Items", ArrayTypeDependentParams="Item")
.
UBlueprintFunctionLibrary
(位于
Kismet/BlueprintFunctionLibrary.h
)是
UCLASS(Abstract)
类,所有成员均为
static
。在其CDO(类默认对象)上调用
GetWorld()
会返回null,因此需要接收
const UObject* WorldContextObject
参数,为其添加
meta=(WorldContext="WorldContextObject")
标记,并通过
GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
来获取World;
meta=(DefaultToSelf="Param")
会将指定引脚预连接到调用该函数的对象。
cpp
// MyBlueprintLibrary.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Templates/SubclassOf.h"
#include "MyBlueprintLibrary.generated.h"

class AActor;

UENUM(BlueprintType)
enum class EMyConsumeResult : uint8
{
    Consumed,
    NotEnough
};

UCLASS()
class MYGAME_API UMyBlueprintLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()
public:
    /** WorldContextObject is hidden on the node and filled in by the graph. */
    UFUNCTION(BlueprintCallable, Category="MyGame|Utility", meta=(WorldContext="WorldContextObject"))
    static int32 CountPawns(const UObject* WorldContextObject);

    /** The return pin takes the type plugged into ActorClass, so Blueprint needs no cast. */
    UFUNCTION(BlueprintPure, Category="MyGame|Utility", meta=(DefaultToSelf="Target", DeterminesOutputType="ActorClass"))
    static AActor* FindAttachedActorOfClass(AActor* Target, TSubclassOf<AActor> ActorClass);

    /** One input enum becomes one output exec pin per enumerator. */
    UFUNCTION(BlueprintCallable, Category="MyGame|Utility", meta=(ExpandEnumAsExecs="Result"))
    static void TryConsume(int32 Available, int32 Amount, EMyConsumeResult& Result);
};
对应的
.cpp
实现请参见:references/blueprint-node-patterns.md。
ExpandEnumAsExecs
仅接受一个输入参数(第二个会触发UHT错误);若要展开返回值,将参数命名为
ReturnValue
即可;对于bool类型请使用
ExpandBoolAsExecs
。通配符数组节点使用
meta=(ArrayParm="Items", ArrayTypeDependentParams="Item")
。

Latent Nodes

Latent节点 {#latent-nodes}

A latent node holds one exec pin pending across frames. It needs
meta=(Latent, LatentInfo="LatentInfo", WorldContext="WorldContextObject")
and a trailing
FLatentActionInfo
parameter, plus an
FPendingLatentAction
subclass registered with the world's
FLatentActionManager
.
UKismetSystemLibrary::Delay
and
FDelayAction
(
Engine/Public/DelayAction.h
) are the engine reference.
cpp
// MyLatentLibrary.h — declaration
UFUNCTION(BlueprintCallable, Category="MyGame|Flow", meta=(Latent, LatentInfo="LatentInfo", WorldContext="WorldContextObject", Duration="1.0"))
static void WaitSeconds(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo);
cpp
// MyLatentLibrary.cpp — the UUID guard is mandatory
void UMyLatentLibrary::WaitSeconds(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo)
{
    if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
    {
        FLatentActionManager& Manager = World->GetLatentActionManager();
        if (Manager.FindExistingAction<FMyWaitAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr)
        {
            Manager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FMyWaitAction(Duration, LatentInfo));
        }
    }
}
Without the
FindExistingAction
guard, re-entering the node stacks a second action on the same UUID and the exec pin fires more than once. The action subclass overrides
UpdateOperation(FLatentResponse& Response)
and finishes with
Response.FinishAndTriggerIf(bDone, ExecutionFunction, OutputLink, CallbackTarget)
, copying
ExecutionFunction
,
Linkage
and
CallbackTarget
out of the
FLatentActionInfo
in its constructor; it also overrides
NotifyObjectDestroyed()
,
NotifyActionAborted()
and, under
WITH_EDITOR
,
GetDescription() const
.
FLatentResponse
additionally offers
DoneIf(bool)
and
TriggerLink(ExecutionFunction, LinkID, CallbackTarget)
for several exit links. Complete subclass and the rest of the
FLatentActionManager
API: references/blueprint-node-patterns.md.
Latent节点会将一个exec引脚挂起,跨帧等待执行完成。它需要添加
meta=(Latent, LatentInfo="LatentInfo", WorldContext="WorldContextObject")
标记,以及一个末尾的
FLatentActionInfo
参数,此外还需要一个
FPendingLatentAction
的子类,并将其注册到World的
FLatentActionManager
中。
UKismetSystemLibrary::Delay
和
FDelayAction
(位于
Engine/Public/DelayAction.h
)是引擎自带的参考实现。
cpp
// MyLatentLibrary.h — 声明
UFUNCTION(BlueprintCallable, Category="MyGame|Flow", meta=(Latent, LatentInfo="LatentInfo", WorldContext="WorldContextObject", Duration="1.0"))
static void WaitSeconds(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo);
cpp
// MyLatentLibrary.cpp — UUID保护是必须的
void UMyLatentLibrary::WaitSeconds(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo)
{
    if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
    {
        FLatentActionManager& Manager = World->GetLatentActionManager();
        if (Manager.FindExistingAction<FMyWaitAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr)
        {
            Manager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FMyWaitAction(Duration, LatentInfo));
        }
    }
}
如果没有
FindExistingAction
的保护,重复进入该节点会在同一个UUID上堆叠第二个动作,导致exec引脚触发多次。动作子类需要重写
UpdateOperation(FLatentResponse& Response)
,并通过
Response.FinishAndTriggerIf(bDone, ExecutionFunction, OutputLink, CallbackTarget)
标记完成,其中
ExecutionFunction
、
Linkage
和
CallbackTarget
需要在构造函数中从
FLatentActionInfo
里复制出来;同时还需要重写
NotifyObjectDestroyed()
、
NotifyActionAborted()
,以及在
WITH_EDITOR
宏下重写
GetDescription() const
。
FLatentResponse
还提供了
DoneIf(bool)
和
TriggerLink(ExecutionFunction, LinkID, CallbackTarget)
用于支持多个输出引脚。完整的子类实现以及
FLatentActionManager
的其他API请参见:references/blueprint-node-patterns.md。

Async Action Nodes

异步动作节点 {#async-action-nodes}

UBlueprintAsyncActionBase
(
Kismet/BlueprintAsyncActionBase.h
) produces a node with one exec input and one output exec pin per
BlueprintAssignable
delegate. The static factory must carry
meta=(BlueprintInternalUseOnly="true")
so only the generated node can call it.
cpp
// MyWaitForHealthAction.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "MyWaitForHealthAction.generated.h"

class UMyStatsComponent;

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyHealthThresholdSignature, double, Health);

UCLASS()
class MYGAME_API UMyWaitForHealthAction : public UBlueprintAsyncActionBase
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable, Category="MyGame|Async", meta=(BlueprintInternalUseOnly="true", WorldContext="WorldContextObject", DisplayName="Wait For Health Below"))
    static UMyWaitForHealthAction* WaitForHealthBelow(UObject* WorldContextObject, UMyStatsComponent* Stats, double Threshold);

    virtual void Activate() override;

    UPROPERTY(BlueprintAssignable)
    FMyHealthThresholdSignature OnReached;      // one output exec pin

    UPROPERTY(BlueprintAssignable)
    FMyHealthThresholdSignature OnFailed;       // a second output exec pin

private:
    UFUNCTION()
    void HandleHealthChanged(double NewHealth);   // AddDynamic targets must be UFUNCTIONs

    UPROPERTY()
    TObjectPtr<UMyStatsComponent> TrackedStats;

    double ThresholdValue = 0.0;
};
The factory does
NewObject<UMyWaitForHealthAction>()
, stores its inputs, calls
RegisterWithGameInstance(WorldContextObject)
and returns the action; the node then binds the delegates and calls
Activate()
, where the work starts. Each completion path broadcasts once and calls
SetReadyToDestroy()
. An unregistered action lives only for the frame it was created on (
RF_StrongRefOnFrame
), so a delegate firing later finds it collected. The engine ensures when more than
bp.MaxAsyncActionCount
(default 10000) actions are alive at once.
UCancellableAsyncAction
(
Engine/CancellableAsyncAction.h
,
UCLASS(Abstract, BlueprintType, meta=(ExposedAsyncProxy=AsyncAction))
) adds
Cancel()
,
IsActive() const
and
ShouldBroadcastDelegates() const
so the node hands the object back for later cancelling; guard every broadcast with
ShouldBroadcastDelegates()
. Full
Activate
/
Cancel
implementations: references/blueprint-node-patterns.md.
UBlueprintAsyncActionBase
(位于
Kismet/BlueprintAsyncActionBase.h
)会生成带有一个输入exec引脚、且每个
BlueprintAssignable
委托对应一个输出exec引脚的节点。静态工厂函数必须添加
meta=(BlueprintInternalUseOnly="true")
标记,确保只有生成的节点可以调用它。
cpp
// MyWaitForHealthAction.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "MyWaitForHealthAction.generated.h"

class UMyStatsComponent;

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyHealthThresholdSignature, double, Health);

UCLASS()
class MYGAME_API UMyWaitForHealthAction : public UBlueprintAsyncActionBase
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable, Category="MyGame|Async", meta=(BlueprintInternalUseOnly="true", WorldContext="WorldContextObject", DisplayName="Wait For Health Below"))
    static UMyWaitForHealthAction* WaitForHealthBelow(UObject* WorldContextObject, UMyStatsComponent* Stats, double Threshold);

    virtual void Activate() override;

    UPROPERTY(BlueprintAssignable)
    FMyHealthThresholdSignature OnReached;      // 一个输出exec引脚

    UPROPERTY(BlueprintAssignable)
    FMyHealthThresholdSignature OnFailed;       // 第二个输出exec引脚

private:
    UFUNCTION()
    void HandleHealthChanged(double NewHealth);   // AddDynamic的目标必须是UFUNCTION

    UPROPERTY()
    TObjectPtr<UMyStatsComponent> TrackedStats;

    double ThresholdValue = 0.0;
};
工厂函数会执行
NewObject<UMyWaitForHealthAction>()
,存储输入参数,调用
RegisterWithGameInstance(WorldContextObject)
,然后返回动作对象;之后节点会绑定委托并调用
Activate()
,正式开始执行逻辑。每个完成路径都应广播一次委托并调用
SetReadyToDestroy()
。未注册的动作对象仅在创建它的那一帧有效(标记为
RF_StrongRefOnFrame
),因此之后触发的委托会找不到已被回收的对象。当同时存活的动作数量超过
bp.MaxAsyncActionCount
(默认10000)时,引擎会进行相应处理。
UCancellableAsyncAction
(位于
Engine/CancellableAsyncAction.h
,声明为
UCLASS(Abstract, BlueprintType, meta=(ExposedAsyncProxy=AsyncAction))
)新增了
Cancel()
、
IsActive() const
和
ShouldBroadcastDelegates() const
方法,节点会将动作对象返回给用户以便后续取消;每次广播委托前都应使用
ShouldBroadcastDelegates()
进行保护。完整的
Activate
/
Cancel
实现请参见:references/blueprint-node-patterns.md。

Blueprint Interfaces

Blueprint接口 {#blueprint-interfaces}

UINTERFACE
's own specifiers (
UI::
) are
MinimalAPI
,
Blueprintable
,
NotBlueprintable
and
ConversionRoot
plus
meta=
; UHT's shared
BlueprintType
is also accepted, and engine interfaces use it so the interface is usable as a Blueprint variable type. Blueprint can implement
BlueprintImplementableEvent
and
BlueprintNativeEvent
members; a plain
virtual
C++ method is invisible to Blueprint.
cpp
// MyInteractable.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MyInteractable.generated.h"

class AActor;

UINTERFACE(Blueprintable, BlueprintType, MinimalAPI)
class UMyInteractable : public UInterface
{
    GENERATED_BODY()
};

class MYGAME_API IMyInteractable
{
    GENERATED_BODY()
public:
    /** Blueprint-only implementations are allowed; C++ implementers write Interact_Implementation. */
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="MyGame|Interaction")
    bool Interact(AActor* Interactor);
    virtual bool Interact_Implementation(AActor* Interactor);

    virtual float GetInteractRange() const { return 200.f; }   // native-only: invisible to Blueprint
};
cpp
// Calling an interface that may be implemented in C++ or in Blueprint
bool MyTryInteract(UObject* Target, AActor* Interactor)
{
    return Target && Target->Implements<UMyInteractable>()
        && IMyInteractable::Execute_Interact(Target, Interactor);
}
FormC++ implementersBlueprint implementers
IMyInteractable::Execute_Interact(Obj, …)
YesYes — always use this
Obj->Implements<UMyInteractable>()
YesYes
Obj->GetClass()->ImplementsInterface(UMyInteractable::StaticClass())
YesYes
Cast<IMyInteractable>(Obj)
then a direct call
YesNo — the cast returns null
TScriptInterface<IMyInteractable>
member
YesInterface pointer is null; still call through
Execute_
TScriptInterface<T>
(
UObject/ScriptInterface.h
) stores the object and the interface pointer together and is documented as useful for native interfaces only. Declare
UPROPERTY(EditAnywhere, BlueprintReadWrite) TScriptInterface<IMyInteractable> Target;
so designers get a filtered picker, and still dispatch with
Execute_
.
UINTERFACE
自身的说明符(
UI::
枚举)包括
MinimalAPI
、
Blueprintable
、
NotBlueprintable
、
ConversionRoot
以及
meta=
标记;UHT也支持通用的
BlueprintType
说明符,引擎自带的接口大多使用它,这样接口就可以作为Blueprint的变量类型。Blueprint可以实现
BlueprintImplementableEvent
和
BlueprintNativeEvent
类型的成员;普通的
virtual
C++方法对Blueprint是不可见的。
cpp
// MyInteractable.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MyInteractable.generated.h"

class AActor;

UINTERFACE(Blueprintable, BlueprintType, MinimalAPI)
class UMyInteractable : public UInterface
{
    GENERATED_BODY()
};

class MYGAME_API IMyInteractable
{
    GENERATED_BODY()
public:
    /** Blueprint-only implementations are allowed; C++ implementers write Interact_Implementation. */
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="MyGame|Interaction")
    bool Interact(AActor* Interactor);
    virtual bool Interact_Implementation(AActor* Interactor);

    virtual float GetInteractRange() const { return 200.f; }   // 仅原生:对Blueprint不可见
};
cpp
// 调用可能由C++或Blueprint实现的接口
bool MyTryInteract(UObject* Target, AActor* Interactor)
{
    return Target && Target->Implements<UMyInteractable>()
        && IMyInteractable::Execute_Interact(Target, Interactor);
}
调用方式C++实现者Blueprint实现者
IMyInteractable::Execute_Interact(Obj, …)
支持支持 —— 请始终使用这种方式
Obj->Implements<UMyInteractable>()
支持支持
Obj->GetClass()->ImplementsInterface(UMyInteractable::StaticClass())
支持支持
Cast<IMyInteractable>(Obj)
后直接调用
支持不支持 —— 转换会返回null
TScriptInterface<IMyInteractable>
成员
支持接口指针为null;仍需通过
Execute_
调用
TScriptInterface<T>
(位于
UObject/ScriptInterface.h
)会同时存储对象和接口指针,官方文档称其仅适用于原生接口。声明
UPROPERTY(EditAnywhere, BlueprintReadWrite) TScriptInterface<IMyInteractable> Target;
可以让设计师在编辑器中使用过滤后的选择器,但调用时仍需通过
Execute_
进行分发。

Enums and Structs in Blueprint

Blueprint中的枚举与结构体 {#enums-and-structs-in-blueprint}

cpp
// MyTypes.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "MyTypes.generated.h"

UENUM(BlueprintType)
enum class EMyWeaponState : uint8      // BlueprintType enums must be uint8-based
{
    Idle,
    Firing      UMETA(DisplayName="Firing Weapon"),
    Reloading,
    MAX         UMETA(Hidden)
};

USTRUCT(BlueprintType)
struct MYGAME_API FMyWeaponStats
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    double BaseDamage = 10.0;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    TArray<FName> Tags;                // container of a Blueprint-supported type

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    TMap<FName, double> Modifiers;     // key and value must both be Blueprint-supported
};

USTRUCT(BlueprintType)
struct MYGAME_API FMyWeaponRow : public FTableRowBase   // data table row type
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    FMyWeaponStats Stats;
};
  • UENUM(BlueprintType)
    requires
    enum class : uint8
    ; UHT rejects any other base ("Invalid BlueprintType enum base - currently only uint8 supported").
    UMETA(DisplayName="…")
    renames an enumerator and
    UMETA(Hidden)
    hides it from pickers.
  • USTRUCT(BlueprintType)
    gives Make and Break nodes. Structs cannot hold
    UFUNCTION
    s — put struct helpers in a function library.
  • No nested containers.
    TArray
    ,
    TSet
    and
    TMap
    clear their own "can be a container key/value" capability in UHT, so
    TArray<TArray<int32>>
    and
    TMap<FName, TArray<int32>>
    do not compile. Wrap the inner container in a
    USTRUCT
    .
  • A container is Blueprint-exposable only when its element type — and for
    TMap
    its key type — is itself Blueprint-supported.
  • FTableRowBase
    (
    Engine/DataTable.h
    ) is
    USTRUCT(BlueprintInternalUseOnly)
    ; derive row structs from it and mark the derived struct
    BlueprintType
    . Table loading and row lookup:
    ue-data-assets-tables
    .
  • FInstancedStruct
    (
    StructUtils/InstancedStruct.h
    ) is
    USTRUCT(BlueprintType)
    and gives Blueprint a single pin whose concrete struct type is chosen in the editor;
    TInstancedStruct<FMyBase>
    is the typed C++ wrapper.
cpp
// MyTypes.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "MyTypes.generated.h"

UENUM(BlueprintType)
enum class EMyWeaponState : uint8      // BlueprintType枚举必须基于uint8
{
    Idle,
    Firing      UMETA(DisplayName="Firing Weapon"),
    Reloading,
    MAX         UMETA(Hidden)
};

USTRUCT(BlueprintType)
struct MYGAME_API FMyWeaponStats
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    double BaseDamage = 10.0;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    TArray<FName> Tags;                // Blueprint支持类型的容器

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    TMap<FName, double> Modifiers;     // 键和值都必须是Blueprint支持的类型
};

USTRUCT(BlueprintType)
struct MYGAME_API FMyWeaponRow : public FTableRowBase   // 数据表行类型
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Weapon")
    FMyWeaponStats Stats;
};
  • UENUM(BlueprintType)
    要求枚举必须是
    enum class : uint8
    类型;UHT会拒绝其他任何底层类型(提示「Invalid BlueprintType enum base - currently only uint8 supported」)。
    UMETA(DisplayName="…")
    可以重命名枚举项,
    UMETA(Hidden)
    可以在选择器中隐藏该枚举项。
  • USTRUCT(BlueprintType)
    会生成Make和Break节点。结构体中不能包含
    UFUNCTION
    ——请将结构体的辅助函数放在函数库中。
  • 不支持嵌套容器。
    TArray
    、
    TSet
    和
    TMap
    在UHT中会清除自身的「可作为容器键/值」能力,因此
    TArray<TArray<int32>>
    和
    TMap<FName, TArray<int32>>
    无法编译。请将内层容器包装在一个
    USTRUCT
    中。
  • 只有当容器的元素类型(对于
    TMap
    来说还包括键类型)本身是Blueprint支持的类型时,该容器才能暴露给Blueprint。
  • FTableRowBase
    (位于
    Engine/DataTable.h
    )声明为
    USTRUCT(BlueprintInternalUseOnly)
    ;请从它派生数据表行结构体,并将派生结构体标记为
    BlueprintType
    。数据表加载与行查找请参见
    ue-data-assets-tables
    。
  • FInstancedStruct
    (位于
    StructUtils/InstancedStruct.h
    )声明为
    USTRUCT(BlueprintType)
    ,在Blueprint中显示为单个引脚,具体结构体类型可在编辑器中选择;
    TInstancedStruct<FMyBase>
    是其类型安全的C++包装器。

Delegates and Events

委托与事件 {#delegates-and-events}

Only dynamic delegates cross the Blueprint boundary. Declaration macros and native binding rules:
ue-cpp-foundations
.
  • UPROPERTY(BlueprintAssignable)
    on a multicast dynamic delegate gives Blueprint a Bind Event node; adding
    BlueprintCallable
    also gives it a Broadcast node. Both are legal only on multicast dynamic delegates.
  • Delegate parameters need names — they become the pin names on the event node.
  • Any C++ handler bound with
    AddDynamic
    must be a
    UFUNCTION()
    ; otherwise the name lookup fails at runtime and the handler never runs.
  • A single dynamic delegate (
    DECLARE_DYNAMIC_DELEGATE
    ,
    DECLARE_DYNAMIC_DELEGATE_OneParam
    , …) works as a
    UFUNCTION
    parameter, which is how async nodes take a one-shot callback:
    UPARAM(DisplayName="Event") FMyReadySignature Callback
    .
  • Native (non-dynamic) delegates cannot be exposed at all; wrap them in a dynamic delegate or an async action.
只有动态委托可以跨越Blueprint边界。委托声明宏和原生绑定规则请参见
ue-cpp-foundations
。
  • 在多播动态委托上添加
    UPROPERTY(BlueprintAssignable)
    会在Blueprint中生成Bind Event节点;再加上
    BlueprintCallable
    还会生成Broadcast节点。这两个说明符仅对多播动态委托有效。
  • 委托参数必须有名称——它们会成为事件节点上的引脚名称。
  • 任何通过
    AddDynamic
    绑定的C++处理函数都必须是
    UFUNCTION()
    ;否则运行时名称查找会失败,处理函数永远不会执行。
  • 单播动态委托(
    DECLARE_DYNAMIC_DELEGATE
    、
    DECLARE_DYNAMIC_DELEGATE_OneParam
    等)可以作为
    UFUNCTION
    的参数,这也是异步节点接收一次性回调的方式:
    UPARAM(DisplayName="Event") FMyReadySignature Callback
    。
  • 原生(非动态)委托完全无法暴露给Blueprint;请将它们包装在动态委托或异步动作中。

Calling Blueprint from Native Code

从原生代码调用Blueprint {#calling-blueprint-from-native-code}

NeedUse
A hook the designer fills in
UFUNCTION(BlueprintImplementableEvent)
, called by its plain name
A hook with a C++ default
UFUNCTION(BlueprintNativeEvent)
, called by its plain name
Fire an event many graphs listen to
UPROPERTY(BlueprintAssignable)
multicast,
Broadcast(...)
Call a function that exists only in a Blueprint asset
FindFunction
+
ProcessEvent
Read defaults off a Blueprint class
TSubclassOf<T>::GetDefaultObject()
,
UClass::GetDefaultObject<T>()
cpp
// The params struct must match the function's parameter list exactly, in declaration order.
struct FMyOnScoredParams
{
    int32 Points;
    FString Reason;
};

void MyNotifyScored(UObject* Target, int32 Points)
{
    if (!Target) { return; }

    if (UFunction* Func = Target->FindFunction(FName(TEXT("OnScored"))))
    {
        checkf(Func->ParmsSize == sizeof(FMyOnScoredParams), TEXT("OnScored signature changed"));
        FMyOnScoredParams Params;
        Params.Points = Points;
        Params.Reason = TEXT("Kill");
        Target->ProcessEvent(Func, &Params);
    }
}
FindFunction
returns null when the Blueprint was renamed or recompiled away, so always branch on it (
FindFunctionChecked
asserts instead). Prefer a
BlueprintImplementableEvent
declaration whenever C++ owns the contract — it is type-checked at compile time.
cpp
// SpawnClassPath is a UPROPERTY(EditDefaultsOnly) TSoftClassPtr<AMyActor> set to BP_MyThing
void MyInspectSpawnClass(const TSoftClassPtr<AMyActor>& SpawnClassPath)
{
    if (UClass* LoadedClass = SpawnClassPath.LoadSynchronous())
    {
        const AMyActor* Defaults = LoadedClass->GetDefaultObject<AMyActor>();
        const bool bIsBlueprintClass = Cast<UBlueprintGeneratedClass>(LoadedClass) != nullptr;   // UClass hides IsA (Class.h:4853)
        UE_LOG(LogMyGame, Log, TEXT("Default health %.1f, Blueprint class %d"), Defaults->GetHealth(), bIsBlueprintClass ? 1 : 0);
    }
}
LoadSynchronous
blocks the game thread; for streamed loads use the Asset Manager (
ue-data-assets-tables
).
UBlueprintGeneratedClass
(
Engine/BlueprintGeneratedClass.h
) is the runtime
UClass
a compiled Blueprint produces — test for it when native and Blueprint classes must be handled differently.
需求使用方式
设计师需要填充的钩子
UFUNCTION(BlueprintImplementableEvent)
,直接用函数名调用
带有C++默认实现的钩子
UFUNCTION(BlueprintNativeEvent)
,直接用函数名调用
触发多个图表监听的事件
UPROPERTY(BlueprintAssignable)
多播委托,调用
Broadcast(...)
调用仅存在于Blueprint资产中的函数
FindFunction
+
ProcessEvent
读取Blueprint类的默认值
TSubclassOf<T>::GetDefaultObject()
、
UClass::GetDefaultObject<T>()
cpp
// 参数结构体必须与函数的参数列表完全匹配,按声明顺序排列。
struct FMyOnScoredParams
{
    int32 Points;
    FString Reason;
};

void MyNotifyScored(UObject* Target, int32 Points)
{
    if (!Target) { return; }

    if (UFunction* Func = Target->FindFunction(FName(TEXT("OnScored"))))
    {
        checkf(Func->ParmsSize == sizeof(FMyOnScoredParams), TEXT("OnScored signature changed"));
        FMyOnScoredParams Params;
        Params.Points = Points;
        Params.Reason = TEXT("Kill");
        Target->ProcessEvent(Func, &Params);
    }
}
当Blueprint被重命名或重新编译后,
FindFunction
会返回null,因此必须先判断返回值再使用(
FindFunctionChecked
会直接断言)。只要契约由C++定义,就应优先使用
BlueprintImplementableEvent
声明——它会在编译期进行类型检查。
cpp
// SpawnClassPath是一个UPROPERTY(EditDefaultsOnly) TSoftClassPtr<AMyActor>,设置为BP_MyThing
void MyInspectSpawnClass(const TSoftClassPtr<AMyActor>& SpawnClassPath)
{
    if (UClass* LoadedClass = SpawnClassPath.LoadSynchronous())
    {
        const AMyActor* Defaults = LoadedClass->GetDefaultObject<AMyActor>();
        const bool bIsBlueprintClass = Cast<UBlueprintGeneratedClass>(LoadedClass) != nullptr;   // UClass隐藏了IsA (Class.h:4853)
        UE_LOG(LogMyGame, Log, TEXT("Default health %.1f, Blueprint class %d"), Defaults->GetHealth(), bIsBlueprintClass ? 1 : 0);
    }
}
LoadSynchronous
会阻塞游戏线程;流式加载请使用资源管理器(Asset Manager,参见
ue-data-assets-tables
)。
UBlueprintGeneratedClass
(位于
Engine/BlueprintGeneratedClass.h
)是编译后的Blueprint在运行时对应的
UClass
——当需要区别对待原生类和Blueprint类时,可以用它来判断。

Subsystems and Settings in Blueprint

Blueprint中的子系统与设置 {#subsystems-and-settings-in-blueprint}

  • Every
    UGameInstanceSubsystem
    ,
    UWorldSubsystem
    ,
    ULocalPlayerSubsystem
    and
    UEngineSubsystem
    subclass automatically gets a
    Get <Subsystem>
    node (
    UK2Node_GetSubsystem
    ); its
    BlueprintCallable
    members then appear on that object. No extra specifier is needed. Subsystem lifetimes and native accessors:
    ue-cpp-foundations
    .
  • UDeveloperSettings
    (
    Engine/DeveloperSettings.h
    , module
    DeveloperSettings
    ) auto-registers a Project Settings page. Declare it
    UCLASS(Config=Game, DefaultConfig, meta=(DisplayName="My Game"))
    , mark fields
    UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category=…)
    , and add a
    UFUNCTION(BlueprintPure)
    static accessor returning
    GetDefault<UMyGameSettings>()
    — Blueprint has no node for
    GetDefault
    .
    GetContainerName()
    ,
    GetCategoryName()
    and
    GetSectionName()
    are overridable to move the page.
  • UFUNCTION(Exec)
    declares a console command; the console routes to PlayerInput, PlayerController, Pawn, HUD, GameMode, CheatManager, GameState, PlayerCameraManager and GameInstance.
    meta=(DevelopmentOnly)
    compiles a
    BlueprintCallable
    out of Shipping, as
    UKismetSystemLibrary::PrintString
    does.
  • 所有
    UGameInstanceSubsystem
    、
    UWorldSubsystem
    、
    ULocalPlayerSubsystem
    和
    UEngineSubsystem
    的子类都会自动获得一个
    Get <Subsystem>
    节点(
    UK2Node_GetSubsystem
    );其标记为
    BlueprintCallable
    的成员会显示在该对象上。无需额外的说明符。子系统生命周期和原生访问方式请参见
    ue-cpp-foundations
    。
  • UDeveloperSettings
    (位于
    Engine/DeveloperSettings.h
    ,属于
    DeveloperSettings
    模块)会自动注册一个项目设置页面。将其声明为
    UCLASS(Config=Game, DefaultConfig, meta=(DisplayName="My Game"))
    ,将字段标记为
    UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category=…)
    ,并添加一个
    UFUNCTION(BlueprintPure)
    静态访问函数返回
    GetDefault<UMyGameSettings>()
    ——Blueprint没有直接调用
    GetDefault
    的节点。可以重写
    GetContainerName()
    、
    GetCategoryName()
    和
    GetSectionName()
    来调整设置页面的位置。
  • UFUNCTION(Exec)
    用于声明控制台命令;控制台会将命令路由到PlayerInput、PlayerController、Pawn、HUD、GameMode、CheatManager、GameState、PlayerCameraManager和GameInstance。
    meta=(DevelopmentOnly)
    会让
    BlueprintCallable
    函数在Shipping版本中被编译掉,和
    UKismetSystemLibrary::PrintString
    的行为一致。

Editor-Callable Functions

编辑器可调用函数 {#editor-callable-functions}

CallInEditor
— see
AMyActor::SnapToGround
above — is a
UFUNCTION
specifier, not a
meta=
key, and the function takes no parameters. It runs against the editor world, so guard anything that assumes PIE. Details customizations, editor utility widgets and asset actions:
ue-editor-tools
.
CallInEditor
——参见上文的
AMyActor::SnapToGround
——是
UFUNCTION
的说明符,而非
meta=
键,且该函数不能有参数。它会在编辑器世界中运行,因此任何依赖PIE(游戏中预览)的逻辑都需要加保护。详情面板自定义、编辑器工具控件和资产操作请参见
ue-editor-tools
。

Deprecated — do not use

已弃用 — 请勿使用 {#deprecated-do-not-use}

Do not emitUse in 5.8Source
meta=(HideAssetPicker)
meta=(HidePinAssetPicker)
UE_DEPRECATED(5.6)
in
UObject/ObjectMacros.h:1673
Delegate.ProcessMulticastDelegate<UObject>(Params)
Delegate.ProcessDelegate<UObject>(Params)
UE_DEPRECATED(5.8)
in
UObject/ScriptDelegates.h:1462
SomeClass->ClassDefaultObject
GetDefault<T>()
,
UClass::GetDefaultObject<T>()
UE_DEPRECATED(5.6)
in
UObject/Class.h:4045
GENERATED_UCLASS_BODY()
,
GENERATED_IINTERFACE_BODY()
GENERATED_BODY()
Legacy aliases in
UObject/ObjectMacros.h:802-805
Cast<IMyInterface>(Obj)->Foo()
on a Blueprint implementer
IMyInterface::Execute_Foo(Obj, …)
Blueprint implementers have no
IMyInterface
pointer (
UObject/ScriptInterface.h:135
)
Obj->Foo_Implementation()
to raise a
BlueprintNativeEvent
Obj->Foo()
The plain name is the generated dispatch thunk (
UObject/ObjectMacros.h:997
)
A C++ body for a
BlueprintImplementableEvent
Declare it and stopUHT generates the body (
UObject/ObjectMacros.h:992
)
UPROPERTY(BlueprintReadWrite)
on a private member
Add
meta=(AllowPrivateAccess="true")
, or make it protected
UHT error "BlueprintReadWrite should not be used on private members"
UENUM(BlueprintType) enum class EMyState : uint32
enum class EMyState : uint8
UHT error "Invalid BlueprintType enum base - currently only uint8 supported"
TArray<TArray<T>>
or
TMap<K, TArray<V>>
in a
UPROPERTY
Wrap the inner container in a
USTRUCT
TArray
/
TMap
clear
CanBeContainerValue
/
CanBeContainerKey
in UHT
AddNewAction
without a
FindExistingAction
guard
Check
LatentInfo.UUID
first
Engine/LatentActionManager.h:121,143
NewObject<UMyAsyncAction>()
returned without registering
RegisterWithGameInstance(WorldContextObject)
Kismet/BlueprintAsyncActionBase.h:37
请勿使用5.8版本中请使用来源
meta=(HideAssetPicker)
meta=(HidePinAssetPicker)
UObject/ObjectMacros.h:1673
中的
UE_DEPRECATED(5.6)
Delegate.ProcessMulticastDelegate<UObject>(Params)
Delegate.ProcessDelegate<UObject>(Params)
UObject/ScriptDelegates.h:1462
中的
UE_DEPRECATED(5.8)
SomeClass->ClassDefaultObject
GetDefault<T>()
、
UClass::GetDefaultObject<T>()
UObject/Class.h:4045
中的
UE_DEPRECATED(5.6)
GENERATED_UCLASS_BODY()
、
GENERATED_IINTERFACE_BODY()
GENERATED_BODY()
UObject/ObjectMacros.h:802-805
中的旧别名
对Blueprint实现者使用
Cast<IMyInterface>(Obj)->Foo()
IMyInterface::Execute_Foo(Obj, …)
Blueprint实现者没有
IMyInterface
指针(
UObject/ScriptInterface.h:135
)
调用
Obj->Foo_Implementation()
来触发
BlueprintNativeEvent
Obj->Foo()
原始名称对应的是生成的调度转接函数(
UObject/ObjectMacros.h:997
)
为
BlueprintImplementableEvent
编写C++实现
仅声明即可,无需实现UHT会生成实现体(
UObject/ObjectMacros.h:992
)
在私有成员上使用
UPROPERTY(BlueprintReadWrite)
添加
meta=(AllowPrivateAccess="true")
,或将成员改为protected
UHT错误「BlueprintReadWrite should not be used on private members」
UENUM(BlueprintType) enum class EMyState : uint32
enum class EMyState : uint8
UHT错误「Invalid BlueprintType enum base - currently only uint8 supported」
UPROPERTY
中使用
TArray<TArray<T>>
或
TMap<K, TArray<V>>
将内层容器包装在
USTRUCT(BlueprintType)
中
TArray
/
TMap
在UHT中清除了
CanBeContainerValue
/
CanBeContainerKey
标记
没有
FindExistingAction
保护的
AddNewAction
先检查
LatentInfo.UUID
Engine/LatentActionManager.h:121,143
未注册就返回
NewObject<UMyAsyncAction>()
调用
RegisterWithGameInstance(WorldContextObject)
Kismet/BlueprintAsyncActionBase.h:37

Common Mistakes

常见错误 {#common-mistakes}

BlueprintNativeEvent
without
_Implementation
, or a C++ body for a
BlueprintImplementableEvent
:
the first is an unresolved external, the second a duplicate symbol (UHT already emitted one). A native event needs
virtual void Foo_Implementation(...)
defined; an implementable event needs no definition at all.
BlueprintPure
with side effects:
the compiler prunes unused pure nodes and re-evaluates the rest at each consumer, so the side effect runs an unpredictable number of times. Use
BlueprintCallable
.
A non-const reference parameter that was meant as an input:
cpp
static void Scale(FVector& Value, double Factor);                 // WRONG: Value becomes an output pin
static void Scale(UPARAM(ref) FVector& Value, double Factor);     // RIGHT: by-reference input
static void Scale(const FVector& Value, double Factor);           // RIGHT: plain input
A library function that needs the world but declares no
WorldContext
:
GetWorld()
returns null on a
UBlueprintFunctionLibrary
CDO. Take
const UObject* WorldContextObject
, tag it
meta=(WorldContext="WorldContextObject")
and resolve with
GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
.
BlueprintReadWrite
on a private member:
a UHT error, not a warning. Add
meta=(AllowPrivateAccess="true")
.
Nested containers in a
UPROPERTY
:
UHT rejects
TArray<TArray<T>>
and
TMap<K, TArray<V>>
. Wrap the inner container in a
USTRUCT(BlueprintType)
.
A latent action added without the UUID guard: re-entering the node stacks duplicate actions and the completion pin fires once per copy. Check
FindExistingAction<FMyWaitAction>(LatentInfo.CallbackTarget, LatentInfo.UUID)
first.
An async action collected mid-flight: an action that never calls
RegisterWithGameInstance
lives only for the frame it was created on, so a delegate firing later never reaches it. Register in the factory and call
SetReadyToDestroy()
exactly once when finished.
Cast<IMyInterface>(Obj)
on a Blueprint implementer:
returns null and the call is silently skipped. Test with
Obj->Implements<UMyInterface>()
and dispatch with
IMyInterface::Execute_Foo(Obj, …)
.
A static async factory without
meta=(BlueprintInternalUseOnly="true")
:
Blueprint offers both the proxy node and a bare call to the factory, and the bare call returns an object nothing ever activates.
BlueprintNativeEvent
没有对应的
_Implementation
实现,或为
BlueprintImplementableEvent
编写了C++实现:
前者会导致外部符号未解析错误,后者会导致符号重复(UHT已经生成了一个实现)。原生事件需要定义
virtual void Foo_Implementation(...)
;而可实现事件完全不需要定义实现。
BlueprintPure
函数带有副作用:
编译器会裁剪未使用的纯节点,并在每个消费点重新求值,因此副作用的执行次数不可预测。请使用
BlueprintCallable
。
本应作为输入的非常量引用参数:
cpp
static void Scale(FVector& Value, double Factor);                 // 错误:Value变成了输出引脚
static void Scale(UPARAM(ref) FVector& Value, double Factor);     // 正确:按引用传递的输入
static void Scale(const FVector& Value, double Factor);           // 正确:普通输入
需要获取World的库函数没有声明
WorldContext
:
在
UBlueprintFunctionLibrary
的CDO上调用
GetWorld()
会返回null。请接收
const UObject* WorldContextObject
参数,添加
meta=(WorldContext="WorldContextObject")
标记,并通过
GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
来获取World。
在私有成员上使用
BlueprintReadWrite
:
这是UHT错误,而非警告。请添加
meta=(AllowPrivateAccess="true")
。
UPROPERTY
中使用嵌套容器:
UHT会拒绝
TArray<TArray<T>>
和
TMap<K, TArray<V>>
。请将内层容器包装在
USTRUCT(BlueprintType)
中。
添加latent动作时没有UUID保护: 重复进入节点会堆叠重复的动作,导致完成引脚触发多次。请先检查
FindExistingAction<FMyWaitAction>(LatentInfo.CallbackTarget, LatentInfo.UUID)
。
异步动作在执行过程中被回收: 从未调用
RegisterWithGameInstance
的动作仅在创建它的那一帧存活,因此之后触发的委托无法找到它。请在工厂函数中注册,并在完成时恰好调用一次
SetReadyToDestroy()
。
对Blueprint实现者使用
Cast<IMyInterface>(Obj)
:
会返回null,调用被静默跳过。请使用
Obj->Implements<UMyInterface>()
进行判断,并通过
IMyInterface::Execute_Foo(Obj, …)
进行调用。
静态异步工厂函数没有
meta=(BlueprintInternalUseOnly="true")
标记:
Blueprint会同时显示代理节点和直接调用工厂函数的选项,而直接调用返回的对象永远不会被激活。

Related Skills

相关技能 {#related-skills}

  • ue-cpp-foundations
    —
    UCLASS
    /
    UPROPERTY
    /
    UFUNCTION
    fundamentals, containers, delegates, subsystem lifetimes, GC
  • ue-actor-component-architecture
    — Actor and component lifecycle, spawning,
    BlueprintSpawnableComponent
  • ue-gameplay-framework
    — GameMode, PlayerController, damage, the classes Blueprint most often extends
  • ue-editor-tools
    — details customization, editor utility widgets, asset actions, editor subsystems
  • ue-async-threading
    —
    AsyncTask
    ,
    UE::Tasks
    , timers and thread safety behind an async node
  • ue-data-assets-tables
    —
    UDataAsset
    , DataTable rows, soft references and Asset Manager loading
  • ue-ui-umg-slate
    —
    UUserWidget
    C++ bases,
    BindWidget
    , exposing widget data to Blueprint
  • ue-cpp-foundations
    ——
    UCLASS
    /
    UPROPERTY
    /
    UFUNCTION
    基础、容器、委托、子系统生命周期、GC
  • ue-actor-component-architecture
    —— Actor与组件生命周期、生成、
    BlueprintSpawnableComponent
  • ue-gameplay-framework
    —— GameMode、PlayerController、伤害系统、Blueprint最常扩展的类
  • ue-editor-tools
    —— 详情面板自定义、编辑器工具控件、资产操作、编辑器子系统
  • ue-async-threading
    ——
    AsyncTask
    、
    UE::Tasks
    、定时器以及异步节点背后的线程安全
  • ue-data-assets-tables
    ——
    UDataAsset
    、DataTable行、软引用以及资源管理器加载
  • ue-ui-umg-slate
    ——
    UUserWidget
    C++基类、
    BindWidget
    、将控件数据暴露给Blueprint