在实现游戏 UI 时,我们通常不会过多考虑可维护性。收到来自艺术家的原型后将其分解成单个元素,将小部件作为占位符放在屏幕上,并在准备就绪时放入最终资源。这对于诸如HUD或菜单之类的元素非常有用,这些元素不需要太多的迭代,但是当我们需要公开更复杂的系统时,我们该怎么办?无需在每次添加新项或需要新菜单选项时都对 UI 进行不断修改,我们可以设置数据驱动的 UI,从而自动将基础数据直接耦合到界面。

1、数据驱动的用户界面

数据驱动的 UI 元素是基于某些基础数据源以程序方式构造的元素,而不是手动构建的元素。此模式的美妙之处在于,设计人员可以对 UI 公开的系统进行更改,而无需对 UI 本身进行任何调整。最大的缺点是,由于该元素仅存在于运行时,因此很难预览和精细控制它在游戏中的显示方式。

例如,想象一下游戏中的商店。界面需要显示可购买商品的列表及其价格和图标。用这些信息构建一个窗口不会太困难,但是如果设计师想要在商店中添加或删除物品怎么办?如果价格需要调整或图标艺术需要更新,该怎么办?所有这些都需要对界面进行修改,否则可能会导致界面与数据不同步。没有人愿意在游戏中用500金币购买一件物品,却发现从他们的钱包扣了1000金币!

在这篇文章中,我将描述如何设置数据表并将其耦合到商店小部件,该小部件将在滚动列表中显示任意数量的项目。我还将展示如何在做出选择时广播事件,并讨论如何扩展这些想法以满足项目需求。

2、数据驱动的UI小部件实现

任何数据驱动的UI元素中最重要的部分是数据本身,因此让我们设置一个数据表来包含商店的库存。首先,我们需要创建一个结构表示每一行包含的列。通过单击Add New,打开蓝图类,然后单击结构来创建结构。

我的设置如下图所示:

请注意,我没有创建"Name"字段,因为数据表稍后会自动添加此字段。如果需要在表中查找特定条目,我们将使用名称。

接下来,我们将通过再次单击"Add New"并展开杂项类别来创建数据表。选择之前创建的结构,然后开始添加一些条目:

现在我们已经有了数据,我们需要创建两个小部件。首先,我们需要商店主橱窗,只要商店可用,我们就会创建这个橱窗。然后,我们将创建一个 ItemRow 小部件,该小部件将在运行时创建,以表示数据表的一行。

让我们从 ItemRow开始,目标是创建一个可以自动复制和填充的通用小部件,其布局可以调整以适应不同的文本长度。

我对最大文本大小和图标大小做出了一些假设,小部件如下图所示:

请注意,我已从层级结构顶部删除了起始画布面板,因为此小部件将按其父级进行定位和调整大小。

我还创建了一个 SetValues 函数,以简化将数据馈送到模板中的过程。本来也可以使用属性绑定来获得快速简便的解决方案,但绑定会在每个渲染周期更新值。考虑到性能问题,最好避免绑定,而是设置事件以便仅在需要时更新属性。

接下来,我们将创建保存条目的橱窗小部件。此小组件将包含蓝图,用于读取数据表并为每行创建 ItemRow 小部件。可以暂时放置一些 ItemRow 微件进行可视化,但是我们希望在最终产品中有一个空的滚动框,因为这些行将由蓝图添加。

现在,我们只需要将橱窗小部件添加到视口,它就会被数据表中的最新信息自动填充!如果我们要添加、删除或更改任何条目,所要做的就是更新数据表。

3、数据驱动UI的改进

你可能已经注意到的第一个问题是,必须实际启动PIE才能查看商店界面,这意味着进行调整可能需要反复试验。幸运的是,如果你使用的是 4.15 或更高版本,我们已经为小部件蓝图添加了"Event Pre Construct"节点。这与 Construct 节点基本相同,只是它将在编辑时运行,因此我们可以在不点击播放的情况下查看小部件在预览视口中的外观。只需断开用于构建行的蓝图与"Construct"节点的连接,并将其附加到"Event Pre Construct"节点即可。

请注意,如果修改数据表,则需要点击编译以重建预览小组件,即使按钮仍显示通常的复选标记。

同样重要的是要记住,尽管连接到Event Pre Construct的节点仍将像以前一样在运行时调用,但现在也在预览小部件上的蓝图编辑器中调用它们。你需要坚持使用修饰代码,并避免引用在运行时之前不存在的任何内容。

另一个要考虑的因素是,即使我们目前显示的是任意物品列表,也需要一种方法在点击按钮时通知其他游戏系统(例如角色控制器),以便我们可以扣除金币并将物品添加到玩家的库存中。为了尽可能地保持模块化,我喜欢在顶层小部件上创建一个事件调度程序,作为单个条目和任何想要在按下特定按钮时接收事件的人之间的中介。你的 ItemRow 将需要一个句柄返回到根小部件,以便它可以触发该事件,我们将通过覆盖 ItemRow 的 OnMouseButtonDown 来调用该事件。

任何感兴趣的各方都可以将自定义事件绑定到根小组件的事件调度程序,接收单击项目的名称作为参数,并在数据表中执行自己的查找以获取完整详细信息。

同样重要的是要了解,示例中使用的数据表只能在编辑时进行修改。在某些情况下,你可能需要一个更动态的数据结构,可以在游戏过程中进行修改。你甚至可能希望使用数据表作为数据的起始状态,将其复制到另一个结构,然后可以根据需要对其进行修改。为了获得最大的多功能性,你可以创建一个具有任意属性和一些元数据的数据结构,以告知 UI 如何显示这些属性。值得花一些时间预先确定哪种解决方案最适合自己,而不是遇到意想不到的限制。

4、最后的思考

虽然某些UI元素的变化不足以保证设置数据驱动小部件所需的额外时间和精力,但许多系统可以从它们提供的灵活性中受益。快速迭代对于良好的游戏设计至关重要,以程序方式生成界面可以让你立即从想法到执行。


原文链接:Creating a Data-Driven UI with UMG

BimAnt翻译整理,转载请表明出处