如果您开始制作基于 C++ 的 UI,您将遇到的最常见问题之一是:
- 如何从 C++ 控制蓝图创建的小部件?
答案是BindWidget元属性。
基本 BindWidget 示例
UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
class UTextBlock* TitleLabel;
UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
class UImage* IconImage;
虽然UPROPERTY()文档中没有提到它,但它是作为 UI 开发人员的最有用的标签之一。
顺便说一句,请查看我的完整 UPROPERTY 文档,其中包括所有属性BindWidget和其他属性。
通过将指向小部件的指针标记为BindWidget,您可以在 C++ 类的蓝图子类中创建 同名小部件,并在运行时从 C++ 访问它。
这是使测试正常工作的分步过程:
- 创建.UUserWidget
- 在其中添加一个成员变量UWidget或它的子类(例如UImage*,UTextBlock等)
- 用 UPROPERTY(meta=(BindWidget)) 标记它。
- 运行编辑器并创建 C++ 类的蓝图子类。
- 创建一个与您的成员变量具有相同类型和确切名称的小部件。
- 您现在可以从 C++ 访问小部件。
如果这些步骤中的任何一个没有意义,请查看我关于使用 Unreal 制作 UI 的介绍系列。
示例代码
BindExample.h
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "BindExample.generated.h"
UCLASS(Abstract)
class UBindExample : public UUserWidget
{
GENERATED_BODY()
protected:
virtual void NativeConstruct() override;
UPROPERTY(BlueprintReadOnly, meta=(BindWidget))
class UTextBlock* ItemTitle;
};
BindExample.cpp
#include "BindExample.h"
#include "Components/TextBlock.h"
void UBindExample::NativeConstruct()
{
Super::NativeConstruct();
// ItemTitle can be nullptr if we haven't created it in the
// Blueprint subclass
if (ItemTitle)
{
ItemTitle->SetText(FText::FromString(TEXT("Hello world!")));
}
}
现在编译 C++ 并打开添加ItemTitle属性的 C++ 类的蓝图子类。ItemTitle如果您编译蓝图,如果您的 UserWidget中没有命名 TextBlock 小部件,则会显示错误。
使用缺少 BindWidget 标记的属性编译蓝图
如果我们现在添加一个 Text 小部件并更改其名称以匹配我们的 C++ 文件,则此编译错误将消失,并且当我们运行游戏时,文本将更改为“Hello world!”。
具有正确命名的文本块的蓝图
使用BindWidget和 C++的优缺点
为什么我们要使用BindWidgetC++ 而不仅仅是在蓝图中编写所有逻辑?
- 更容易在 C++ 中维护复杂的逻辑。蓝图中没有意大利面条式的战斗。
- 协作更轻松,无需担心锁定蓝图资产。
- 需要重新编译才能看到更改。使用 Live Coding 可以在一定程度上避免这种时间成本。
- 当逻辑从蓝图转移到 C++ 时,非程序员更难看到数据是如何填充的。
可选小部件
在某些情况下,您可能有一个基础 C++ 类和它的许多不同的蓝图子类。例如,一个基本按钮类在 C++ 中可能具有通用逻辑,但您可以创建许多不同的蓝图子类,为每种视觉样式创建一个子类。一些视觉样式可能有图标,另一些可能有文本,因此将文本和图标图像小部件设为可选是有意义的。
要使小部件可选使用meta=(BindWidgetOptional). 这样,如果 Blueprint 类没有具有该名称的小部件,则不会显示错误。
BindWidget可选示例
UPROPERTY(BlueprintReadWrite, meta=(BindWidgetOptional))
UTextBlock* ButtonLabel;
UPROPERTY(BlueprintReadWrite, meta=(BindWidgetOptional))
UImage* IconImage;
BindWidget的怪处
- 编译一个没有被BindWidget标记过的 widget 的蓝图将显示错误。但是仍然可以运行您的游戏。如果您想避免在这种情况下发生崩溃,您需要检查该变量是否不是nullptr.
- 通常,当在 UserWidget 上勾选“Is Variable”标志时,它会在编辑器的 Graph 选项卡中可用。但是,如果存在具有该名称的属性,标记为BindWidget,则使其在蓝图中可访问的唯一方法是将其添加BlueprintReadOnly或添加BlueprintReadWrite到其**UPROPERTY()**标记中。
- 默认情况下,父 C++类中定义的变量仅在选中“显示继承的变量”时才会显示在变量列表中(参见屏幕截图)。
- 用BindWidget标记过的 widget 在 C++ 构造函数中为空,它们稍后在生命周期中被初始化。如果您需要进行类似构造函数的设置,请使用该**NativeConstruct()**函数。
显示在父 C++ 类中定义的变量