在 Unity3D 中管理复杂的预制件层次结构

Managing complex prefab hierarchies in Unity3D

我在几个地方问过这个问题,但还没有完全弄明白,所以也许这里的一些聪明人会知道如何解决这个问题。

在项目中维护预制件深层嵌套层次结构的最佳方式是什么?比如说,我们有几个 GUI 屏幕,由较小的通用组件组成。为了简单起见,我们称前者为"views",后者为"components"。视图处理语义(例如库存视图、商店视图);组件是可配置的,但没有任何业务逻辑附加到它们(例如,一个按钮只关心它的 OnTap callback/event 处理程序)。

视图和组件都可以嵌套。

GUI 个视图需要在多个场景中重复使用。

Unity 中这种方法的主要问题是,任何嵌套层次结构一旦变成预制件,就会失去对其子预制件的引用,就像这个(完全构成,但仍然有效)的例子:

- storeView - UIViewHeader - UIHeaderLabel<Text> - UIList - UIListItem - UIThumbnail - UITitleLabel<Text> - UISubTitleLabel<Text> - UIPrimaryButton<Button> - ...

我想将所有这些小型 UI* 组件保留在单独的预制件中,但也保留 storeView 预制件,以便我可以轻松地将它添加到不同的场景中。不幸的是,当我们创建 storeView 预制件时,所有 UI* 预制件引用都丢失了。 鉴于此,我们可以尝试一种不同的方法,而不是使用带有内容的 storeView 预制件,我们将其保持为空并选择以下几个选项之一:

  1. 在 运行 时间内将行为附加到 storeView 并加载子预制件
    • 缺点:使设计人员的工作流程更加复杂,将复杂性置于脚本中,从开发人员的角度来看,这也更容易出错
    • 优点:可以更轻松地在场景之间重用 storeView,组件预制件可以设置样式,全局修改
  2. 将 storeView 保留为一个空的预制件,并在每个需要它的场景中重新组装它
    • 缺点:需要手动连接组件,仍然很容易不小心保存整个 storeView 并丢失预制引用
    • 优点:保证组件预制件引用得到维护,允许视图之间存在细微差异(恕我直言,这实际上是一个问题,因为这些应该属于配置层)
  3. 将整个 storeView 保存为预制件
    • 缺点:扩展性极差,使得迭代新功能或小的 UX 更改更耗时(额外的 QA、验收测试等...)
    • 优点:这是一种快速而肮脏的解决方案,适用于小型项目
  4. 使用 Prefab Evolution 或类似的
    • 缺点:我认为该包将被路线图上的嵌套预制件淘汰?需要取决于第 3 方代码并且可能不够灵活(这里有什么意见,伙计们?)
    • 优点:从我看到的许多(混合)评论中,有些评论非常积极。然而,项目越复杂,这些评论就越不积极。
  5. 编写一堆自定义编辑器脚本:
    • 缺点:耗时,而且 - 似乎平台应该提供的东西,即使它主要处理游戏
    • 优点:完全控制行为,开发人员可以根据设计师的反馈进行改进。有人可能会争辩说,严格遵守将实施、测试和设计人员友好的工具作为功能要求听起来是一种很好的设计实践(从而减少技术债务,更容易维护)

这是我个人的、理想主义的、不切实际的解决方案*:

使用组件化架构,默认情况下子预制件引用存储在复杂预制件中。在内部将它们视为移动设备上的 UI 视图和子视图 (Cocoa),或 React 中的组件 类 或更好的功能组件 - React/Cycle/Elm/anything 与 FRP 配合得很好(是的,我知道这些方法在很多方面都有所不同,但关键是可组合性,可通过功能组合、装饰器等实现,等等...)。

澄清一下,这不是关于 Unity 的咆哮,作为一个以前使用移动(本地)和 Web 的开发人员,我发现它解决了我的许多问题,并且其中一些简单得可笑,这给我留下了深刻的印象解决方案是。

我可能会在单独的场景中维护复合视图。简单地将这些场景视为预制件,仅包含给定 class.

的单个原型对象

使用 SceneManager.LoadScene with LoadSceneMode.Additive 你甚至可以创建一些静态工厂方法,比如:

public class UIStoreView
{
    public static UIStoreView Instance()
    {
        SceneManager.LoadScene("UIStoreView", LoadSceneMode.Additive);
        return GameObject.Find("UIStoreView");
    }
}

通过一些命名约定,您可以实现像 storeView = UIStoreView.Instance();.

这样简单的事情

不是通用/可扩展的东西,但至少你有一些轻量级/可维护的东西,直到他们推出嵌套预制件(timeline uncertain 正如他们所说)。