zhen's blog

编译思想,编译人生

【翻译转载】如果你想要构建一套基于ECS的GUI框架

译者序

本文原文地址:So you want to build an ECS-backed GUI framework | Leafwing Studios (leafwing-studios.com)。翻译和发布本文前,已经获得了原作者的许可。

本人翻译这篇文章的主要是因为尽管该文是从 bevy_ui 这一具体的库出发,但其核心是讨论GUI框架的设计思路与范式,同时还介绍了很多已有成果,个人觉得如果有读者对基于Rust的GUI建设感兴趣的话,一定会从这篇文章中学到很多有价值的内容。当然,笔者只是利用自己欠佳的英语水平结合翻译软件来对原文进行了翻译,所以如有翻译上的问题,还请读者指出,感激不敬!

此外,这篇文章由原作者于23年11月编写,当时 bevy 正值0.12版本,在翻译这篇文章的时候已经是24年的8月底,bevy的版本已经更新到了0.14,所以文中关于 bevybevy_ui 的一些建设工作(如PR、讨论等)不一定具有时效性。

原文标题为:“So you want to build an ECS-backed GUI framework”,从文章上下文理解看来,实际上应该是"So (if) you want to build an ECS-backed GUI framework"

正文

Challenges and opportunities in the future of bevy_ui

关于 bevy_ui 未来的挑战与机遇

Alice I. Cecile, Rose Peck | 2023-11-27

如果你想要通过Rust(以及其生态)构建一个用户界面,那有什么工具能比实体-组件-系统(ECS)框架更合适呢?它不仅提供了一种类型安全的、流行的状态管理方案,而且最重要的是:这套体系的速度将非常快(显然无需进行基准测试)。

当然,Bevy 确实在这么做!实际上,它已经这样做了好几年。那么,为何它没有在竞争中脱颖而出,赢得数百万人的心,并取代 areweguiyet.rs 呢?

译者注:Are we GUI yet?网站陈列了目前已知的所有基于Rust绑定或实现的GUI框架。这里的“取代”主要含义是为什么Bevy的基于ECS的GUI框架为什么做的并不出众,没有成为某种优秀的范例,进而让基于Rust的GUI框架结束百花齐放的局面。

