搜索Unity

使用脚本化对象制作游戏的三种有效方法

上次更新时间:2018年12月

本页内容简介:有关如何通过使用脚本化对象构建游戏代码以便轻松管理、更改和调试的现成说明。

这些技巧由Schell Games首席工程师Ryan Hipple提供。他在使用他在使用脚本化对象制作游戏方面拥有丰富的经验。构建游戏方面拥有丰富的经验。您可以在在这里观看Ryan有关脚本化对象的Unite演讲;我们还建议您观看Unity工程师Richard Fine的演讲,以获取以获取有关脚本化对象的精彩介绍。。谢谢Ryan!

简介:什么是脚本化对象?

脚本化对象是一个可序列化的Unity类,能够让您在脚本实例之外存储大量共享数据。使用脚本化对象可以更轻松地管理更改和调试。在游戏中,您可以在不同系统之间建立灵活的通信级别。总之,随着您的不断进步和重用组件,更改和调整内容会更易于管理。

智能游戏工程的三大支柱

坚持采用模块化设计...

  • 避免创建直接相互依赖的系统。例如,库存系统应该能够与游戏中的其他系统通信,但您不会希望在它们之间创建硬引用,因为这会导致将系统重新组装成不同的配置和关系变得很难。
  • 从头开始创建场景:避免场景之间存在瞬态数据。每当点击一个场景时,该场景应该是光洁的断面和负载。这可以让您获得其他场景中所没有的独特行为的场景,而不需要强制去摸索。
  • 设置预制件,使其独立工作。您拖入场景的每一个预制件都应包含所有内部功能。这有助于大型团队控制源代码,其中场景是预制件列表,并且预制件包含单独的功能。这样,您的大部分签入都处于预制件级别,从而减少了场景中的冲突。
  • 让每个组件主要解决一个问题,这样可以更轻松地将多个组件拼凑在一起以构建新的组件。

...可编辑

  • 尽可能多利用数据驱动游戏:当您将游戏系统设计成像机器一样,将数据作为指令处理时,即使游戏正在运行,也能够更有效地对游戏进行更改。
  • 如果尽可能将系统设置为模块化和基于组件的,美术师和设计师将能更轻松地进行编辑。如果设计师能够在游戏中将事物拼凑在一起而无需要求具有明确的功能,很大程度上要归功于每个只实现一个功能的微小组件,然后他们可以通过不同方式组合这些组件,以找到新的游戏玩法/机制。Ryan说,他的团队在他们的游戏中使用的一些主要功能来自于此过程,他称之为"紧急设计"。
  • 团队可以在运行时更改游戏的能力至关重要。您可以在运行时对游戏进行的更改更多,就可以找到更多平衡和值。如果您还能够将运行时状态保存回来(就像脚本化对象一样),那就更好了。

...并可调试

这更像是前面两个游戏的子支柱。游戏模块化程度越高,就越容易测试其中的任何一个部分。也就是说,游戏越是可编辑,在自己的Inspector视图中拥有的功能就越多,调试也就越容易。确保可以在Inspector中查看调试状态,并且在为如何调试游戏制定某些计划之前从不考虑完成的功能。

Ryan中使用脚本化对象制作的三大主要工具

变量

您可以使用脚本化对象制作游戏的最简单的方法之一是使用自包含的基于资源的变量。以下是FloatVariable的示例,但该变量也可扩展到任何其他可序列化类型。

Unity脚本化对象浮点变量

使用该变量,无论技术怎样,您的团队中的每个人都可以通过创建新的FloatVariable资源来定义新游戏变量。任何MonoBehaviour或ScriptableObject都可以使用公共FloatVariable(而不是公共浮点变量),以引用新的共享值。

更妙的是,如果一个MonoBehaviour更改了FloatVariable的值,则其他MonoBehaviour可以看到此更改。这在不需要彼此引用的系统之间创建了一种消息传递层。

此类示例之一是玩家的HP。在拥有一个本地玩家的游戏中,玩家的HP可以是名为PlayerHP的FloatVariable。当玩家受到伤害时,它会从PlayerHP中减去,当玩家接受治疗时,它会加到PlayerHP中。

