
开始使用 Unity ScriptableObjects 演示
本页面提供了 PaddleBallSO 的概述,这是电子书 在 Unity 中使用 ScriptableObjects 创建模块化游戏架构 的配套演示项目,并解释了它如何在其代码架构中使用设计模式和模块化。
这是为帮助 Unity 开发者使用伴随电子书的演示而创建的六个迷你指南中的第一个。该演示受到经典球拍和球的街机游戏机制的启发,展示了 ScriptableObjects 如何帮助您创建可测试、可扩展且设计师友好的组件。
电子书、演示项目和这些迷你指南共同提供了在您的 Unity 项目中使用 编程设计模式 的最佳实践。这些提示可以帮助您简化代码、减少内存使用并促进代码重用。
本系列包括以下文章:
开始之前的重要说明
在您深入了解 ScriptableObject 演示项目和这一系列迷你指南之前,请记住,设计模式的核心只是一些想法。它们并不适用于每种情况。这些技术可以帮助您学习与 Unity 和 ScriptableObjects 一起工作的新的方法。
每种模式都有其优缺点。仅选择那些对您的特定项目有意义的模式。您的设计师是否在很大程度上依赖 Unity 编辑器?基于 ScriptableObject 的模式可能是一个不错的选择,可以帮助他们与您的开发人员协作。
最终,最佳的代码架构是适合您的项目和团队的架构。

球拍球 ScriptableObject 项目
PaddleBallSO 以经典游戏为中心,该游戏催生了现代视频游戏,特色是两个玩家、两个球拍和一个球。
这里的重点是基础设施,而不是游戏机制。将其视为“游戏管道”——虽然不那么光鲜,但却是保持应用程序运行的重要基础。
该项目专注于 ScriptableObjects 如何在幕后工作以构建统一的应用程序。使用它们构建多功能游戏系统,可以简化项目维护并促进代码重用。您还可以将它们用于玩家数据、游戏状态管理、游戏内行为等。
PaddleBallSO 项目与最新版本的 Unity 长期支持 (LTS) 兼容,目前是 2022 LTS。它结合了 UI 工具包用于创建运行时 UI,以及输入系统用于处理用户输入。

准备开始
在 GitHub 仓库 中找到并下载该项目。
加载Bootloader_scene或从GameSystems菜单启用Load Bootstrap Scene on Play。进入播放模式以开始。
尽管这些并不特定于ScriptableObjects,但演示项目使用了一些常见技术,以帮助以一致和可预测的状态启动游戏应用程序。
场景 引导程序(或引导加载程序)是负责设置游戏初始状态的编辑器扩展脚本。此初始化代码与游戏逻辑分开,并确保场景中对象的所有依赖项都正确设置。
为了避免依赖问题,引导程序在加载场景时配置基本游戏对象、管理器或服务。
如果您的Unity应用程序跨越多个场景,引导加载程序可以强制加载特定的引导场景,该场景是构建设置中的第一个场景。退出播放模式时,编辑器会重新加载先前的场景。
引导场景中的另一个组件序列管理器可以在场景加载时实例化基本预制件。在这个特定的演示项目中,创建游戏所需的一切都是预制件,包括相机、启动画面、UI菜单和场景加载器。
场景加载器然后根据需要增量加载(和卸载)任何游戏场景。在大多数情况下,这些场景主要由预制件组成。
项目场景
每个迷你游戏关卡都是一个单独的Unity场景,并出现在构建设置中。如果您想探索这些单独的场景,请在GameSystems菜单中禁用SceneBootstrapper。
许多项目还包括引导场景后的主菜单的暂存区域。这个简化的演示项目省略了主菜单场景。

PaddleBallSO 项目的概述
使用播放和模式菜单测试PaddleBallSO,其中包括:
- 设计模式演示,或小型示例,展示特定技术并单独说明每种模式。
- 迷你游戏将这些组合成功能齐全的工作示例。
核心文件夹包含代码库中与应用程序无关的部分,如基本模式脚本、场景管理和用户界面逻辑。这些是更通用的类,可以应用于各种应用程序。

探索模式和迷你游戏
示例游戏重现了一个标志性的2D物理模拟,并展示了基于ScriptableObject的设计模式的潜力。
然而,在深入了解模式之前,您需要熟悉构成应用程序的MonoBehaviours。正如您所料,像Paddle、Ball、Bouncer和ScoreGoal脚本这样的组件控制基本游戏玩法。
几个更高级的管理脚本控制游戏流程:
- 游戏管理器控制游戏状态(开始、结束、重置),初始化游戏组件,管理用户界面,并响应事件。
- 游戏设置与游戏管理器一起工作,设置球、球拍、墙壁和目标。
- 得分管理器更新得分值,处理游戏事件,并控制得分显示的用户界面更新。
这些MonoBehaviours与您的ScriptableObjects一起工作。它们在连接这些组件方面发挥着至关重要的作用,以便它们可以相互交流和共享数据。
事件对于项目不同部分之间的通信至关重要。它们将这些管理脚本与其他场景对象和用户界面连接起来。这种事件驱动的架构可以帮助使代码更有组织性和可调试性。
我们还为每种最常见的ScriptableObject模式提供了简化的演示示例。当您熟悉它们时,您会开始认识到ScriptableObjects如何支撑迷你游戏的架构。
我们本可以用更少的代码行制作特色迷你游戏,但这个演示特别关注与ScriptableObjects的设计模式。请注意,您也可以在没有ScriptableObjects的情况下实现许多这些模式。
作为一个团队决定每种模式如何适用于您的项目,并选择最适合您的方法。

