![Hero background image](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F0c660eec367947d97e5c33fc2eb8f056c53365f2-1200x675.jpg&w=3840&q=100)
本页概述了电子书《 使用 ScriptableObjects 在 Unity 中创建模块化游戏架构》的配套演示项目 PaddleBallSO,并解释了它如何在其代码架构中使用设计模式和模块化。
这是六本迷你指南系列中的第一本,旨在帮助 Unity 开发人员使用电子书附带的演示。该演示的灵感来自经典的球和桨街机游戏机制,并展示了 ScriptableObjects 如何帮助您创建可测试、可扩展且设计人员友好的组件。
电子书、演示项目和这些迷你指南共同提供了在 Unity 项目中使用 ScriptableObject 类的 编程设计模式 的最佳实践。这些技巧可以帮助您简化代码,减少内存使用,并提高代码的可重用性。
本系列包括以下文章:
在深入研究 ScriptableObject 演示项目和本系列迷你指南之前,请记住,设计模式本质上只是想法而已。它们并不适用于所有情况。这些技术可以帮助您学习使用 Unity 和 ScriptableObjects 的新方法。
每种模式都有优点和缺点。仅选择那些对您的特定项目有意义的内容。您的设计师是否严重依赖 Unity 编辑器?基于 ScriptableObject 的模式可能是帮助他们与开发人员合作的一个好选择。
最终,最好的代码架构是适合您的项目和团队的架构。
![paddleball](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F192acf691c9a632c7bee44e3229c5f1d5347245e-1999x1125.png&w=3840&q=75)
PaddleBallSO 以催生现代视频游戏的经典游戏为中心,其中有两个玩家、两个球拍和一个球。
这里的重点是基础设施而不是游戏机制。可以将其视为“游戏管道”——不那么光鲜但却是保持应用程序运行至关重要的基础。
该项目专注于 ScriptableObjects 如何在幕后工作以构建统一的应用程序。使用它们来构建多功能游戏系统,可以简化项目维护并提高代码可重用性。您还可以将它们用于玩家数据、游戏状态管理、游戏内行为等。
PaddleBallSO 项目与最新版本的 Unity 长期支持 (LTS) 兼容,目前为 2022 LTS。它包含用于创建运行时 UI 的 UI 工具包和用于处理用户输入的输入系统。
![bootloader](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F284426dcc53dbb2791e88cd61c83bbf21af058d3-1999x1158.png&w=3840&q=75)
在 GitHub 存储库中找到并下载该项目。
从 GameSystems 菜单加载 Bootloader_scene 或启用 在播放时加载 Bootstrap 场景 。进入播放模式开始。
虽然这些不是 ScriptableObjects 所特有的,但演示项目使用了一些常用技术来帮助以一致且可预测的状态启动游戏应用程序。
场景引导程序 (或引导加载程序)是一个编辑器扩展脚本,负责设置游戏的初始状态。此初始化代码与游戏逻辑分开,并确保正确设置场景中对象的所有依赖关系。
为了避免依赖问题,引导程序在加载场景时配置必要的游戏对象、管理器或服务。
如果您的 Unity 应用程序跨越多个场景,则引导加载程序可以强制加载特定的引导场景,即来自构建设置的第一个场景。退出播放模式时,编辑器会重新加载前一个场景。
引导场景中的另一个组件 “序列管理器”可以在场景加载时实例化必要的预制件。在这个特定的演示项目中,创建游戏所需的一切都是一个预制件 (Prefab),包括一个相机、SplashScreen、UI 菜单和一个 SceneLoader。
然后,SceneLoader 会根据需要额外加载(和卸载)任何游戏场景。大多数情况下,这些场景主要由预制件 (Prefabs) 组成。
项目场景
每个迷你游戏级别都是一个单独的 Unity 场景,并出现在构建设置中。如果您想探索单独的场景,请禁用 GameSystems 菜单中的 SceneBootstrapper。
许多项目还包括引导场景之后的主菜单的暂存区。这个简化的演示项目省略了主菜单场景。
![tab5](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F8e86a91f23d941afb7c23afaef6b5e0a701a1ddd-1584x602.png&w=3840&q=75)
使用 Play 和 Pattern 菜单来测试 PaddleBallSO,其中包括:
- 设计模式 演示或简短示例,展示特定技术并单独说明每种模式
- 将这些内容组合成功能齐全的迷你游戏
Core 文件夹包含代码库的非应用程序特定部分,例如基本模式脚本、场景管理和 UI 逻辑。这些是更通用的类,可以适用于各种应用程序。
![tab10](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F2d39fac6f824fba44cbca60a3136d0105d200b50-1999x1125.png&w=3840&q=75)
示例游戏重现了标志性的 2D 物理模拟,并展示了基于 ScriptableObject 的设计模式的潜力。
然而,在深入研究模式之前,您需要熟悉构成应用程序的 MonoBehaviour。正如您所预料的,Paddle、Ball、Bouncer 和 ScoreGoal 脚本等组件控制着基本的游戏玩法。
几个更高级别的管理脚本控制游戏流程:
- GameManger 控制游戏状态(开始、结束、重置)、初始化游戏组件、管理 UI 并响应事件。
- GameSetup 与 GameManager 一起设置球、桨、墙壁和球门。
- ScoreManager 更新分数值、处理游戏事件并控制分数显示的 UI 更新。
这些 MonoBehaviour 与您的 ScriptableObjects 一起工作。它们在连接这些组件方面发挥着至关重要的作用,使得它们能够相互交流和共享数据。
事件对于项目不同部分之间的沟通至关重要。它们将这些管理脚本与其他场景对象和用户界面连接起来。这种事件驱动的架构可以帮助使代码更有条理、更易于调试。
我们还为每个最常见的 ScriptableObject 模式提供了简化的演示示例。当你熟悉它们时,你就会开始认识到 ScriptableObjects 如何支撑迷你游戏的架构。
我们本可以用更少的代码来制作特色迷你游戏,但此演示特别关注使用 ScriptableObjects 的设计模式。请注意,您也可以在没有 ScriptableObjects 的情况下实现许多这些模式。
作为一个团队决定每种模式如何应用于您的项目并选择最适合您的方法。
![tanb22](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Fa8ee97164913a2af247ea6bad5c22c9c2ba7c2d2-1037x627.png&w=3840&q=75)
软件开发中的模块化涉及将应用程序分解为更小、更独立的部分。这些模块有特定的用途,可以单独开发和测试。
每小组物体都作为一个单元发挥作用,并处理游戏的一个方面。这可以是任何事情,从控制玩家输入、处理物理到计算分数。
在探索项目脚本时,请注意以下关键要点:
- 使用预制件构建场景:您会注意到,项目中的大多数场景都只是具有最少覆盖的预制件的集合。预制件本身提供了一定程度的模块化。对某个功能的故障排除变成了单独测试该特定预制件的问题。与 ScriptableObjects 一样,Prefabs 是可以在多个场景中重复使用和共享的项目级资产。
- 使用 ScriptableObjects 进行数据存储(及更多):避免使用 MonoBehaviours 来存储静态游戏数据。相反,利用 ScriptableObjects 实现更好的可重用性。您还可以将某些游戏逻辑委托给 ScriptableObjects 或让它们促进场景对象之间的通信。
- 单独的关注点:在项目中保持数据、逻辑和用户界面之间的明确区分。这提高了代码的可维护性并简化了调试。我们的菜单屏幕系统采用了较新的 UI 工具包。该 UI 系统强制执行将界面与其底层逻辑分离的工作流程。欲了解更多详细信息,请参阅 Unity 中的用户界面设计和实现 电子书。
- 最小化依赖关系:减少组件之间的依赖性可以更轻松地修改或替换项目的各个部分,而不会导致不可预见的问题。
- 使用事件进行沟通:事件使得组件之间能够松散耦合,使得它们能够相互发送消息而不需要直接依赖。您可以使用基于 ScriptableObject 的“事件通道”进一步将它们解耦。
- 避免不必要的单例:单例设计模式很有用,但只有当类的单个实例必不可少时才有用。过度使用单例可能会导致代码紧密耦合并妨碍测试。如果不需要,请跳过单例。
将大型单片脚本重构为较小的片段可提高可重用性和可扩展性。这将提高团队协作并简化测试。
![设计模式](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2F12957c932379ffcad31516fd14bcc32200e43440-1999x1125.png&w=3840&q=75)
我们创建了 PaddleBallSO 项目来演示 ScriptableObject 用例。您可以在以下几个特定位置看到它们的实际作用:
- GameDataSOScriptableObject 作为游戏设置的中央数据容器。编辑您的通用数据一次,然后与需要它的其他对象共享。
- 迷你游戏依靠众多 事件通道 以解耦的方式进行通信。这些基于 ScriptableObject 的事件构成了对象之间相互发送消息的基础。
- 声音播放使用基于 ScriptableObject 的 委托对象 将逻辑与 MonoBehaviour 组件分离。
- 我们使用基于 ScriptableObject 的 PlayerIDSO枚举 来区分 Player1 和 Player2 之间的“团队”。
- LevelLayoutSOScriptableObject 可作为游戏元素(例如桨、球门、墙壁和球)起始位置的数据容器。这样可以轻松修改 Unity 内部以及通过导出的 JSON 文件在外部的级别布局。在 Unity 之外对关卡设计进行修改可以鼓励玩家发挥创造力并分享自定义布局。
请务必查看 设计模式演示 以获取一些额外内容!
![包裹箱](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Fed2297f0b3bd9714692a11d6b14786b9850ae6d8-989x755.png&w=3840&q=75)
虽然 ScriptableObjects 可以成为存储游戏数据和简化工作流程的强大工具,但有效地使用它们以避免使项目混乱至关重要。
以下是在 Unity 中使用 ScriptableObjects 的一些最佳实践:
- 保持数据模块化和有序化:对不同的数据类型使用单独的 ScriptableObjects (例如,一个用于玩家统计,一个用于敌人行为等)。将复杂数据分解为易于管理的小块。
- 使用文件夹和命名约定:ScriptableObjects 可以帮助减少脚本中的代码,但代价是处理项目文件夹中的更多资产。正确的命名和目录组织可以帮助有效地管理这些 ScriptableObject。请查看我们的 代码风格指南 以获取有关命名的提示。
- 避免过度使用 ScriptableObjects:ScriptableObjects 是出色的数据容器,但重要的是不要过度使用它们。请记住,ScriptableObjects 会增加项目的复杂性,因此只有当它们能带来明显的好处时才部署它们。(例如,不要使用它们来保存持久数据。)
- 定期备份数据:ScriptableObjects 的更改在运行时“实时”发生,并保存为资产文件。请务必定期备份您的项目以避免丢失任何数据。使用版本控制软件来跟踪 ScriptableObjects 的变化。
- 使用检查器窗口:ScriptableObjects 的一个主要优势是它们是可序列化的并且出现在 Inspector 中。利用编辑器界面查看和操作存储在 ScriptableObjects 中的数据。
- 使用自定义编辑器脚本来发挥你的优势:ScriptableObjects 无法原生引用来自场景层次结构的运行时对象。如果您需要可视化这些对象,请使用编辑器脚本使 Inspector 成为更加用户友好的界面。
通过遵循这些指南,您可以避免常见的开发陷阱。
![可编写脚本的结局](/_next/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Ffuvbjjlp%2Fproduction%2Fb056e4b6effe69e49a8184113218ad2935520e52-810x540.jpg&w=3840&q=75)
在电子书《使用 ScriptableObjects 在 Unity 中创建模块化游戏架构》中了解有关使用 ScriptableObjects 的设计模式的更多信息。您还可以在电子书《通过游戏编程模式提升您的代码》中了解有关常见 Unity 开发设计模式的更多信息。