创建实例

上一部分中,我们了解到场景是一系列组织成树状结构的节点,其中只有一个节点是根节点。你可以将项目拆分成任意数量的场景。这一特性可以帮你将游戏拆解成不同的组件,并进行组织。

你可以创建任意数量的场景,用 .tscn 扩展名保存到磁盘上,表示“text scene”(文本场景)。上节课的 Label.tscn 文件就是一个例子。我们把这些文件叫作“打包场景”(Packed Scene),因为它们将场景的内容信息进行了打包。

这是一个小球的例子。它由以下内容组成:一个叫“Ball”的 RigidBody2D 节点是根节点,可以让小球下落、在撞墙后反弹,一个 Sprite 节点,以及一个 CollisionShape2D

../../_images/instancing_ball_scene.png

保存场景过后,这个场景就可以作为蓝图使用:你可以在其他场景中进行任意数量的翻制。将对象根据模板进行翻制的过程就叫作实例化

../../_images/instancing_ball_instances_example.png

我们在上一部分提到过,实例化场景的行为与节点类似:编辑器默认会隐藏其中的内容。实例化 Ball 之后,你只会看到 Ball 节点。请注意制作出的副本,名字是唯一的。

Ball 场景的实例最开始都和 Ball.tscn 有相同的结构和属性。不过你也可以单独修改各个实例,比如修改反弹的方式、重量等源场景所暴露的属性。

实践

让我们来实践一下实例化,看看到底在 Godot 里是如何使用的。我们为您准备了小球的示例项目,欢迎下载:instancing.zip

将压缩包解压到电脑上。然后打开 Godot,在项目管理器中点击“导入”按钮来导入这个项目。

../../_images/instancing_import_button.png

在弹出框中点击浏览按钮,定位到刚才解压的文件夹。

../../_images/instancing_import_browse.png

双击打开 project.godot 文件。

../../_images/instancing_import_project_file.png

最后点击“导入并编辑”按钮。

../../_images/instancing_import_and_edit_button.png

这个项目里包含两个打包场景:Main.tscn,包含了小球会碰撞的墙体,以及 Ball.tscn。应该会自动打开 Main 场景。

../../_images/instancing_main_scene.png

让我们为 Main 节点添加一个小球作为子节点。在“场景”面板中,选择 Main 节点。然后点击场景面板顶部的链接图标。这个按钮的作用是为当前选中节点添加另一个场景的实例作为子节点。

../../_images/instancing_scene_link_button.png

双击小球场景来实例化。

../../_images/instancing_instance_child_window.png

小球会出现在视区的左上角。

../../_images/instancing_ball_instanced.png

点击它,然后拖拽到视图的中心。

../../_images/instancing_ball_moved.png

按 F5 运行游戏。你应该会看到它往下掉。

现在我们希望创建更多的 Ball 节点实例。保持小球仍处于选中的状态,按下 Ctrl-D(macOS 则是 Cmd-D)调用制作副本命令。点击并将新的小球拖到别的位置。

../../_images/instancing_ball_duplicated.png

你可以重复这个过程在场景中多建几个。

../../_images/instancing_main_scene_with_balls.png

再次运行游戏。现在你应该看到每个小球都各自下落。这就是实例的作用。每一个都是模板场景的独立副本。

编辑场景和实例

实例还有很多用法。使用这个特性,你可以:

  1. 使用“检查器”修改一个小球的属性,不影响其他实例。

  2. 打开 Ball.tscn 场景修改 Ball 节点,从而修改所有 Ball 的默认属性。在保存时,项目中所有 Ball 的实例都会受到更新。

备注

修改实例上的属性总是会覆盖对应打包场景中的值。

让我们来试一试。打开 Ball.tscn 然后选中 Ball 节点。在右侧的“检查器”中,点击展开 PhysicsMaterial 属性。

../../_images/instancing_physics_material_expand.png

将其 Bounce(弹力)属性设为 2,只要点击对应的数字字段、输入 2、然后按 Enter 就可以了。

../../_images/instancing_property_bounce_updated.png

F5 运行游戏,请注意所有的小球都更有弹性了。因为 Ball 场景是所有实例的模板,对它进行修改并保存,就会导致所有实例同时进行更新。

现在让我们来调整单个实例。点击视区上方的对应选项卡回到 Main 场景。

../../_images/instancing_scene_tabs.png

选择一个 Ball 实例节点,然后“检查器”中将 Gravity Scale(重力缩放)设为 10

../../_images/instancing_property_gravity_scale.png

在被调整过的属性旁边就会多一个灰色的“复原”按钮。

../../_images/instancing_property_revert_icon.png

这个图标表示你覆盖了源打包场景中的值。即使你修改了原始场景中的这个属性,这个覆盖后的值也还是会保留在这个实例中。点击复原图标会将属性恢复成保存场景中的值。

重新运行游戏,请注意这个小球会比其他小球落得快得多。

备注

如果你修改了一个实例中的 PhysicsMaterial,那么就会影响到其他所有实例。这是因为 PhysicsMaterial 是一个资源,而资源是跨实例共享的。要让某个实例的资源唯一,请在“检查器”中对其右键,然后选择弹出菜单中的“唯一化”。

资源也是 Godot 游戏的关键组件,我们会在后续课程中介绍。

作为设计语言的场景实例

Godot中的实例和场景提供了一种优秀的设计语言,使该引擎与其他引擎不同。我们从一开始就围绕这个概念设计Godot。

我们建议在使用 Godot 制作游戏时忽略架构代码模式,例如模型-视图-控制器 (MVC) 或实体关系图。相反,你可以从想象玩家将在游戏中看到的元素开始,并围绕它们构建代码。

例如,你可以这样拆解一个射击游戏:

../../_images/instancing_diagram_shooter.png

对于几乎任何类型的游戏,都可以想出这样的图表。矩形表示的是从玩家角度可以在游戏中看到的实体,箭头表示的是场景之间的从属关系。

在得到这样的图之后,建议你为其中的每一个元素都创建一个场景。而无论是通过代码还是直接通过编辑器来创建场景树,你都将会用到实例化的方法。

程序员们乐于花费大量时间来设计抽象的架构,尽力使得组件能够适用于这个架构。基于场景的设计取代了这种方法,使得开发更快、更直接,能够让你去专注于游戏逻辑本身。因为大多数游戏的组件都是直接映射成一个场景,所以使用基于场景实例化的设计意味着需要很少的其他架构代码。

这里是另一个更复杂的开放世界类游戏的示例,这个示例包括有很多素材和嵌套元素:

../../_images/instancing_diagram_open_world.png

想象一下,我们从创建房间开始。我们可以制作几个不同的房间场景,在其中有独特的家具安排。后来,我们可以制作一个房屋场景,在内部使用多个房间实例。我们将用许多实例化的房子和一个大的地形来创建一个城堡,我们将把城堡放在这个地形上。每一个场景都将是一个或多个子场景的实例。

之后,我们可以创建代表守卫的场景,将它们加到城堡之中。也就会间接地加到了游戏世界里。

使用 Godot,就可以很容易地像这样迭代你的游戏,因为你需要做的就是创建并实例化更多的场景。我们将编辑器设计成了易于程序员、设计师、艺术家使用的形式。一个典型的团队开发过程会涉及 2D 或 3D 美术、关卡设计师、游戏设计师、动画师等,他们都可以用 Godot 编辑器工作。

总结

实例化,从蓝图生成对象的过程有许多方便的用途。通过场景,它为您提供:

  • 能将你的游戏分离成可以重复利用的组件。

  • 一个构建和封装复杂系统的工具。

  • 一种以自然方式思考游戏项目结构的语言。