模块化游戏架构
软件开发中的模块化涉及将应用程序分解为更小、更独立的部分。这些模块服务于特定目的,可以单独开发和测试。
每一小组对象作为一个单元运作,处理游戏的一个方面。这可以是控制玩家输入、处理物理或计算分数的任何事情。
在探索项目脚本时,请注意以下关键要点:
- 从预制件构建场景:您会注意到项目中的大多数场景只是预制件的集合,几乎没有覆盖。预制件本质上提供了一定程度的模块化。 故障排除某个功能变成了在隔离状态下测试该特定预制件的问题。像可脚本化对象一样,预制件是项目级资产,可以在多个场景中重用和共享。
- 使用可脚本化对象进行数据存储(及更多):避免使用MonoBehaviours来存储静态游戏数据。相反,利用可脚本化对象以获得更好的可重用性。 您还可以将某些游戏逻辑委托给可脚本化对象,或让它们促进场景对象之间的通信。
- 分离关注点:在项目中保持数据、逻辑和用户界面之间的清晰区分。这提高了代码的可维护性并简化了调试。 我们的菜单屏幕系统利用了更新的UI工具包。该UI系统强制执行一种工作流程,将界面与其底层逻辑分开。有关更多详细信息,请查看Unity中的用户界面设计与实现电子书。
- 最小化依赖关系:减少组件之间的依赖关系,使您更容易修改或替换项目的部分,而不会导致意外问题。
- 使用事件进行通信:事件使组件之间松散耦合,允许它们在没有直接依赖的情况下相互发送消息。您可以通过基于ScriptableObject的“事件通道”进一步解耦它们。
- 避免不必要的单例:单例设计模式可能很有用,但仅在类的单个实例至关重要时。过度使用单例可能导致紧密耦合的代码,并妨碍测试。如果不需要,就跳过单例。
将大型单体脚本重构为更小的部分促进了可重用性和可扩展性。这导致了团队协作的改善和测试的简化。

ScriptableObjects 的实际应用
我们创建了PaddleBallSO项目来演示ScriptableObject的用例。以下是您将看到它们在实际应用中的几个特定地方:
- GameDataSO ScriptableObject作为游戏设置的中央数据容器。编辑您的公共数据一次,然后与其他需要它的对象共享。
- 迷你游戏依赖于众多事件通道以解耦的方式进行通信。这些基于ScriptableObject的事件构成了对象之间发送消息的基础。
- 声音播放使用基于ScriptableObject的委托对象将逻辑与MonoBehaviour组件分离。
- 我们使用PlayerIDSO基于ScriptableObject的枚举来区分Player1和Player2之间的“团队”。
- LevelLayoutSO ScriptableObject作为游戏元素(如球拍、目标、墙壁和球)的起始位置的数据容器。 这允许在Unity内部和通过导出的JSON文件轻松修改关卡布局。在Unity外部对关卡设计进行修改可以鼓励玩家的创造力和分享自定义布局。
一定要查看设计模式演示以获取一些额外内容!

ScriptableObjects 的 6 个最佳实践
虽然ScriptableObjects可以是存储游戏数据和简化工作流程的强大工具,但有效使用它们以避免使项目杂乱是至关重要的。
以下是使用ScriptableObjects在Unity中的一些最佳实践:
- 保持数据模块化和有序:为不同的数据类型使用单独的ScriptableObjects(例如,一个用于玩家统计,一个用于敌人行为等)。将复杂数据分解为更小的部分,以便于管理。
- 使用文件夹和命名约定:ScriptableObjects可以帮助减少脚本中的代码,但代价是需要处理项目文件夹中的更多素材资源。适当的命名和目录组织可以帮助有效管理这些ScriptableObjects。查看我们的代码风格指南以获取命名的建议。
- 避免过度使用ScriptableObjects:ScriptableObjects是出色的数据容器,但重要的是不要过度使用它们。请记住,ScriptableObjects可能会增加项目的复杂性,因此仅在它们提供明确好处时才使用。(例如,不要用它们来保存持久数据。)
- 定期备份数据:对ScriptableObjects的更改在运行时是“实时”发生的,并作为资产文件保存。请确保定期备份您的项目,以避免丢失任何数据。使用版本控制软件跟踪对ScriptableObjects的更改。
- 使用检查器窗口:ScriptableObjects 的一个主要优点是它们是可序列化的,并且出现在检查器中。利用编辑器界面查看和操作存储在 ScriptableObjects 中的数据。
- 利用自定义编辑器脚本:ScriptableObjects 不能原生引用场景层次结构中的运行时对象。使用编辑器脚本使检查器成为更用户友好的界面,如果您需要可视化这些对象。
遵循这些指南,您可以避免常见的开发陷阱。

更多 ScriptableObject 资源
在电子书《使用 ScriptableObjects 创建模块化游戏架构》中了解更多关于 ScriptableObjects 的设计模式。您还可以在电子书《通过游戏编程模式提升您的代码》中找到更多关于常见 Unity 开发设计模式的信息。