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

枚举是一种方便的方式来管理代码中固定的一组命名值。然而,它们也有一些限制。由于序列化的枚举值以整数而不是其符号名称存储,删除或重新排序一个值可能会导致不正确或意外的行为。这意味着枚举,尤其是当你有很多枚举时,可能会在Unity开发中造成麻烦。
标准方法
以下是一个典型的枚举的样子:
[System.Serializable]
public enum HandGestures
{
石头,
纸,
剪刀
}
您可以使用System.Serializable属性序列化枚举,它会出现在检查器中。
问题
重新排序或删除一个值可能会导致问题。因为每个值在内部都是一个整数,它所代表的内容可能会变得不同。在给定的示例中,删除纸张值会导致剪刀假设值为1。
或者,如果我们添加一个值,如下面的示例所示。
如果选定的枚举值在删除的条目之后,它将会改变。
当维护和更新项目时,这可能会导致问题,特别是当您的枚举包含许多值时。您可以通过保留一个空白或未使用的元素,或通过显式设置整数值来减轻此问题。然而,这两种解决方案都不是理想的。

基于ScriptableObject的枚举为您提供传统枚举的功能,但作为单独的资产存储。例如,请查看下面示例中PaddleBallSO项目中的PlayerIDSO ScriptableObject。
本质上,这是一个空白的ScriptableObject。
您可以用它在项目中创建多个ScriptableObject资产,如P1、P2等。即使它们不包含任何数据,您也可以使用ScriptableObjects进行比较。只需在项目中创建一个新的ScriptableObject资产并给它命名。
您可以在项目中创建尽可能多的玩家ID,并轻松切换它们。只需更改 GameDataSO 脚本中分配的素材资源。
如果您正在检查相等性,这与枚举的工作方式类似。两个变量是否指向同一个 ScriptableObject?如果是,它们是相同的项目类型。否则,它们不是。
即使没有任何额外数据,ScriptableObject 也代表一个类别或项目类型。
在 PaddleBallSO 中,PlayerIDSO 成为团队标识。我们在 GameDataSO 中使用 P1 和 P2 素材资源来区分两个球拍。
GameSetup 脚本为每个球拍分配一个玩家 ID。在游戏过程中,Paddle 脚本将玩家输入与指定的团队 ID 进行比较。
这对任何类型的多人游戏都有应用。或者,考虑在您需要枚举的其他地方采用它们。
由于它们仅仅是在检查器中的分配,ScriptableObjects 不会面临重命名和重新排序的相同问题。
想将标识符名称分别更改为“Player1”或“Player2”吗?您可以这样做,一切仍然正常工作。添加更多 ScriptableObjects?没问题 - 检查器中的素材资源分配保持不变。

这种行为对于创建游戏玩法非常有用。在 Patterns 演示中,单击切换枚举按钮以更改团队。DemoBall上的MonoBehaviour会相应地更新SpriteRenderer。
当球与一个方块碰撞时,它会造成伤害吗?通过运行快速的相等性测试来找出答案。以下代码示例中有一种比较它们的方法。
此方法可以确定两个GameObjects是否在同一队,这在检查友方与敌方交互时非常有用。这种简单的比较可以应用于物品拾取、伤害或任何其他具有“团队”或“对齐”的内容。
有趣的部分发生在你为ScriptableObjects添加逻辑时。与传统的枚举不同,ScriptableObject可以拥有字段和方法,除了保存数据。
使用这些,以便每个ScriptableObject可以拥有专门的比较逻辑。例如,你可以有一个ScriptableObject定义特殊的伤害效果(例如,寒冷、热量、电、魔法等)。
如果你的应用程序需要一个库存系统来装备游戏物品,ScriptableObjects可以表示物品类型或武器槽。某些角色是否不允许持有某些物品?某些物品是否是魔法的或具有特殊能力?基于ScriptableObject的枚举可以添加方法来检查这一点。
前一个示例中的MonoBehaviour DemoBall 包含 AreEqual 方法来比较ScriptableObjects。在扩展行为时,你可以将比较逻辑捆绑在ScriptableObject本身内。
在Patterns演示中,你可以修改球,使其在与物体碰撞时更加挑剔。查看下面代码示例中的通用物品以进行碰撞。
这可以实现与当前演示类似的结果,但现在它有一个 m_Weakness 字段。这允许每个ScriptableObject定义另一个在碰撞时销毁的ScriptableObject。
与其调用AreEqual方法,每个ScriptableObject只需管理自己的比较逻辑。
结果更加灵活和可扩展。你可以更具体,而不是让球摧毁不同团队的方块。场景中的多个球可以摧毁不同的方块,具体取决于它们各自的CollisionItems。
这为不同的、更复杂的交互设置了舞台。如果你想制作一个剪刀石头布系统,你可以定义三个ScriptableObjects:石头、纸和剪刀。每个都可以有自己独特的m_Weakness,并使用IsWinner方法来处理交互。
与枚举不同,ScriptableObjects使这个过程模块化和适应性强。无需依赖额外的数据结构或添加额外的逻辑来与一组单独的数据同步。只需添加一个额外的字段和/或方法来处理逻辑。
一旦你熟悉了基于ScriptableObject的枚举,你会发现它们可以改善你的工作流程,特别是在与队友合作时。由于它们是素材资源,更新它们会减少合并冲突,降低数据丢失的风险。
添加新的基于ScriptableObject的枚举就像创建另一个素材资源一样。与传统枚举不同,添加新值不会破坏你现有的代码。此外,Unity已经内置了工具来搜索、过滤和组织它们,就像任何其他素材资源一样。
作为额外奖励,使用编辑器的拖放UI使你的设计师能够在没有软件开发人员额外支持的情况下扩展游戏数据。你仍然需要协调如何最初设置字段,但设计师可以自己填充这些字段的数据。

基于ScriptableObject的枚举是你的团队可以用来改善协作和效率的又一资源。
阅读更多关于使用ScriptableObjects的设计模式的信息,参见我们的技术电子书 在Unity中使用ScriptableObjects创建模块化游戏架构。您还可以在 通过游戏编程模式提升您的代码 中找到有关常见Unity开发设计模式的更多信息。