开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情
什么是 ScriptableObject?
ScriptableObject 是一个可序列化的 Unity 类,允许您独立于脚本实例存储大量共享数据。使用 ScriptableObjects 可以更轻松地管理更改和调试。您可以在游戏中的不同系统之间建立一定程度的灵活通信,以便在整个生产过程中更改和调整它们以及重用组件更易于管理。
游戏工程的三大支柱
采用模块化设计:
- 避免创建直接相互依赖的系统。例如,库存系统应该能够与游戏中的其他系统通信,但您不会希望在它们之间创建硬引用,因为这会导致将系统重新组装成不同的配置和关系变得很难。
- 从头开始创建场景:避免场景之间存在瞬态数据。每当点击一个场景时,该场景应该是光洁的断面和负载。这可以让您获得其他场景中所没有的独特行为的场景,而不需要强制去摸索。
- 设置预制件,使其独立工作。您拖入场景的每一个预制件都应包含所有内部功能。这有助于大型团队控制源代码,其中场景是预制件列表,并且预制件包含单独的功能。这样,您的大部分签入都处于预制件级别,从而减少了场景中的冲突。
- 让每个组件主要解决一个问题,这样可以更轻松地将多个组件拼凑在一起以构建新的组件。
简化更改和编辑部分:
- 尽可能多利用数据驱动游戏。当您将游戏系统设计成像机器一样,将数据作为指令处理时,即使游戏正在运行,也能够更有效地对游戏进行更改。
- 如果尽可能将系统设置为模块化和基于组件的,美术师和设计师将能更轻松地进行编辑。如果设计师能够在游戏中将事物拼凑在一起而无需要求具有明确的功能,很大程度上要归功于每个只实现一个功能的微小组件,然后他们可以通过不同方式组合这些组件,以找到新的游戏玩法/机制。Ryan 说,他的团队在他们的游戏中使用的一些主要功能来自于此过程,他称之为“紧急设计”。
- 团队可以在运行时更改游戏的能力至关重要。您可以在运行时对游戏进行的更改更多,就可以找到更多平衡和值。如果您还能够将运行时状态保存回来(就像脚本化对象一样),那就更好了。
简化调试:
这更像是前面两个支柱的子支柱。游戏模块化程度越高,就越容易测试其中的任何一个部分。也就是说,游戏越是可编辑,在自己的 Inspector 视图中拥有的功能就越多,调试也就越容易。确保可以在 Inspector 中查看调试状态,并且在为如何调试游戏制定某些计划之前从不考虑完成的功能。
变量的设计
我们可以使用 ScriptableObject 制作游戏的最简单的方法之一是使用自包含的基于资源的变量。以下是 FloatVariable 的示例,但该变量也可扩展到任何其他可序列化类型。
无论技术怎样,团队中的每个人都可以通过创建新的 FloatVariable 资源来定义新游戏变量。任何 MonoBehaviour 或 ScriptableObject 都可以使用公共 FloatVariable(而不是公共浮点变量),以引用新的共享值。
更妙的是,如果一个 MonoBehaviour 更改了 FloatVariable 的值,则其他 MonoBehaviour 可以看到此更改。这在不需要彼此引用的系统之间创建了一种消息传递层。
事件的设计
可以在 ScriptableObject 上构建的 Ryan 最喜欢的功能之一是事件系统。事件架构可在彼此不直接了解的系统之间发送消息来帮助模块化代码。它们允许事件对状态的更改作出响应,而无需在更新循环中进行持续监控。
其他系统的设计
脚本化对象不一定只是数据。使用我们在 MonoBehaviour 中实现的任意系统,看看是否可以将实现移动到 ScriptableObject。不要在 DontDestroyOnLoad MonoBehaviour 上使用 InventoryManager,而是将其放置在 ScriptableObject 上。
由于它与场景无关,因此它没有 Transform 函数,也无法获得 Update 函数,但是它将在场景加载之间保持状态,而不需要进行任何特定初始化。当需要脚本访问库存时,对库存系统对象的公共引用(而不是使用单例)。这使得在测试库存或教程库存中进行交换比使用单例更简单。
在这里,我们可以想象一下引用库存系统的玩家脚本。当玩家生成时,它可以向库存询问所有拥有的对象并生成任何设备。设备 UI 还可以引用库存并循环遍历项目以确定要绘制的内容。