UMG Viewmodel

您所在的位置:网站首页 wincc下拉菜单实现对变量赋值 UMG Viewmodel

UMG Viewmodel

2023-05-14 08:31| 来源: 网络整理| 查看: 265

本页面的内容

工作流程

必需设置

Viewmodel

创建Viewmodel

带FieldNotify说明符的变量

带FieldNotify说明符的函数

使用宏触发FieldNotify说明符

示例

向控件添加Viewmodel

初始化你的Viewmodel

创建实例

手动

属性路径

全局Viewmodel集合

访问Viewmodel的成员

使用数组

创建Viewmodel的最佳实践

视图绑定

向控件添加视图绑定

使用细节面板

使用视图绑定菜单

配置视图绑定

选择目标控件

创建视图绑定条目

选择控件属性

选择Viewmodel属性

设置绑定方向

使用转换函数

UI开发人员通常会将后端数据和视觉设计分解成独立的系统。这样做可以在构建用户界面(UI)的过程中减少破坏性,提高构建效率,因为设计人员可以在不破坏UI底层代码的情况下更改视觉呈现,程序员则可以专注于数据和系统,不需要完整的前端。Viewmodel 插件通过引入Viewmodel资产和 视图绑定(View Bindings) 为该工作流程提供了实现途径。

工作流程

Viewmodel包含可在你的UI中使用的变量。设计人员可以使用 视图绑定(View Binding) 面板将其UI中的字段绑定到这些变量,而程序员可以自行构建Viewmodel,并根据自己的需要将它们与应用程序的代码配合使用。

将Viewmodel添加到 UMG 控件后,你就可以访问它并调用函数或更新变量。Viewmodel会将更新推送到字段已绑定到其变量的所有控件。

这种替代方法比原始属性绑定更有效率,因为它只会在你更新变量时更新控件。它还具有事件驱动UI框架的优点,却无需花费实现时间手动建立框架。

必需设置

如需在你的项目UI中使用Viewmodel,请在 插件(Plugins) 菜单中启用 UMG Viewmodel **插件。

如果你不启用此插件,将无法使用 UMVVMViewModelBase 类,也将无法使用UMG中的"视图绑定(View Bindings)"。

Viewmodel

Viewmodel的主要用途有两个:

维护你的UI所需变量的清单。

提供你的UI与应用程序其余部分之间的通信途径。

需要让你的UI感知某个变量时,可以将它添加到Viewmodel,然后将该Viewmodel添加到你的控件,并将字段绑定到它。需要更新变量时,你可以获取对该Viewmodel的引用,并从该处设置它们。然后,它们会将更改通知绑定到这些变量的控件并更新控件。

创建Viewmodel

你可以在C++中,通过扩展 UMVVMViewModelBase 类或另一个Viewmodel类来创建Viewmodel。Viewmodels是 UObject ,依赖 FieldNotify 变量和函数来广播对绑定到它们的控件的更改。

在未来发布的虚幻引擎版本中,将可以使用蓝图创建Viewmodel。

带FieldNotify说明符的变量

在你的Viewmodel中定义变量时,每个变量都应该是"私有(Private)"变量,并且必须具有带 FieldNotify 说明符的 UPROPERTY 宏。用于FieldNotify变量的说明符的完整列表如下:

UPROPERTY说明符

说明

FieldNotify

向绑定到此变量的所有控件广播通知。

Setter

声明变量应允许设置其值。使用此说明符的前提是,Setter函数的名称格式为 Set[Variable Name]

Setter="[FUNCTION_NAME]"

作为Setter,但你可以提供自定义函数的名称以将其用作Setter。

Getter

声明变量应允许检索其值。使用此说明符的前提是,Getter函数的名称格式为 Get[Variable Name]

Getter="[FUNCTION_NAME]"

作为Getter,但你可以提供自定义函数的名称以将其用作Getter。

必须指定 FieldNotify 说明符,才能向控件广播值更改。具有此说明符的变量将出现在"视图绑定(View Binding)"菜单中。

你可以决定是否提供 Setter 或 Getter 说明符。如果你决定不提供其中一个,将无法在此类之外执行该运算。如果你只想针对变量本身触发通知,只需在指定Getter和Setter说明符时不附带函数名称。

自定义Getter和Setter说明符适合在以下情况下使用:

你需要在检索变量之前执行一项运算。

你希望在设置变量时触发其他函数或更新其他变量。

例如,如果你想使用 进度条(Progress Bar) 控件创建血条,就需要检索表示角色剩余生命值的百分比值,因为"进度条(Progress Bar)"使用百分比值来确定其填充程度。在这种情况下,你可以为角色的当前生命值定义一个自定义Setter,然后让它触发一个单独的Notify说明符,以返回角色最大生命值的百分比。

