
Unity — 如何为移动游戏创建基础框架
正如我之前提到的,这些设计模式取决于您项目的需求。在一切之前,请记住,本文和 github 仓库是根据我的需求准备的。当我阅读虚幻引擎的文档时,游戏框架对我来说似乎非常不同且有趣,因为我们习惯在 Unity 中自己编写一切。
版权声明:
- 本文为“优梦创客”原创文章,您可以自由转载,但必须加入完整的版权声明
- 文章内容不得删减、修改、演绎
- 本文涉及的资源都在文末参考链接中
已经很久没有在这里写文章了。终于,是时候在这里分享一些新东西了 :)
今天,我将一步步解释如何为即将推出的移动游戏创建基础框架。您无需在每个仓库中一次又一次地重新发明轮子。此外,该框架将通过强制所有开发者使用相同的类/函数来提高团队效率和和谐。
在一切之前,请记住,本文和 github 仓库是根据我的需求准备的。因此,您最好根据您的需求进行调整/扩展。
另一方面,没有一种方法能够满足所有游戏开发限制的最佳方案。因此,首先优先考虑您的项目需求。
1- 文件夹结构
初看之下,项目初期文件夹结构应该井然有序,因为所有项目贡献者都必须轻松找到资产文件夹。这非常重要。此外,随着即将到来的提交,文件夹数量将迅速增长。因此,我们首先应该考虑的是文件夹结构。
在那个时刻,让我快速提供一些建议和最佳实践。
1- 一些文件夹名称是 Unity 引擎特有的,因此请按照 Unity 的设计使用它们。
请参阅链接:Unity - Manual: Reserved folder name reference
我的文件夹结构
2- 一些文件夹名称在 Unity 开发者中非常常见。因此,最好使用这些名称。
例如,预制件、脚本、艺术等。
3- 将第三方资产保存在不同的文件夹中,例如插件文件夹。
为了减少编译时间,一个特定的“第三方文件夹”使我们能够在它们没有任何的情况下创建一个组件定义文件。
4- 将脚本文件夹与命名空间结构对齐是一种良好的做法。
在顶级,编辑器、运行时和测试文件夹应该足够,除非你需要任何额外的特定文件夹。
请参考我的框架以了解脚本文件夹作为示例(参见文末链接)
顶级脚本文件夹
5- 使用 AssetPostProcessor 在导入资产时自动设置首选项。这将加快将资产导入特定文件夹的过程。
请参考:Unity - Scripting API: AssetPostprocessor
2- 场景结构
第二个主题是场景结构。在深入细节之前,我们应该分析需求并相应地创建场景结构。
- 我看到的第一种结构是场景由场景和常用预制件组成。所有场景都是独立的。然而,我不建议这种做法。这是最容易和最基本的开发方式,但维护起来最糟糕。
-第二结构是一个启动场景和其它关卡场景。在我看来,这对关卡式游戏来说是个不错的选择。启动场景为游戏做准备并创建将在游戏过程中保持活跃的 DontDestroyOnLoad 对象。我将在本文中提供更多细节。
- 第三结构,一个起始场景和其上的附加场景。
这种方法对于可能需要更多优化的大型游戏来说可能很好。特别是,与 Addressables 结合时它可能非常强大。然而,这种结构需要更多经验。不同场景中 gameobjects 的通信可能成为问题。
让我解释一下我的结构。
- 我有一个包含我的 DontDestroyOnLoad 管理器的起始场景。
- 为几帧时间,这些经理准备自己。之后,GameStarter 对象改变场景并开启游戏。
启动场景层次结构
- 当新关卡场景打开时,启动场景的 DontDestroyOnLoad 管理器与打开关卡的游戏对象和谐工作。
- 当最新关卡完成时,我使用 SceneManager 打开了新关卡。永驻的管理器要设置为 DontDestroyOnLoad。基于关卡的游戏的一个优点是,你可以利用游戏的地图切换时间来调用 Resources.UnloadUnusedAssets()以从内存中卸载对象。然而,我们仍然可以使用 Addressables 在这里获得更多控制。(参考“Unity全栈开发大师》一小时极速掌握Addressables资源热更新”)
示例层级结构
3- 预制结构
就像 C#中的继承一样,我强烈建议在 Unity 中使用 Prefab 变体。
请找到链接:https://docs.unity3d.com/Manual/PrefabVariants.html
4-代码结构
4-0 命名规范
关于编码,开发者首先应该达成一致的是风格和逻辑上的命名规范。一切都应该清晰且符合风格。
请参阅链接获取详细信息:https://unity.com/how-to/naming-and-code-style-tips-c-scripting-unity
Unreal 与 Unity 相比,我观察到的一点是命名规范更好。
4-1 编程范式
Unity 最终发布了用于生产的实体-组件系统。它提供了更高的性能和大量的 GameObject。然而,我认为它仍然离我们脑海中第一个出现的范式相去甚远。因此,我们很可能会使用面向对象编程。我将提供一些关于它的实际生活建议。
- 继承:在某些情况下,继承使我们能够通过避免重复代码来节省大量时间。
在 C#中,一个类只能从特定的类继承。然而,我们可以通过接口提供更多的功能。如果你之前没有使用它们,请从现在开始使用吧 :) - 封装:通过封装,其他对象可以访问但不能更改它。在 Unity 中,我们可以像下面图片所示轻松实现封装。还有其他实现方式,但对我来说这是最简单的一种。
- 多态性:以下是一个快速示例,请参考下面的图片。第一个函数实现传送,第二个函数实现位置插值。唯一的区别是参数数量。这是一个基本且常见的多态性示例。实际上,多态性远不止于此。请参考下面的链接。
更多详情:小白的游戏梦》玩游戏,学C#
多态示例
4-2 核心与系统架构
将您的运行时代码分成两块“核心”和“系统”。这样做有几个优点。
- 第一个原因是我们可以从基础仓库分叉仓库后,在所有项目中使用这些“系统”而无需更改。此外,我们可以将它们转换为 Unity 包。因此,我们可以在目标 Unity 项目中单独使用它们。
- 第二个原因,我们能够为系统使用不同的汇编定义。因此,当我们更改核心游戏系统的代码时,我们不需要等待编译系统的代码。
- 第三,我们将更容易测试我们的代码。
4–3- 设计模式
在 Unity 的设计模式如游戏循环、更新、原型(预制件)和组件的基础上,我发现实施以下设计模式很有用。正如我之前提到的,这些设计模式取决于您项目的需求。
- 观察者模式:观察者模式简单来说就是存在一个广播对象和一些监听对象。如果广播对象触发一个事件,所有监听者都将被通知。
这种设计模式的一个好处是,广播对象不关心哪些对象正在监听事件。
与 Unreal 不同,Unity 和 C#在观察者模式方面非常出色。在我看来,它对于大型项目来说非常强大且易于维护。然而,请注意,在我们禁用监听器时,您需要取消订阅它们。否则,可能会发生内存泄漏。
作为最佳实践,项目中应只有一个 Event Manager 来处理所有事件。对象不应直接相互通信以处理事件。
让我在这里举一个很好的例子。“OnGameStateChange”是一个几乎所有 gameobject 都需要遵循的事件。在 GameManager 中,我们可以通过 EventManager 触发此事件,因此所有已订阅的对象都将收到通知。
触发一个事件,例如
监听器对象为例
- 服务定位器 & 单例模式
与 Unreal 不同,Unity 非常适合单例模式。我们可以创建一个类的静态实例。然而,当项目变得更大时,使用单例模式会导致复杂的情况。此外,所有开发者都应该了解项目中的所有单例。这就是服务定位器设计模式解决问题的所在。
单例设计模式
在服务定位器模式中,只有一个单例对象为我们提供我们需要的其他对象。快速且安全的方式组织。
- 工厂模式 & 对象池模式
我不会过多介绍如何在 Unity 中实例化对象和对象池。YouTube 上已经有了很多视频。我只是想说,不要在任何类中创建对象。这会使跟踪变得困难。计划中的对象应在工厂中创建。这是一种更易于维护的方法。
我为我基础框架创建了一个简单的抽象工厂类。它为我们提供了从实例化函数或对象池中获取对象的选择。顺便说一句,我认为能够从可寻址系统中创建和返回 GameObject 会更好。(进行中)
- 命令模式 & 状态机模式
尽管我在以前的项目中很少使用它们。我发现它们非常高效且有用。最好去了解一下。
4–4 设计原则
- SOLID: SOLID
请参阅更多信息:https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/may/csharp-best-practices-dangers-of-violating-solid-principles-in-csharp
SOLID 原则迫使开发者编写更干净、更可重用和更易于维护的代码。我强烈建议在可能的情况下随时随地进行遵循。
让我快速分享一下我的想法。
单一职责 => 每件事应该只有一个职责。请勿创建庞大的单体类、函数等。我认为这是最重要且最实用的一个。我在 Unity 中以及生活中都非常认真地采取这种做法 :)
O, L, I, D => 一些最佳实践包括;不要使用与枚举一起的 switch。使用接口代替。这些接口应仅涵盖特定部分,而不是一个单体。对象应通过抽象而不是具体类型相互依赖。
5- 核心
核心是项目的核心。所有项目特定的代码都应该在这里。我将其拆分为不同的部分。
5-1 游戏框架
当我阅读虚幻引擎的文档时,游戏框架对我来说似乎非常不同且有趣,因为我们习惯在 Unity 中自己编写一切。
如果您的项目中存在一个文档齐全的游戏框架,该框架将提高效率。此外,它还能防止游戏开发者之间的一些主要冲突。
根据上述信息,我开始在 Unity 中创建自己的游戏框架
5-1-1 演员(Actor) & 角色(Character)
我定义“Actor”为具有特定行为的 gameobject。另一方面,我将“Character”定义为从“Actor 类”继承的特定版本的 actors。因此,Characters 除了 Actor 的行为外,还拥有独特的行为。我们将要创建的大多数 gameobject 都将继承自 Actor 类和 Character 类。
一些 Actor 对象的行為
5-2 经理与控制器
管理人员是他们领域内所有逻辑的主要责任对象。
- 大多数都依赖于一个为管理者提供额外功能的系统。
- 大部分是 DontDestroyedOnload
- 大多数它们都有只负责特定领域部分的控制器对象。因此,管理者能够分配责任。
管理者
5–3 可脚本对象
在我看来,可脚本化对象是 Unity 游戏开发中最基本的部分之一。我认为这对于所有开发者来说都是必须的:)
- 轻量级
- 能够具有多种用途:存储数据、提供逻辑等。
- 能够调整核心和系统,无需在检查器中触摸任何组件。
- 它们可以在编辑器和运行时使用/更改/调整
- 独立于场景
- 艺术家和游戏设计师会非常感激你 :)
6- 系统
- 系统独立于项目的核心。
- 仅有少数依赖于某些其他系统或第三方插件。
- 系统不需要从 MonoBehaviour 继承。它们可以是纯 C#类。然而,我更喜欢它们是 MonoBehaviour,这样我在检查器上调试起来更方便。这是我的个人选择:)
- 它们可以被转换成 Unity 包。我计划在以下版本中进行转换。
谢谢阅读。
参考:
- 以下资源请加alice17173,注明需要的资源:
-
- 移动游戏代码框架
- Unity全栈开发》一小时极速掌握Addressables热更新
- 小白的游戏梦》玩游戏,学C#
更多推荐
所有评论(0)