Godot 关键概念概述
(注:该文章参考自godot官方文档与deepseek的回答)
或许,无论是什么原因想选择godot的开发者们在没有引擎基础的情况下,都会发现和我一样的问题:网上godot的教程少且不够全面,很难凭借一两套教程就真正入门godot,最后发现还是得阅读Godot官方文档,但是这份文档又过于简洁,所以其实我们还得用到deepseek这样的工具辅助我们理解文档。
本文是结合deepseek对官方文档的一个解读,主要是自用,偶然刷到的朋友,也祝你早日开发出自己的游戏。
任何游戏引擎都是围绕着构建程序所用的抽象运转的。在 Godot 中,游戏就是一棵由节点构成的树,树又可以结合起来构成场景。然后你还可以将这些节点连起来,让它们通过信号进行通信。
所以初学godot,我们要了解这四个基本概念。简单地了解它们,让我们对引擎的工作原理有一个了解。在实践中,我们会不断使用它们,直到最后烂熟于心。
场景
在 Godot 中,游戏被分解成可重复使用的场景。
场景(scene),顾名思义,但又有所不同于我们日常理解的场景,因为godot的场景可以是一个角色、一件武器、用户界面中的一个菜单、一座房子、整个关卡、或者任何你能想到的东西,我们不妨理解为,一个事物就可以构成一个场景。Godot 的场景很灵活,既能够充当预制件(Prefab),又能够用作其他游戏引擎中的场景。
(该图中就是一个游戏主菜单的场景示意)
还可以嵌套场景,例如,可以把一个角色放在关卡中,然后拖放一个场景作为它的子级。
节点
场景由一个或多个节点组成。节点是游戏中最小的构建块,将其排列成树得到一个场景。以下是一个角色场景。
它由名为“Player”的 CharacterBody2D 节点、Camera2D、Sprite2D、CollisionShape2D 组成。
节点名称以“2D”结尾,因为这是一个 2D 场景。对应 3D 节点的名称以“3D”结尾。(原本的空间节点“Spatial”从 Godot 4 开始改名成了“Node3D”。)
注意节点和场景在编辑器中看起来是一样的。当你把一棵节点树保存为场景时,它就显示为单个节点,其内部结构在编辑器中是隐藏的。
Godot 提供了丰富的基础节点类型库,你可以通过组合和扩展来建立更强大的节点。无论是 2D、3D 还是用户界面,你都可以用这些节点完成大多数事情。
上图展示了 Godot 中一些核心类的继承层次结构。解读:
类继承关系解析
-
Node:
- 这是 Godot 中最基础的类,所有其他类都直接或间接继承自
Node
。 Node
是场景树的基本单位,负责管理子节点和处理生命周期事件(如_ready()
、_process()
)。
- 这是 Godot 中最基础的类,所有其他类都直接或间接继承自
-
Viewport:
- 继承自
Node
。 Viewport
是一个用于渲染内容的容器,可以包含 2D 或 3D 场景。- 它通常用于创建分屏、画中画效果,或者作为渲染目标。
- 继承自
-
Window:
- 继承自
Viewport
。 Window
是 Godot 4.0 引入的类,用于管理应用程序窗口。- 它允许控制窗口的大小、位置、标题等属性。
- 继承自
-
SubViewport:
- 继承自
Viewport
。 SubViewport
是一个嵌套的视口,通常用于渲染到纹理或实现复杂的渲染效果。
- 继承自
-
CanvasItem:
- 继承自
Node
。 CanvasItem
是 2D 渲染的基础类,所有 2D 对象(如精灵、UI 元素)都继承自它。- 它提供了绘制、变换、可见性控制等功能。
- 继承自
-
Node2D:
- 继承自
CanvasItem
。 Node2D
是 2D 场景中常用的基类,提供了 2D 变换(位置、旋转、缩放)和层级管理功能。
- 继承自
-
CollisionObject2D:
- 继承自
Node2D
。 CollisionObject2D
是 2D 碰撞检测的基础类,所有 2D 物理对象(如角色、障碍物)都继承自它。- 它支持碰撞形状和物理交互。
- 继承自
-
AnimatedSprite2D:
- 继承自
Node2D
。 AnimatedSprite2D
是一个用于播放 2D 动画的节点,支持帧动画和动画控制。
- 继承自
-
AudioListener2D:
- 继承自
Node2D
。 AudioListener2D
用于 2D 场景中的音频监听,决定音频的播放位置和效果。
- 继承自
-
AudioStreamPlayer2D:
- 继承自
Node2D
。 AudioStreamPlayer2D
是一个 2D 音频播放器,用于播放声音文件,支持空间化效果。
- 继承自
-
BackBufferCopy:
- 继承自
Node2D
。 BackBufferCopy
用于复制屏幕内容,通常用于实现后期处理效果(如模糊、扭曲)。
- 继承自
继承关系总结
- Node 是 Godot 中所有类的基类。
- Viewport 和 CanvasItem 是两大分支:
Viewport
分支用于渲染管理和窗口控制。CanvasItem
分支用于 2D 渲染和对象管理。
- 具体的功能类(如
AnimatedSprite2D
、AudioStreamPlayer2D
)继承自Node2D
或CollisionObject2D
,提供了更具体的功能。
图的作用
这张图可以帮助开发者理解 Godot 中类的继承关系,从而更好地选择和使用合适的类来实现功能。例如:
- 如果需要实现 2D 动画,可以使用
AnimatedSprite2D
。 - 如果需要播放 2D 音频,可以使用
AudioStreamPlayer2D
。 - 如果需要实现复杂的渲染效果,可以使用
Viewport
或SubViewport
。
场景树
游戏的所有场景都汇集在场景树中,字面意思是场景的树。由于场景是节点树,因此场景树也是节点树。但是,从场景的角度来考虑你的游戏更容易,因为它们可以代表角色、武器、门或你的用户界面。
该图是Godot引擎的场景结构图,展示了场景中节点的层级关系。
解读:
1. 场景结构概述
- 图中展示了一个 Godot 场景的节点树结构。
- 节点树是 Godot 中组织场景的核心方式,每个节点可以包含子节点,形成层级关系。
- 这种结构通常用于组织游戏对象、UI 元素、背景层等。
2. 节点层级解读
根节点:Scene
- 这是场景的根节点,所有其他节点都是它的子节点。
- 在 Godot 中,每个场景都有一个根节点,通常是
Node
、Node2D
或Control
类型。
Filter Nodes
- 这是一个过滤节点,可能用于对场景中的某些节点进行筛选或分组。
- 在 Godot 中,过滤节点可以用于逻辑分组或管理特定类型的节点。
Level01
- 这是一个表示关卡或场景的节点,可能是
Node2D
或Node
类型。 - 它包含了关卡中的所有对象(如背景、角色、敌人、UI 等)。
BackgroundLayer
- 这是一个背景层节点,通常用于管理背景元素。
- 它可能包含
TextureRect
或其他 2D 节点,用于显示背景图片或动画。
TextureRect
- 这是一个用于显示纹理的 UI 节点,通常用于显示背景图片。
- 它是
Control
类的子类,适合用于 UI 或 2D 场景中的静态图像。
InterfaceLayer
- 这是一个界面层节点,用于管理用户界面(UI)元素。
- 它可能包含按钮、标签、进度条等 UI 控件。
UserInterface
- 这是用户界面的根节点,通常包含所有与 UI 相关的元素。
- 它可能是
Control
或CanvasLayer
类型,用于管理 UI 的渲染顺序和交互。
TileMap
- 这是一个用于创建 2D 地图的强大工具,它允许开发者使用 图块(Tiles) 来快速构建复杂的 2D 场景,如平台游戏的地形、RPG 游戏的地图或策略游戏的网格。
CharacterBody2D
- 这是一个 2D 角色节点,通常用于表示玩家或 NPC。
- 它是
PhysicsBody2D
的子类,支持物理交互(如碰撞、移动)。
Portal2D
- 这是一个传送门节点,可能用于实现关卡之间的切换或角色传送。
- 它可能是自定义节点,包含触发区域和传送逻辑。
Coins
- 这是一个硬币节点,通常用于表示可收集的物品。
- 它可能是
Area2D
或PhysicsBody2D
的子类,支持碰撞检测。
Enemies
- 这是一个敌人节点,通常用于管理所有敌人对象。
- 它可能包含多个子节点,每个子节点表示一个敌人。
3. 场景结构的用途
- 层级管理:通过节点树组织场景,便于管理和调试。
- 逻辑分组:将相关节点分组(如背景、UI、角色等),提高代码可读性。
- 模块化设计:每个节点可以独立开发,便于复用和维护。
4. 示例场景的功能
- 背景:通过
BackgroundLayer
和TextureRect
显示背景图片。 - UI:通过
InterfaceLayer
和UserInterface
管理用户界面。 - 角色:通过
CharacterBody2D
控制玩家或 NPC 的行为。 - 交互对象:通过
Portal2D
和Coins
实现传送和收集功能。 - 敌人:通过
Enemies
管理所有敌人对象。
5. 小总结
这张图展示了一个典型的 Godot 场景结构,包含了背景、UI、角色、交互对象和敌人等元素。通过节点树的层级关系,开发者可以清晰地组织和管理场景中的各个部分。这种结构是 Godot 场景设计的核心,能够有效支持复杂的游戏逻辑和交互。
信号
节点在发生某些事件时发出信号。此功能无需在代码中硬连接节点就能让它们相互通信。它为你提供了构建场景的灵活性。
(In other words,信号是 Godot 中用于实现节点间通信的一种机制,它允许节点在特定事件发生时通知其他节点,而无需直接引用或硬编码连接。)
其他内置信号可以告诉你两个对象何时碰撞,角色或怪物何时进入给定区域等等。你还可以针对游戏量身定制新的信号。
1. 信号的核心概念
什么是信号?
- 信号是一种事件驱动的通信机制。
- 当某个节点发生特定事件(如按钮点击、角色死亡等)时,它可以发射(emit)一个信号。
- 其他节点可以监听(connect)这个信号,并在信号发射时执行相应的逻辑。
信号的优势
- 松耦合:节点之间不需要直接引用彼此,只需通过信号通信。
- 灵活性:多个节点可以监听同一个信号,各自执行不同的操作。
- 事件驱动:适合处理异步事件(如用户输入、游戏事件等)。
2. 信号的组成部分
-
信号的定义:
- 信号在脚本中定义,通常与特定事件相关联。
- 使用
signal
关键字定义信号,例如:signal player_died signal health_changed(new_health)
-
信号的发射:
- 使用
emit_signal()
方法发射信号,并可传递参数。例如:emit_signal("player_died") emit_signal("health_changed", 50)
- 使用
-
信号的连接:
- 使用
connect()
方法将信号连接到目标对象的某个方法。例如:player.connect("player_died", self, "_on_player_died")
- 当信号发射时,目标方法会被调用。
- 使用
-
信号的断开:
- 使用
disconnect()
方法断开信号与方法的连接。例如:player.disconnect("player_died", self, "_on_player_died")
- 使用
3. 信号的示例
示例 1:按钮点击信号
# Button.gd
extends Button
signal button_clicked
func _ready():
connect("pressed", self, "_on_button_pressed")
func _on_button_pressed():
emit_signal("button_clicked")
# Game.gd
extends Node
func _ready():
var button = $Button
button.connect("button_clicked", self, "_on_button_clicked")
func _on_button_clicked():
print("Button was clicked!")
示例 2:角色死亡信号
# Player.gd
extends Node2D
signal player_died
func die():
emit_signal("player_died")
# Game.gd
extends Node
func _ready():
var player = $Player
player.connect("player_died", self, "_on_player_died")
func _on_player_died():
print("Player has died!")
4. 信号的工作原理
-
定义信号:
- 在脚本中定义信号,表示某个事件。
-
发射信号:
- 在特定条件下(如按钮点击、角色死亡),调用
emit_signal()
发射信号。
- 在特定条件下(如按钮点击、角色死亡),调用
-
连接信号:
- 使用
connect()
方法将信号连接到目标方法。
- 使用
-
处理信号:
- 当信号发射时,目标方法被调用,执行相应的逻辑。
5. 信号的优势
-
松耦合:
- 节点之间不需要直接引用彼此,只需通过信号通信。
- 例如,
Player
节点不需要知道Game
节点的存在,只需发射信号。
-
灵活性:
- 多个节点可以监听同一个信号,各自执行不同的操作。
- 例如,
Player
死亡时,Game
节点可以显示游戏结束界面,Score
节点可以保存分数。
-
事件驱动:
- 信号适合处理异步事件(如用户输入、游戏事件等)。
- 例如,按钮点击、角色死亡等事件可以通过信号处理。
6. 信号的注意事项
-
信号命名:
- 信号名称应清晰明确,反映事件的含义(如
player_died
、health_changed
)。
- 信号名称应清晰明确,反映事件的含义(如
-
信号连接:
- 确保信号连接的目标方法存在,否则会报错。
-
信号断开:
- 在不需要时断开信号连接,避免内存泄漏或重复调用。
-
信号参数:
- 信号可以传递参数,目标方法需要接收这些参数。
7. 信号的进阶用法
动态连接信号
extends Node
func _ready():
var button = $Button
button.connect("pressed", self, "_on_button_pressed", [button.name])
func _on_button_pressed(button_name):
print("Button clicked:", button_name)
信号与自定义数据
# Player.gd
extends Node2D
signal health_changed(new_health)
var health = 100
func take_damage(amount):
health -= amount
emit_signal("health_changed", health)
# Game.gd
extends Node
func _ready():
var player = $Player
player.connect("health_changed", self, "_on_health_changed")
func _on_health_changed(new_health):
print("Player health changed to:", new_health)
小总结
信号是 Godot 中用于实现节点间通信的强大工具,它允许节点在特定事件发生时通知其他节点,而无需直接引用或硬编码连接。通过信号,开发者可以实现松耦合、灵活且事件驱动的游戏逻辑。信号的核心功能包括定义、发射、连接和处理,适用于处理用户输入、游戏事件等多种场景。
总结
节点、场景、场景树和信号是 Godot 中的四个核心概念,你将一直操纵它们。
节点是游戏最小的构建块。你把它们组合起来创建场景,再把它们组合起来并嵌套到场景树中。最后,你可以使用信号来使节点对其他节点或不同的场景树分支中的事件做出响应。
这个简短的分解并不能让我们就此入门godot,可能还是有很多疑惑,接着阅读godot官方文档的整个入门系列中并动手实践或许能得到解决。