UFUNCTION(BlueprintCallable) void SetCurrentHealth(int newCurrentHealth) { if (CurrentHealth != newCurrentHealth) { UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(CurrentHealth, newCurrentHealth); UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent); } }

如果你创建自定义Getter或Setter函数,不要将它们设置为 UFUNCTIONS ,因为如果这样做,将在蓝图中为Get和Set函数创建冗余列表。变量的 UPROPERTY 宏已经以变量的Get和Set节点的形式提供了对这些函数的访问。你还应该将自定义Getter函数设置为 const 函数,因为它们唯一的作用应该是返回值。

带FieldNotify说明符的函数

你可以创建自定义函数来广播 FieldNotify 说明符,并且你可以按绑定到变量的相同方式将控件的属性绑定到这些函数。这样用的函数必须遵循以下要求:

必须具有带 FieldNotify 和 BlueprintPure 说明符的 UFUNCTION 宏。

不得带有参数。

必须是 const 函数。

必须只返回单个值。

例如,以下函数是一个FieldNotify函数,用于返回角色当前生命值占其最大生命值的百分比值:

UFUNCTION(BlueprintPure, FieldNotify) float GetHealthPercent() const { //检查以避免除零错误 if (MaxHealth != 0) { return (float) CurrentHealth / (float) MaxHealth; } else { return 0; } } 使用宏触发FieldNotify说明符

更改变量时,函数需要调用Viewmodel的其中一个通知宏,以将更改广播到绑定的控件。可用宏的列表如下:

ViewModel宏

说明

UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED([成员名称])

字段值改变后广播事件。

UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED_WITH_REP([成员名称])

将字段值复制到整个网络游戏,然后在字段值改变后广播事件。

UE_MVVM_SET_PROPERTY_VALUE([成员名称], [新值])

测试该字段的值是否已改变,然后为该字段设置新值,并向绑定的控件发送通知。

UE_MVVM_SET_PROPERTY_VALUE_WITH_REP)[成员名称], [新值])

作为 UE_MVVM_SET_PROPERTY_VALUE ,然后复制它,并向绑定的控件发送通知。

SET_PROPERTY_VALUE 宏的作用与 BROADCAST_FIELD_VALUE 宏基本相同,不同的是 SET_PROPERTY_VALUE 宏会在广播之前检查确认值是否已改变。这项检查在为Viewmodel创建Setter函数时很常见,将其包括在内是为了方便起见。

如果你想通知直接绑定到该值的控件, BROADCAST_FIELD_VALUE_CHANGED 宏能以变量本身为参数,否则也能以函数名称为参数。这适用于以下情况:你希望将控件绑定到的值是从其他变量派生或转换而来的值,但又不希望创建额外的变量来容纳该信息。

示例

以下代码片段是使用上述概念的Viewmodel类的示例。GetHealthPercent被定义为不同于Getter和Setter函数的独立函数,但Setter函数除了在变量本身变化时发出通知外,还会调用该函数。

UCLASS(BlueprintType) class UVMCharacterHealth : public UMVVMViewModelBase { GENERATED_BODY() public: UPROPERTY(BlueprintReadWrite, FieldNotify, Setter, Getter) int32 CurrentHealth; UPROPERTY(BlueprintReadWrite, FieldNotify, Setter, Getter) int32 MaxHealth; private: void SetCurrentHealth(int32 newCurrentHealth) { if (CurrentHealth != newCurrentHealth) { UE_MVVM_SET_PROPERTY_VALUE(CurrentHealth, newCurrentHealth); UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent); } } void SetMaxHealth(int32 newMaxHealth) { if (MaxHealth != newMaxHealth) { UE_MVVM_SET_PROPERTY_VALUE(MaxHealth, newMaxHealth); UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent); } } int32 GetCurrentHealth() const { return CurrentHealth; } int32 GetMaxHealth() const { return MaxHealth; } public: UFUNCTION(BlueprintPure, FieldNotify) float GetHealthPercent() const { if (MaxHealth != 0) { return (float) CurrentHealth / (float) MaxHealth; } else return 0; } }; 向控件添加Viewmodel

你可以在UMG的Viewmodels窗口中向控件添加Viewmodel。你可以在"UMG设计器(UMG Designer)选项卡的 窗口(Window) > Viewmodels 下找到它。

点击 + Viewmodel 按钮,选择你的项目的其中一个Viewmodel,然后点击 选择(Select) 。

初始化你的Viewmodel

当你在Viewmodels窗口中点击Viewmodel时,可以通过 创建类型(Creation Type) 设置选择如何将它初始化。可使用以下方法:

Viewmodel创建类型

说明

创建实例(Create Instance)

该控件会自动创建它自己的Viewmodel实例。

手动(Manual)

该控件在初始化时Viewmodel为null,你需要手动创建一个实例并为其赋值。

全局Viewmodel集合(Global Viewmodel Collection)