虽然基于ECS模式的GUI可能并不是传统方式,但现有技术表明这并非不可能。flecs这篇文档中将许多核心思想进行了实现,同时也像 bellybevy_lunexbevy_ui_dslcuicui_layout以及kayak_ui这些库进行了这方面的尝试,使得Bevy的ECS(模式)显现出了巨大的潜力。甚至还有一个名为Polyphony的,独立的,ECS先行的,基于Javascript编写的GUI库(关于它的讨论:Polyphony ECS GUI and future · Issue #1 · traffaillac/traffaillac.github.io)。

事实证明,困扰bevy_ui的大多数问题都不是由、由于使用ECS甚至使用Rust的决定引起的,而是一些无聊、乏味和令人沮丧的内容:编写GUI框架是一项涉及大量容易变动的工作。Bugs、模板代码和缺失的功能击碎了用户和开发人员逐步改进优化代码的意愿。

但在我们深入讨论繁杂的细节之前,有一个重要的免责声明。Alice是Bevy的维护者(译者注:同时也是本文的作者),但不是项目负责人,甚至不是UI方面的专家。Rose是Foresight Spatial Labs的一名员工,她日常工作是使用Bevy和传统的web框架(React)来构建重GUI的应用程序。本文相关的论点、意见纯粹是我们自己的,不是最终定性的或官方的话!

这篇文章旨在记录如何制作一个GUI框架,为什么我们要使用ECS,以及我们需要修复哪些部分才能使 bevy_ui 变得更优秀。(关于这些内容)我们在很多地方(这里这里这里,以及这里)都有过很多重复的讨论,但很少有实质性,落地的实践活动(除了ickshonpe ,you rock)(译者注:这位icksphonpebevy_ui 的部分提交了大量的优化、修复代码)。说“bevy_ui应该像我最喜欢的ui框架一样工作”很容易,但实际上将其转化为可行的设计、达成共识并其构建出来却要困难得多。

通过编写一份关于需求、愿景和进展的最新、全面、通俗易懂的文档,我们希望Bevy社区能够团结起来解决 bevy_ui 今天遇到的问题,彻底排除各种可能性,并为关键的缺失部分提出可靠的设计。

谁知道呢?也许十年后,你正在阅读这篇文章,梦想着编写自己的基于ECS驱动的GUI框架。

在我感到非常疲惫、厌倦的过往中,有三类常见的关于 bevy_ui “脱轨”(译者注:原文为"get derailed")的讨论:

  1. Bevy应该使用一套现有的GUI框架。
  2. 一个同时适用于游戏和应用程序的GUI框架是不可能的。
  3. 您无法在ECS中构建GUI框架。

为什么不直接用egui(或是dioxus,或是tauri,或是iced,或是yew)?

现在已经有非常多的基于Rust的GUI框架,其中一些甚至一直在积极地维护,编写文档以及增加基本功能!

社区已经为其中一些制作了出色的crates,像Foresight这样的公司甚至使用这些第三方GUI框架制作了复杂的生产应用程序

Bevy想编写我们自己的ui实现,显然是"非我发明"综合症的典型案例(译者注:重复造轮子)。当我们可以使用现有的解决方案来编写即将到来的 Bevy Editor 时,为什么要将稀缺的精力(和决策)用于此?毕竟,我们可以与Dioxus完成一次正式的合作,以此省下很多年的工作量。

然而,以下是我们基于技术以及社会方面考虑的,认为Bevy不应该这样做的原因:

  1. 与引擎的其他部分保持一致性是非常有价值的:
    1. 为新用户提供了更简单、更一致的学习上的体验。
    2. 这样做使得系统更容易维护。
    3. 将所有更改保存在同一个代码仓库中,从而消除了在存在依赖树的情况下,对于版本发布要更加小心谨慎处理的必要。
    4. 一致性让UI模块能够从引擎其他模块的改进中也受益,反之亦然。Cart相信许多挑战并非是UI模块独有的,我们对此表示赞同!
  2. Bevy已经为GUI库需要完成的许多核心任务提供了一个很好的解决方案。
    1. 渲染、状态管理、资产asset、输入、窗口、异步等等。
    2. 为什么我们要再次采用重复的、或许微妙不兼容的方法来完成这些任务?
  3. 将数据发送到外部UI框架和从外部UI框架接收数据本身就容易出错,逻辑会变得复杂,难以维护,并且充斥着很多样板代码。
    1. 我们不可避免的需要提供集成层来面对不匹配的数据模型。
    2. 这并不是UI模块独有的:bevy_rapier在物理方面也遇到了类似的问题(尽管它仍然是一个优秀的库)。
  4. 打破目前“屏幕上的盒子(boxes on a screen)”这一UI标准的设计思路,将会使得现状变得更加困难。
    1. 世界空间UI(World-space UI)是游戏的一个关键功能,涉及到:单元叠加(unit overlays)、VR菜单、计算机屏幕等等。
    2. 游戏UI往往希望与游戏世界状态紧密结合,并具有不同寻常的艺术效果
    3. 使用第三方解决方案编写自定义着色器来覆盖某些节点的行为会变得更加困难。
  5. 现有的Rust GUI项目都没有很好地回答解决一个事实:借用检查器非常讨厌这样数据结构以及讨厌拆分(数据)可变性。
    1. 通过添加关系relations,Bevy保证了一种在Rust中处理图数据关系的独特而强大的方法。
    2. Bevy的系统是一个灵活的、无panic的、快速且可靠的解决方案,用于共享对世界状态的可变访问。这背后有很多黑魔法,亲爱的上帝,我们不想解释两次了(译者注:可以在这里了解)。
  6. 其他项目不由Bevy项目运营。
    1. 我们的目标可能会有所不同:例如,egui是专注于简单、快速构建的UI,为了达到这一要求,它需要在性能和可定制性之间做出权衡取舍。
    2. 改动会变得更难协调:我们需要迁移PR,并且无法快速添加编辑器所需的功能。
    3. 上游的依赖库可能(再次)被废弃。如果Bevy计划继续存在几十年,所依赖其他库的UI解决方案也会一并存在吗?
    4. 我们无法保证某一个关键依赖项的质量。
    5. 它给那些较小的所以依赖的第三方库带来了很大的维护压力,因为会让如此大的客户端向它们发出优化、fix的请求。
  7. 许多建议提到的第三方GUI库由于它们通常依赖于C、C++或JavaScript等其他语言生态,使Bevy的构建和分发过程变得非常复杂。
  8. 不要太苛刻,但很多现有的Rust GUI解决方案,只是不是那么完美。
    1. 虽然我们有很多能够接受的选择,但它们都有不小的缺点。没有人真正成为赢家。
    2. Are we GUI yet?说“根不深,但种子已经播下”(“The roots aren’t deep but the seeds are planted.”)是有原因的。
    3. 在内心深处,我们都知道我们可以做得更好,并且我们应该做得更好。
  9. 喜欢第三方GUI解决方案的用户可以并且无论如何都会使用它们。

我们会学习其他GUI框架吗?当然。我们会正式大规模采用它们吗?绝对不会。

一套GUI框架来统领所有?

在讨论 bevy_ui 时,另一个常见的友善问题是“我们真的能用一个UI框架来满足所有用户的需求吗”?

我看到了一些潜在的分歧:

我相信你能想到更多,分歧很容易出现,也很有趣!理论上,我们可以参考Unity(译者注:原文“pull a Unity”,但译者觉得可以理解为参考Unity,意思是“再搞一个类似的”),并在Bevy中创建多个相互竞争的UI框架。但我们认为这将非常糟糕,因为:

  • 这对用户来说非常困惑。
  • 它分散了开发人员的注意力。
  • 对于用户来说,很难权衡到底要使用哪种解决方案。
  • 在两个相互竞争的解决方案之间进行迁移会非常痛苦。
  • 在同一个项目中使用多个解决方案从根本上是站不住脚的。
  • 需要两倍的时间(如果你幸运的话)。

幸运的是,“在多个用户群体的不同需求中找到正确的所需的东西”并不是UI独有的问题。我们有很好的工具在架构层面管理这一点:

  • 这个问题其实算是一种假设,因为实际上已经在web开发上得到了解决:
    • 我们不会争辩说web UI是否是有史以来最伟大的UI解决方案(它有许多明显和不明显的缺陷)。
    • 但事实上人们已经成功构建了几乎任何一种你能想到的使用HTML/CSS/JavaScript来搭建的UI:网页、代码编辑器、游戏(浏览器和独立版)、CAD应用程序、终端等。有一个常见的笑话是“未来一切都是chrome(<化学元素>铬,但实际指Google的Chrome浏览器)”(谢谢Electron
    • 值得说明的是,web UI技术栈并不是为大多数用例设计的。可以说,它不是为他们中的任何一个单独设计的!
  • 模块化:确保用户可以移除或留下解决方案中某些他们不喜欢(或喜欢)的部分。
    • 组件、系统、插件和Rust库crate的feature特性都非常适用于模块化!
    • 第三方UI库如今存在着,并将继续存在。
  • 可扩展性:确保内部构件可访问且可构建。
    • 公共组件以及资源真的很有用。
    • 想象一下,一个基于 bevy_ui 的可交互扩展库组成的丰富生态系统,所有这些库都建立在我们的核心渲染、交互和布局规范之上。
  • 抽象设计中的渐进式披露(Progressive Disclosure)。
    • widget部件是由节点构建的。
    • 节点只是实体。
    • 在整个过程中,没有什么能阻止你在较低的层次进行hook(译者注:Hook)。

如果用户可以将相同的ECS和渲染工具用于从像素艺术平台到单元着色视觉小说(cell-shaded visual novels)再到PBR竞技场射击游戏的一切实现,那么我们就可以创造出一个足够灵活、舒适的,适合每个人的UI解决方案。

ECS中的GUI:bevy_ui实际上是如何工作的?

解决了这些常见的反对意见后,我们有望开始讨论如何实际构建我们的UI框架。让我们考虑一下我们的实际产品需求,这样我们就可以看到 bevy_ui 的不足之处。

不幸的是,对我们来说GUI框架是极其复杂的“野兽”。其中某些部分是如此重要,以至于将它们排除会破坏整个系统:

  1. 存储节点树(Storing a tree of nodes)
    1. 几乎每个优秀的UI范式都有一个或多个嵌套的元素树。
    2. “节点”是这样的元素:UI中最小的不可再分割的原子。
    3. 你需要把这些数据存储在某个地方!
    4. bevy_ui 中,节点树存储在世界World中:每个节点都是一个具有node组件的实体。
    5. UI实体通过使用父组件和子组件连接在一起。
  2. 布局(Layout)
    1. 一旦有了一组节点后,我们希望能够描述它们在屏幕上的位置。
    2. 只是简单地指定绝对大小和位置并不是很稳健:当添加、删除节点,或屏幕大小更改时,这样的布局可能会遭到破坏。
    3. bevy_ui 中,通过Style组件指定布局(这名称得怪CSS,抱歉)。
    4. bevy_ui 使用了taffy(Alice帮助维护!):它支持flexbox弹性盒子css-grid网格的布局策略。
    5. 如果你不想受到Web布局算法的约束,morphorm(在我们看来)是一个更好的选择。
  3. 输入(Input)
    1. 以键盘按压、鼠标点击、鼠标移动、触摸屏点击、游戏手柄输入等形式收集用户输入。
    2. 通常与“拾取(picking)”相匹配:根据位置找出鼠标指针事件关联的元素。
    3. 理想情况下,为涵盖悬停、按下、释放和长按按钮等操作设计一套不错的抽象概念。
    4. bevy_ui 依赖于 bevy_input,而 bevy_input 又从 winitgilrs 这些底层库中获得数据。
  4. 文本(Text)
    1. 将字符串转换为像素以便于我们能够将它们绘制到屏幕上。
    2. 在所包含文本的UI节点的边界内排列这些文本。
    3. 精确的像素对渲染很重要,但大小尺寸作为节点布局的输入同样也重要。
    4. bevy_ui 当前正使用 glyph_brush 进行文字字符渲染。
    5. cosmic-text 对非拉丁文字有更好的塑形(shaping)支持。
  5. 窗口管理(Window management)
    1. 创建一个(或n个)窗口来绘制UI。
    2. bevy 使用 winit,你也应该用它。
  6. 渲染(Rendering)
    1. 将UI的元素绘制到用户屏幕上。
    2. Bevy使用 bevy_render,而其内部进而使用 wgpu
    3. 如果你正在构建自己的Rust GUI框架,试试vello
  7. 状态管理(State management)
    1. 保持对UI状态的持久性跟踪。
    2. 填充的文本内容,单选按钮,动画进度,菜单是打开还是关闭,暗/亮模式等。
    3. bevy_ui 中,状态作为组件存储在实体上(或者少量的状态作为全局资源)。这工作得非常好!
  8. 数据传递(Data transfer)
    1. 将数据从UI传输到其他数据结构进行存储,反之亦然。
    2. 在Bevy的上下文中,“其他数据存储”是存储您所有游戏/应用程序状态的ECS World世界。
    3. 数据绑定是一种用于自动化此过程的抽象:自动和精细地进行数据的变更传递。
    4. 目前,bevy_ui使用系统(system)在world其他部分来回发送数据。

在此基础上,您可能希望 bevy_ui 添加如下的功能:

  1. 导航(Navigation,GUI页面上元素导航)
    1. 以有原则的离散的方式浏览GUI菜单元素:“tab键”是常见的键绑定。
    2. 导航对键盘和游戏手柄都非常有用。
    3. 传统应用程序的重要可访问性功能(accessibility,a11y)。
    4. bevy_ui 对这块还没有第一方的解决方案。
  2. 样式(Styling)
    1. widget部件和节点具有大量主要是样式性的属性。
    2. 我们希望确保我们的应用程序具有一致的外观和体感,并能完成快速更换。
    3. 对于应用程序(尤其是移动应用程序)来说,我们期望能够达到原生外观和体感。
    4. 样式可能采取以下形式达成实际效果:
      1. 级联继承(如CSS)。
      2. 选择器(如CSS中的选择器,或您可能使用在 bevy_ui 中提供的查询能力编写选择器)。
      3. 全局主题,例如白天明亮模式以及夜间暗黑模式。
      4. widget部件支持某些特定样式。
    5. 样式通常需要有可预测的组合规则:当多个样式同时影响一个元素时会发生什么?
    6. bevy_ui 目前没有任何第一方的抽象设计。
  3. 可组合、可重用Widget部件的抽象设计(An abstraction for composable, reusable widgets)
    1. 即使是简单的部件类型(单选按钮、文本输入框),代码编写使用起来也相当复杂!
    2. 用户应该能够一次性编写这些代码,然后在整个项目中重用(reuse)它们,从而提高开发效率和以及保持UI一致性。
    3. 部件可以由一个或多个节点/元素组成。
    4. 每个部件的节点数量可以动态变化:想想不断增长的待办事项列表。
    5. 部件需要能够接受参数来更改其内容或行为。例如,创建一个具有可自定义文本的可重用按钮。
    6. bevy_ui 目前使用Bundle类型,但由于无法处理多个节点,因此算是较为失败。
  4. 动作行为抽象(Action abstractions)
    1. 撤销-重做。
    2. 可重新绑定热键。
    3. 命令选项板。
    4. bevy_ui 对此没有第一方解决方案,甚至第三方解决方案也不成熟(抱歉!)。
  5. 可访问性(Accessibility)
    1. 为UI创建并暴露机器程序友好的API:读取状态、控制渲染/显示、发送输入并检测这些输入更改时会发生什么。
    2. 通常能够对键盘导航进行hook。
    3. 提供API供屏幕阅读器等工具使用,这些工具提供了一个可供选择的用户界面,以满足残疾用户的需求。
    4. bevy_a11y hook到accesskit,你的GUI框架也应该如此。
    5. 关于可访问性,存在很多事项需要讨论,但不幸的是,我们在这里没有具体事项统计。
  6. 本地化(Localization)
    1. 存在不止一种用户语言:你需要一种方法来变更UI元素(尤其是文本),以满足喜欢不同语言的用户的需求。
    2. 有些语言是从右向左而不是从左向右阅读的,如果不考虑这一点,某些UI设计往往会倒退(backwards)。
    3. 图标和表情符号在不同的地方也有不同的文化含义。
    4. 说真的,用fluent就行了。
  7. 资产管理(Asset management)
    1. UI经常使用预先渲染的图像或图标作为视觉效果,尤其是在游戏中。
    2. 你可能想要自定义的UI的效果和图标等,或者以自己的方式显示图像和视频。
    3. bevy_ui 使用bevy_asset来完成它
  8. 动画(Animation)
    1. 小动画,特别是一些UI元素发生变化时的小动画,可以显著提高UI的精致度和丰富性。
    2. 折叠/展开上下文菜单,滑动抽屉,旋转加载图标,淡入/淡出等动画效果。
    3. bevy_ui 理论上与bevy_animation集成在一起,但集成还没有完善(译者注:翻译这篇文章的时候,已经集成发布了)。
  9. 调试工具(Debug tools)
    1. 渲染后快速检测,修改UI节点树。
    2. 调试工具对于定位bug和调试过程中摆弄(twiddling)样式非常有用。
    3. bevy_ui 对此没有解决方案,但 bevy_inspector_egui 做得很好。
  10. UI序列化(内存中对象到文件)和反序列化(文件到内存中对象)(UI serialization (in-memory object to file) and deserialization (file to in-memory object))
    1. 如果我们可以根据存储在文件中的定义并构建UI,那么我们可以:
      1. 使得外部工具(如游戏编辑器)更容易构建UI。
      2. 让客户端用户更容易自定义UI(想想Greasemonkey和游戏模组)。
      3. 构建调试工具会更容易实现和使用。
      4. 减少编译时间:只需热更新UI资产。
      5. 允许完全控制用于定义对象的格式和语法(译者注:比如创造DSL)。
      6. 提供了更好的模块化工具的潜力,可以在不修改源代码的情况下创建更高级别的抽象和自动化迁移
    2. 在游戏中,这被称为“数据驱动”方式。
    3. bevy_ui 当前使用场景scenes(来自bevy_scene)来实现UI序列化和反序列化。
  11. 异步任务(Asynchronous tasks)
    1. 有的任务工作是由UI触发的,但是需要很长时间才能完成。
    2. 当发生上述情况时,你肯定不希望你的程序处于一直UI冻结的状态。
    3. bevy_ui 中,使用 bevy_tasks 来实现。

为什么 bevy_ui 很糟糕?

通过hook到功能齐全(但尚未完成)的游戏引擎Bevy,bevy_ui 实际上在大多数这些领域都有初步的解决方案!

那么,为什么绝大多数人认为它比Bevy更像Bavy呢(more Bavy than Bevy)?在听取和整理了用户使用 bevy_ui 的体验后,以下是 bevy 0.12版本中,按照对用户体验的主观印象进行排列的,关于 bevy_ui 的关键问题:

  1. 生成具有大量自定义属性的实体需要大量的样板:

    1. 无限嵌套的以及随处可见的..Default::default()
    2. 当处理树中排列的多个实体时,情况会变得更糟。如前所述,你不能在此场景使用bundle。
    3. 数据驱动的工作流并没有被广泛使用,因为Bevy的场景冗长且文档不足。
  2. Bevy需要为UI部件拥有一套真正的抽象:

    1. 并非所有widget部件都可以有意义地表示为单个实体。
    2. Bevy提供了很少的预构建好的widget部件:我们只有按钮和图像。
    3. 因为我们缺乏标准化的抽象,即使添加最简单、最有用的widget部件也会引起争议并陷入困境。(需要明确的是,这不是审稿人或作者的错)
  3. 在schedule中使用系统不太适合数据绑定:

    1. UI的行为几乎总是一次性(one-off)的或非常离散稀疏的(very sparse)。(译者注:而ECS体系下的系统总是每一轮都在进行执行)
    2. 从UI侧启动的任务要么通常很小,要么通常将其放入异步任务池中。
    3. 我们真的希望能够引用一个单一的特定实体及其父实体和子实体。
      1. 解决这个问题需要创建几十个标记组件(Marker Component):几乎每个按钮、文本框、图像、容器等都有一个。
    4. 99%的时间,这些处理UI事件的系统不会工作,比较浪费时间,因为schedule模块必须不断轮询以查看是否需要做任何事情。
  4. 在 bevy_ecs 中管理和遍历层次结构(向上或向下)真的很糟糕:

    1. Relations关系管理短时间还不会实现。
  5. Bevy的UI输入处理非常的原始:

    1. 用于处理指针输入的Interaction交互组件使用起来非常有限了。
    2. 对移动端多点触控支持也非常有限
    3. 键盘和游戏手柄导航目前是缺乏支持的(译者注:翻译此文章的时候已经支持了)。
    4. 对于可配置的按键绑定,没有第一方动作行为抽象支持。
    5. Bevy的“picking选取”支持非常简单,且不容易扩展到非矩形元素或世界空间中的元素。(bevy_mod_picking加油!)
  6. Flexbox弹性盒子布局(以及小范围的CSS Grid网格)很难学习,有令人沮丧的边缘案例情况,还有糟糕的API。你能解释一下flex-basis是什么吗?

  7. 由于不久前的修复错误bevy_ui 中的字体渲染有时非常难看。

  8. Bevy缺失了样式抽象:

    1. 如今我们已经完成了实现:只需修改组件即可!
  9. bevy_ui 添加非常规的视觉效果太难了:

    1. 我们缺少圆角:这对美观的UI至关重要。(它们目前在UI方面非常流行。我们可以等几年让它们过时,但无论如何,它们都会在几年后回来。)
    2. 我们也没有阴影,但没人在乎。
    3. 我们缺少九宫格布局支持(nine-patch support):这对美观但也灵活的基于资产定义的UI至关重要。
    4. 在Bevy 0.12的UI材质(功能发布)之前,没有另外一条路可以让你在 bevy_ui 中添加自己的渲染抽象。
  10. 用纯代码或通过手工编写场景文件来构建UI可能会很痛苦且容易出错:如果有一款可视化编辑器将会很棒。

  11. 世界空间的UI的支持程度很差,并且使用了一套完全不同的工具

    1. 这对于游戏(生命条、单位帧)至关重要,对于GIS或CAD应用程序中的标记和标签等也非常有用。
  12. bevy_ui没有对动画的一级支持。

  13. bevy_ui节点都有TransformGlobalTransform 组件,但不允许开发者使用它们。

  14. 在Bevy中处理异步任务的人体工程学设计是令人沮丧的:需要对执行的任务进行手写代码跟踪和轮询太多。

在这些问题中,只有1(实体生成样板代码过多)、2(widget部件的抽象不足)、3(系统不适合UI事件回调)以及4(UI节点层次结构处理令人痛苦)是由于我们选择使用ECS架构而引起的。其余的都是标准的GUI问题:无论你使用什么范式,都需要解决这些问题。关键的是,每一个与ECS相关的问题都是Bevy应该为其他用例修复的:

  1. 生成自定义实体(尤其是实体组合)对于常规的游戏代码来说很糟糕,场景scene也不是最佳实践。例如,我们生成一个玩家及其所有武器。
  2. Bevy缺少一种涵盖多实体层次结构的代码定义级别的抽象:bundle也支持的不够好。
  3. 一次性系统适用于各种定制的复杂逻辑,我们需要创造一套开发模式来有效地使用它们。
  4. Bevy的对于处理层级继承等部分的实现从根本上来说是缓慢、脆弱和痛苦的。Relations关系设计需要首要一级支持。

ECS和GUI之间没有根本上的模式不匹配或架构不兼容。bevy_ui 并不是一个有根本缺陷的概念设计,只是它的ECS部分的基础还不够好。

bevy_ui的前进之路

bevy_ui 真正强大还有很长的路要走,但我们可以一步一步脚踏实地。尽管我们面临着一些悬而未决的问题,以及即将对核心组件进行重写的事项,但这并不意味着 bevy_ui 中的所有内容都应该被毁灭移除。GUI框架涉及大量复杂的,独立的子组件:一个领域模块的改进不会因其他领域模块的重写而无效!

我们可以把要做的工作分为三类:毫无争议的部分,有争议的部分以及待研究的部分。

没有争议直截了当的部分只需要解决掉它们即可。这些任务可能很容易,也可能比较困难,但对于如何或是否应该这样做,不应该有太多的分歧。就目前,这类事项包括:

  1. 检视以及合并UI圆角支持
  2. 检视以及合并九宫格布局支持
  3. 检视以及合并实现动画插值与混合能力的Animatable特性
  4. 检视以及合并winit更新,它包含了对各种BUG的修复以及功能优化。
  5. 最后,检视并合并将ab_glyph迁移为cosmic-text的PR,该部分解锁了系统字体和复杂字体的使用。
  6. 添加对世界空间UI的支持,开始着手查看和合并基于摄像机驱动的UI的PR
  7. 添加对UI不透明度变化的支持。
  8. 添加更多关于 bevy_scene 的文档、示例和测试,使其更容易扩展和学习。
  9. 为Bevy中的多点触控输入添加更好的示例和功能。
  10. 改进Bevy中关于处理异步任务的人体工程学设计。
  11. 在taffy中添加Morphorm和/或cuicui_layout布局策略,并在Bevy中暴露出来。
  12. 添加数十个widget部件(但目前由于围绕良好的widget部件抽象还未达成共识)。

有争议的事项是那些我们对其具备清晰的理解和广泛的共识,但完成它们会产生重大的架构影响的事项,比如:

  1. 创建一套样式抽象设计,使其通过修改组件值来完成工作:
    1. Alice写了一个非常古老的RFC来说明这是如何工作的,bevy_kot提供了一种样式级联的方式,viridia的quill的实验也有一个很好的提案。
  2. 上游的bevy_fluent,将会在bevy项目的保护之下进行长期的维护。
  3. 添加对键盘以及游戏手柄的导航支持,并将其加入到 bevy_a11y 中。
  4. 如何处理指针事件和状态添加适当的抽象
  5. 优化并实现Cart的bsn提案,以提高场景的可用性:
    1. 这是受到现有作品(如cuicuibellypolako)的启发并与之密切相关。
  6. 添加类似bundle的抽象,但适用于多级层次组合:
    1. 添加一个bsn!宏,以便更容易实例化Bevy实体,特别是使用较少样板代码的实体层次结构。
    2. 添加一种通过派生宏从结构体生成这些多层次实体的方法。
    3. 现有技术包括bevy_protomoonshine-spawn
  7. 添加插值颜色的方法以提升UI动画效果。
  8. 创建一个UI特定的变换类型,以实现更快的布局和更清晰,类型更安全的API。
  9. taffy 中添加在单个节点树中的混合布局策略的支持。
  10. bevy_easingsbevy_tweening这两个库完成后,添加对动画缓动(easing)/tween的支持。
  11. 使用上游的leafwing-input-manager库,用于提供对按键绑定的抽象。
  12. 使用上游的bevy_mod_picking库,解锁高性能、灵活的元素选择。
  13. 实现Relations,并将其应用到 bevy_ui 中。

研究任务需要具备相关重要的设计专业知识,需要仔细考虑截然不同的提案,可能没有明确的要求需要干嘛:

  1. 定义并实现一个标准的UI widget部件抽象
    1. 可组合的:widget部件可以与其他部件组合以创建新的部件类型。
    2. 灵活的:我们应该能够使用这种抽象来支持从按钮到列表再到选项卡视图的所有内容。
    3. 可配置的:用户可以更改widget部件某些重要属性来达成效果,而无需重复创建自己的类型。
    4. 或许可以映射到一个或多个Bevy实体,使用普通系统就可以对widget部件进行动态更新。
    5. 可在Bevy场景之间进行序列化。
  2. 弄清楚我们希望如何处理UI行为(和数据绑定),以避免因为使用ECS中的系统system而卷入其他的问题:
    1. 这是Alice创建一次性系统的最初动机。
    2. 事件冒泡和各种各样(quillbevy_kot)的响应式UI尝试(futures-signalsbevy_rxu4)似乎是有趣的潜在工具库。
    3. 虽然并不总是直接适用,但Raph Levien在Xilem上的帖子很有趣,值得一读。
    4. 数据模型是一项关键的挑战:很容易陷入所有权问题。
  3. 弄清楚如何将数据绑定逻辑集成到Bevy场景中:
    1. Callback as Asset这个PR看起来很有希望。
    2. Vultix提出了一种用.bsn文件定义数据绑定的语法和策略。
  4. 构建Bevy编辑器,并实现使用编辑器构建GUI场景的能力:
    1. 这里存在一种“循环依赖”:bevy_ui 越好,编辑器构建起来就越容易(,编辑器构建越容易,使用 bevy_ui 构建GUI就越好用)

显然,还有很多工作要做!但关键的是,并没有什么是完全不可能的。如果我们(Bevy开发者社区)能够团结起来,一次一个稳步地解决这些问题,我们(Alice和Rose)真的认为 bevy_ui 总有一天会达到我们对引擎其他部分所期望的质量、灵活性和人体工程学的标准。

感谢您的阅读:希望它具有教育意义、发人深省和/或有趣。如果你想在未来阅读更多这样的内容,可以考虑注册我们的电子邮件列表或订阅我们的RSS

译者写在最后

尽管有翻译工具的帮助,但是为了读懂原作者的意思,还是需要结合Bevy这个库以及ECS概念。翻译这篇文章也花了一定的时间,感谢读者的耐心阅读。

本文也会同步发布到本人的博客上:zhen.blog,欢迎小伙伴访问。