现在,想像一下场景中的生命条预制件。生命条监控PlayerHP变量以更新显示。如果不更改任何代码,它可以轻松地指向不同的对象,如PlayerMP变量。生命条对场景中的玩家一无所知,它只是从玩家写入的同一个变量中读取值。

Unity脚本化对象处理玩家死亡

我们这样设置,就可以很容易添加更多内容来观看PlayerHP。当PlayerHP变低时,可以改变音乐系统,当敌人知道玩家脆弱时,他们可以更改攻击模式,还可以用屏幕空间效果来强调下一次攻击的危险性。这里的关键是玩家脚本不会将消息发送到这些系统,并且这些系统不需要知道玩家游戏对象。您还可以在游戏正在运行时进入Inspector,更改PlayerHP的值,以便对某些变量进行测试。

编辑FloatVariable的值时,最好是将数据复制到运行时值,从而不更改存储在磁盘上的ScriptableObject的值。如果这样做,MonoBehaviour应访问RuntimeValue以防止编辑保存到磁盘的InitialValue。

Unity脚本化对象浮点变量

活动

您可以在脚本化对象上构建的我最喜欢的功能之一是事件系统。事件架构可在彼此不直接了解的系统之间发送消息来帮助模块化代码。它们允许事件对状态的更改作出响应,而无需在更新循环中进行持续监控。

此事件系统包含两个部分:GameEvent ScriptableObject和GameEventListener MonoBehaviour。设计师可以在项目中创建任意数量的GameEvent来表示可以发送的重要消息。GameEventListener等待引发特定GameEvent,并通过调用UnityEvent作出响应(这不是一个真正的事件,而是序列化函数调用)。

Unity脚本化对象游戏事件监听器

Unity脚本化对象游戏事件监听器

Unity脚本化对象游戏事件监听器

其中一个示例是在游戏中处理玩家死亡。这里大部分执行都可以更改,但可能很难确定对所有逻辑进行编码的位置。如果玩家脚本通过UI触发游戏,音乐会发生变化吗?如果玩家还活着,敌人会检查每一帧吗?事件系统可以让我们避免出现像这样有问题的依赖关系。

如果玩家死亡,玩家脚本会在OnPlayerDied事件上调用Raise。玩家脚本不需要知道哪些系统与之相关,因为它只是一个广播。游戏结束UI正在侦听OnPlayerDied事件并开始制作动画,摄像机脚本可以侦听它并开始淡化为黑色,音乐系统可以对音乐变化作出响应。我们也可以让每个敌人侦听OnPlayerDied,触发讽刺动画或状态更改以重新回到空闲行为。

这种模式可以非常容易地为玩家死亡添加新的响应。此外,还可以通过从Inspector中的某些测试代码或按钮的事件调用Raise,轻松地对玩家死亡响应进行测试。

Unity脚本化对象处理玩家死亡

我在Schell Games中构建的事件系统已经变得越来越复杂,并且具有可传递数据和自动生成类型的功能。我无法一一解释这里的所有细节,但本示例基本上是我们现在使用某些内容的起点。

系统

脚本化对象不一定只是数据。使用您在MonoBehaviour中实现的任意系统,看看您是否可以将实现移动到ScriptableObject。不要在DontDestroyOnLoad MonoBehaviour上使用InventoryManager,而是将其放置在ScriptableObject上。

由于它与场景无关,因此它没有Transform函数,也无法获得Update函数,但是它将在场景加载之间保持状态,而不需要进行任何特定初始化。当需要脚本访问库存时,对库存系统对象的公共引用(而不是使用单例)。这使得在测试库存或教程库存中进行交换比使用单例更简单。

在这里,您可以想像一下引用库存系统的玩家脚本。当玩家生成时,它可以向库存询问所有拥有的对象并生成任何设备。设备UI还可以引用库存并循环遍历项目以确定要绘制的内容。

更多资源

您喜欢本文吗?请告诉我们!

喜欢。继续发送 还行。有待改进

Unity Buffbot让您的工作更轻松

注册接收Unity专家每周提供的技术与创意知识。

订阅
明白了

我们使用Cookie来确保在我们的网站上为您提供最佳体验。有关更多信息,请访问我们的Cookie政策页面