指你的项目中的所有控件均可使用的全局可用Viewmodel。需要 全局Viewmodel标识符 。

属性路径(Property Path)

在初始化时,执行一个函数来查找Viewmodel。Viewmodel属性路径 将使用句点分隔的成员名称。例如:GetPlayerController().Vehicle.Wheel[0].ViewModel。属性路径始终是相对于控件的路径。

Viewmodel与控件之间不一定存在一一对应的关系。你可用多种方法设置它们,并将它们赋值给控件,而且多个控件可以从单个Viewmodel获取信息。下文详述了每种创建类型方法。

创建实例

创建实例(Create Instance) 创建方法会自动为控件的每个唯一实例创建一个新的Viewmodel实例。这意味着,如果你在视口中有同一控件的数个副本,并且你更改了其中一个副本的Viewmodel变量,则只有该控件会更新,所有其他副本将保持不变。同理,如果你创建多个使用同一Viewmodel的不同控件,这些控件都不会感知到彼此信息的变化。下面介绍的其他方法适用于你希望多个控件引用相同数据的情况。

手动

手动(Manual) 创建方法需要你在应用程序代码的某个位置自行创建一个Viewmodel实例,然后手动将其赋值给控件。控件具有Viewmodel对象引用,但在你为它赋值Viewmodel之前,它将具有空值。你赋值Viewmodel后,就可以在想要更新UI时更新它,不用获取对控件的引用。

这样便可有机会将同一Viewmodel赋值给你的UI中多个不同的控件。

属性路径

属性路径(Property Path) 创建方法提供的替代方案可能更简洁,需要的代码支持也更少。控件并不是让其他类访问其内部来设置其Viewmodel引用,而是通过一系列函数调用和引用向外访问来获取Viewmodel。编辑器中的"属性路径(Property Path)"字段要求一系列句点分隔的成员名称,并且它假定调用这些函数的起始点是 Self 。换言之,它的起始点始终是你正在编辑的控件。

不要在你的属性路径中手动指定 Self ,因为"属性路径(Property Path)"字段已经假定你的起始点是对 Self 的引用。

例如,以下字段会获取控件的所属玩家控制器,然后获取附加到其当前控制的车辆第一个车轮的Viewmodel:

GetPlayerController().Vehicle.Wheel[0].ViewModel

你还可以调用在蓝图中定义的函数,这有助于精简属性路径的逻辑并提高灵活性。例如,以下函数会从拥有该控件的角色获取角色生命值Viewmodel:

然后你可以使用以下函数的名称作为属性路径:

GetHealthViewModel() 全局Viewmodel集合

全局Viewmodel集合(Global Viewmodel Collection)是 MVVM子系统(MVVM Subsystem) 中可全局访问的Viewmodel列表。这些Viewmodel非常适合用于处理可能需要在你的整个UI中访问的变量,例如你的游戏选项菜单的设置。如需将Viewmodel添加到蓝图中的全局Viewmodel集合,请执行以下步骤:

添加对MVVM子系统的引用。

从MVVM Subsystem节点拖出一个引脚,然后调用 Get Global Viewmodel Collection 。

从全局Viewmodel集合拖出一个引脚,然后调用 Add Viewmodel Instance 。

然后你可以构建一个Viewmodel实例,并通过此节点将其添加到集合中。你可在 游戏实例(Game Instance) 类中方便地初始化这些实例。

选择"全局Viewmodel集合(Global Viewmodel Collection)"作为初始化模式时,请通过 全局Viewmodel标识符(Global Viewmodel Identifier) 中的 Add Viewmodel Instance 节点提供 上下文名称(Context Name) 。此名称必须与Viewmodel的类名一致。例如,如果你的Viewmodel叫做VM_GraphicsOptions,你需要将其同时用作上下文名称和全局Viewmodel标识符。

访问Viewmodel的成员

将Viewmodel赋值给控件后,你可以在蓝图中将其作为控件的属性进行访问。它将位于 变量(Variables) > Viewmodel 类别下。获得对Viewmodel的引用后,你就可以访问其变量和函数。

根据Viewmodel中配置的选项,你可能无法访问所有内部函数或Setter函数。

使用数组

你通常无法访问Viewmodel中的数组。如需访问Viewmodel中的数组,请创建你自己的 FieldNotify 函数,以便直接向Viewmodel本身添加、从中删除以及获取数组成员。

创建Viewmodel的最佳实践

创建Viewmodel时,你应该秉承小而简洁而非大而庞杂的原则。这样做可以简化UI的调试。

例如,你可以创建一个表示角色扮演游戏(RPG)角色的Viewmodel,其中包含一组完整的特性、物品栏和击中点。但如需调试依赖此Viewmodel的UI部分,你首先需要生成一个完整的角色来填充Viewmodel的数据。如果你将这些拆分成不同的组件,在调试时就可以更轻松地使用测试数据填充它们。

你可以将Viewmodel嵌套在其他Viewmodel中。这适合用于表示复杂的数据集。

视图绑定

创建Viewmodel后,你可以将其添加到UMG编辑器中的控件中,并使用"视图绑定(View Bindings)"窗口将其作为目标。

向控件添加视图绑定

要向控件添加视图绑定,有两种方法可用:你可以使用"细节(Details)"面板中的"属性绑定(property binding)"下拉列表添加它们,也可以使用"视图绑定(View Binding)"菜单来管理你的所有控件的绑定。

使用细节面板

如需在"细节(Details)"面板中使用视图绑定,请选择你要添加绑定的控件,然后点击你想绑定的属性上的 绑定(Bind) 下拉列表。适用于该属性的Viewmodel变量和函数将出现在下拉列表的底部。点击一个以指定绑定。

使用视图绑定菜单

"视图绑定(View Binding)"窗口可对视图绑定的行为进行更详细的控制。在"UMG设计器(UMG Designer)"选项卡中点击 窗口(Window) > 视图绑定(View Binding) ,打开"视图绑定(View Binding)"窗口。

点击 + 添加控件(+ Add Widget) ,向"视图绑定(View Binding)"列表中添加条目。

你在Viewmodels窗口中添加的Viewmodel可以进行绑定。

在当前版本的虚幻引擎中,如果你使用"视图绑定(View Bindings)"菜单绑定Viewmodel,然后使用 细节(Details) 面板为其重新赋值,可能导致绑定失效。如需解决此问题,你应该从"视图绑定(View Bindings)"菜单中删除绑定,然后为其重新赋值。

配置视图绑定

"视图绑定(View Bindings)"包含以下信息:

绑定的 目标控件 和 目标 Viewmodel 。

你想相互绑定的 控件属性 和 Viewmodel 属性 。

绑定的 方向 ,决定两个目标属性之间的信息流。

绑定的 更新类型 。

启用/禁用 开关,用于打开和关闭绑定。

以下小节详细介绍了其中的每个字段及其配置方法。

选择目标控件

视图绑定条目的第一个下拉列表用于选择你要添加视图绑定的控件。当你点击它时,下拉列表将显示控件的层级,你可以选择父控件本身或其子控件。点击 选择(Select) 确认你的选择。

创建视图绑定条目

目标控件下面有你想设置Viewmodel绑定的各个属性对应的条目。每个绑定都从它所属的控件缩进显示。你可以点击控件下拉列表旁的 + 按钮,为单个控件添加多个绑定。每个绑定都必须以不同的属性为目标。

选择控件属性

视图绑定条目中的第一个下拉列表将显示目标控件的变量和函数列表。例如,如果你选择"进度条(Progress Bar)控件, 百分比(Percent) 属性将可用。

如需使在C++中定义的属性或函数出现在此列表中,必须通过 UFUNCTION 或 UPROPERTY 宏令其对虚幻引擎反射系统可见。蓝图定义的变量和函数自动可用。

选择Viewmodel属性

第三个下拉列表用于选择你想作为目标的Viewmodel,以及你在视图绑定时想要使用它的哪些属性。当你点击它时,它会显示你添加到该控件的Viewmodel的列表。

点击你在显示可进行视图绑定的变量和函数列表时想要使用的Viewmodel。如需使变量和函数出现在此处,它们必须具有 FieldNotify 说明符。

设置绑定方向

第二个下拉列表用于选择视图绑定的 绑定方向(Bind Direction) 。它将决定信息在控件和Viewmodel之间的流动方式。

可用的绑定方向如下:

绑定方向(Bind Direction)

说明

单向至控件(One Way to Widget)

仅执行从Viewmodel到控件的绑定。每当你更新Viewmodel中的对应变量时,它都会通知控件变量已改变,并更新选定的控件属性。或者,如果你选择某个函数,则调用该函数也会更新选定的控件属性。

单向至Viewmodel(One Way to Viewmodel)

仅执行从控件到Viewmodel的绑定。只要用户或你的代码更改了控件中的选定属性,都会将该更改应用于Viewmodel属性。典型示例包括用户编辑的文本字段或图形选项。

双向(Two Way)

绑定在两个方向上都适用。

使用转换函数

作为直接绑定到变量的替代方法,你可以选择 转换函数(Conversion Functions) 。这些函数提供了一个接口,用于将Viewmodel中的变量转译为不同类型的数据,比如将整型转换为文本。转换函数会出现在Viewmodel列表下方的Viewmodel属性下拉列表中。

当你选择一种转换函数时,用于配置该函数参数的选项列表将出现在视图绑定的下拉列表下方。

如果你点击其中一个属性的 链接(link) 按钮,可将该属性绑定到Viewmodel值。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3