第一部分 01:Godot 基础世界观
先建立一个总认知:
Godot 做游戏的核心思路是:用一个个 Node 组成 Scene,再把 Scene 放进 SceneTree 运行。代码 Script 负责控制 Node,Resource 负责存数据,Signal 负责对象之间通信。
你可以先把 Godot 想成一个“可视化的前端工程”:
| Godot | 前端类比 | 说明 |
|---|---|---|
| Project | 一个前端项目仓库 | 整个游戏工程 |
| Scene | 一个组件 / 页面 / prefab | 可复用的对象组合 |
| Node | DOM 节点 / 组件实例 | 游戏里的最小功能单位 |
| Scene Tree | 当前页面运行时 DOM 树 | 游戏运行时真实存在的节点树 |
| Resource | 图片、配置、JSON、样式资源 | 被节点使用的数据或资源 |
| Script | JS / TS 逻辑文件 | 控制节点行为 |
| Signal | 事件 emit / on | 节点之间的消息通知 |
Godot 官方文档里也强调:Node 是 Godot 的构建块,节点可以组成树;一棵节点树就是一个 Scene,Scene 可以保存到磁盘并实例化到其他 Scene 中。 (Godot Engine documentation)
1. Godot 是什么
Godot 是一个开源的游戏引擎,可以做 2D、3D、UI、工具、桌面应用,甚至可以导出到多个平台。它不是一个“库”,而是一个完整编辑器 + 运行时 + 脚本系统 + 资源系统。Godot 当前官方下载页显示最新稳定版为 4.6.2;如果你现在用的是 4.x,下面这些基础概念基本都通用。(Godot Engine)
前端类比
如果前端是这样:
Vue / React 项目
├── 页面
├── 组件
├── 资源
├── 路由
├── 状态
└── 构建配置
Godot 大概是这样:
Godot 项目
├── Scene 场景
├── Node 节点
├── Resource 资源
├── Script 脚本
├── Project Settings 项目配置
└── Export 导出配置
你现在做 RPG、星露谷类似游戏,最常接触的是:
2D 场景
角色节点
地图节点
碰撞节点
摄像机节点
UI 节点
资源文件
脚本逻辑
2. Project:项目是什么
Godot 的 Project 就是整个游戏工程。
一个 Godot 项目一般会有:
my_game/
├── project.godot
├── scenes/
├── scripts/
├── assets/
├── resources/
└── .godot/
常见目录说明
project.godot
这是 Godot 项目的核心配置文件,类似前端里的 package.json + vite.config.ts + 项目配置 的混合体。
.godot/
Godot 自动生成的缓存目录,通常不需要手动改,也一般不提交到 Git。
scenes/
放场景文件,比如:
player.tscn
main.tscn
world.tscn
house.tscn
scripts/
放 GDScript 脚本,比如:
player.gd
camera_controller.gd
weather_manager.gd
assets/
放图片、音频、字体、tileset 等资源。
resources/
放自定义资源,比如角色配置、物品配置、天气配置。
Project 重点理解
项目不是游戏画面本身。
项目只是一个“容器”,真正运行起来的是项目里指定的 主场景 Main Scene。
Godot 文档里提到,一个项目可以有很多 Scene,但引擎只需要一个主场景作为程序启动时首先加载的场景。主场景路径会保存到 project.godot 里,也可以通过 Project Settings 修改。(Godot Engine documentation)
前端类比
Godot Project ≈ 前端项目根目录
Main Scene ≈ main.tsx / App.vue / 根页面
project.godot ≈ 项目配置文件
比如你做一个 RPG:
Project: FantasyRPG
Main Scene: res://scenes/main.tscn
运行游戏时,Godot 先加载 main.tscn。
3. Scene:场景是什么
这是 Godot 里最重要的概念之一。
Scene 不只是“地图场景”。
在 Godot 里,Scene 更像一个“可复用的对象模板”。
它可以是:
一张地图
一个玩家
一个怪物
一个宝箱
一个 UI 面板
一个按钮
一个技能特效
一个天气系统
一个房子内部
Scene 的本质
一个 Scene 本质上是一棵 Node 树。
比如玩家 Scene:
Player CharacterBody2D
├── Sprite2D
├── CollisionShape2D
├── AnimationPlayer
└── Camera2D
这个整体可以保存为:
res://scenes/player.tscn
以后你想在地图里放玩家,不是重新搭一遍,而是把 player.tscn 实例化进去。
前端类比
Scene 很像 Vue / React 组件。
比如前端组件:
<Player>
<Avatar />
<HealthBar />
<Inventory />
</Player>
Godot Scene:
Player
├── Sprite2D
├── CollisionShape2D
├── HealthBar
└── Inventory
但 Godot Scene 比前端组件更“实体化”。
它不仅有 UI,还能有:
位置
碰撞
动画
物理
声音
摄像机
输入处理
Scene 的几个核心特点
1. Scene 有一个根节点
每个 Scene 都必须有一个 root node。
比如:
Main Node2D
├── TileMapLayer
├── Player
├── Camera2D
└── CanvasLayer
这里 Main 就是根节点。
2. Scene 可以嵌套 Scene
比如你的主场景:
Main
├── WorldMap
├── Player
├── WeatherManager
└── UI
其中 Player 可以是另一个 .tscn 文件实例化进来的。
3. Scene 可以重复使用
一个怪物 Scene 可以实例化 100 次。
Slime.tscn
可以生成:
Slime_001
Slime_002
Slime_003
...
这就像前端里同一个组件渲染多个实例。
4. Node:节点是什么
Node 是 Godot 里最小的功能单位。
每个 Node 通常负责一件事。
比如:
| 节点 | 作用 |
|---|---|
| Node | 最基础节点,没有 2D/3D 位置 |
| Node2D | 2D 节点基础类,有位置、旋转、缩放 |
| Sprite2D | 显示一张 2D 图片 |
| CharacterBody2D | 2D 角色物理移动 |
| CollisionShape2D | 碰撞形状 |
| Camera2D | 2D 摄像机 |
| Label | 显示文字 |
| Button | 按钮 |
| AudioStreamPlayer | 播放声音 |
| AnimationPlayer | 播放动画 |
Godot 官方文档把 Node 称为构建块,节点可以作为其他节点的子节点,形成树状结构;同一父节点下的兄弟节点名称需要唯一。(Godot Engine documentation)
前端类比
前端 DOM:
<div class="player">
<img class="sprite" />
<div class="health-bar"></div>
</div>
Godot 节点树:
Player CharacterBody2D
├── Sprite2D
├── CollisionShape2D
└── HealthBar Control
前端里一个元素通常只是显示结构。
Godot 里的 Node 通常带有行为能力,比如:
Sprite2D 负责显示
CollisionShape2D 负责碰撞范围
CharacterBody2D 负责角色移动
Camera2D 负责镜头
AudioStreamPlayer 负责声音
Node 的常用属性
以 Node2D 为例,常见属性有:
position
rotation
scale
visible
z_index
position
节点在父节点坐标系里的位置。
position = Vector2(100, 200)
类似 CSS 里的:
left: 100px;
top: 200px;
但 Godot 里是游戏坐标,不是 DOM 布局。
rotation
旋转角度,单位通常是弧度。
rotation = 1.57
也可以用角度:
rotation_degrees = 90
scale
缩放。
scale = Vector2(2, 2)
visible
是否显示。
visible = false
类似:
display: none;
但注意:隐藏不等于删除,节点仍然存在。
z_index
2D 层级。
z_index = 10
类似 CSS 的:
z-index: 10;
Node 的常用方法
add_child
添加子节点。
add_child(enemy)
类似前端:
parent.appendChild(enemyElement)
get_node
根据路径查找节点。
var sprite = get_node("Sprite2D")
常见简写:
var sprite = $Sprite2D
如果是深层节点:
var hp_bar = $UI/HealthBar
queue_free
删除节点。
queue_free()
它不是立刻粗暴删除,而是排队到安全时机删除。
常用于:
怪物死亡
子弹命中
特效播放结束
临时 UI 关闭
get_parent
获取父节点。
var parent = get_parent()
get_tree
获取当前 SceneTree。
get_tree()
常用于切换场景、暂停游戏、查找分组节点。
5. SceneTree:节点树是什么
SceneTree 是游戏运行时真正存在的节点树。
你在编辑器里看到的是 .tscn 文件的结构。
游戏运行后,Godot 会把场景加载进 SceneTree。
官方文档说,SceneTree 管理节点层级和场景本身,可以添加、获取、删除节点,也可以暂停、切换、重新加载场景;SceneTree 也是 Godot 默认的 MainLoop 实现,负责游戏循环。(Godot Engine documentation)
编辑器里的 Scene 和运行时 SceneTree 的区别
编辑器里:
player.tscn
只是一个资源文件。
运行时:
Player 实例
才是真正活着的对象。
前端类比
前端里:
<Player />
是组件定义。
浏览器运行后:
真实 DOM 节点
才是实际存在的东西。
Godot 里:
Player.tscn
是场景模板。
运行后:
SceneTree 里的 Player 节点实例
才是实际存在的对象。
SceneTree 生命周期
Godot 节点进入 SceneTree 后,才真正“活了”。
官方文档说明,当节点直接或间接连接到 root viewport 后,它就成为 SceneTree 的一部分,并会获得处理输入、显示画面、播放声音、接收通知等能力;移出 SceneTree 后,这些能力也会失去。(Godot Engine documentation)
常见生命周期方法:
func _enter_tree():
pass
func _ready():
pass
func _process(delta):
pass
func _physics_process(delta):
pass
func _exit_tree():
pass
生命周期方法解释
_enter_tree
节点进入 SceneTree 时调用。
func _enter_tree():
print("进入场景树")
注意:这时候子节点不一定都准备好了。
_ready
节点和子节点都准备好之后调用。
func _ready():
print("准备完成")
常用于初始化:
func _ready():
$Sprite2D.visible = true
Godot 文档里说明,进入树时父节点的 _enter_tree() 会先于子节点触发,而 _ready() 调用顺序是从子节点往父节点。也就是说:enter 是父到子,ready 是子到父。 (Godot Engine documentation)
_process(delta)
每一帧调用。
func _process(delta):
position.x += 100 * delta
适合:
动画更新
非物理逻辑
计时
视觉效果
UI 更新
delta 是距离上一帧过去的时间,单位是秒。
比如:
position.x += 100 * delta
表示每秒移动 100 像素,不是每帧移动 100 像素。
_physics_process(delta)
固定物理帧调用。
func _physics_process(delta):
move_and_slide()
适合:
角色移动
碰撞
物理相关逻辑
角色移动一般优先放这里。
_exit_tree
节点离开 SceneTree 时调用。
func _exit_tree():
print("离开场景树")
6. Resource:资源是什么
Resource 是 Godot 中用来保存数据的对象。
它本身通常不“干活”,而是被节点使用。
常见 Resource:
Texture2D 图片纹理
AudioStream 音频
PackedScene 打包场景
TileSet 瓦片集
Animation 动画资源
Font 字体
Material 材质
Shader 着色器
自定义 Resource
前端类比
Resource 类似:
图片文件
音频文件
JSON 配置
CSS 变量
组件配置对象
接口 mock 数据
比如:
玩家节点 Sprite2D 使用 Texture2D
地图节点 TileMapLayer 使用 TileSet
音频节点 AudioStreamPlayer 使用 AudioStream
Resource 和 Node 的区别
这是新手很容易混的点。
| 类型 | 是否在场景树里 | 是否有位置 | 主要作用 |
|---|---|---|---|
| Node | 是 | 可能有 | 执行功能、参与游戏 |
| Resource | 否 | 没有 | 保存数据、被节点引用 |
比如:
Sprite2D 是 Node
Texture2D 是 Resource
Sprite2D 负责“显示图片”。
Texture2D 是“图片数据”。
Resource 的典型例子
图片资源
res://assets/player.png
被 Sprite2D.texture 使用。
场景资源
res://scenes/player.tscn
被 PackedScene 加载后实例化。
var player_scene = preload("res://scenes/player.tscn")
var player = player_scene.instantiate()
add_child(player)
这里:
preload("res://scenes/player.tscn")
拿到的是一个 PackedScene 资源。
instantiate()
才生成真正的节点实例。
7. Script:脚本是什么
Script 是挂在 Node 上的代码。
在 Godot 里,脚本不是孤立运行的,它通常依附于某个节点。
比如:
Player CharacterBody2D
└── player.gd
脚本控制这个玩家节点:
extends CharacterBody2D
func _ready():
print("玩家生成了")
前端类比
Vue 组件:
<script setup>
const speed = 100
</script>
Godot 节点脚本:
extends CharacterBody2D
var speed = 100
你可以理解为:
Node 是结构和能力
Script 是这个 Node 的业务逻辑
extends 是什么
extends CharacterBody2D
表示这个脚本继承自 CharacterBody2D。
也就是说,这个脚本会拥有 CharacterBody2D 的能力。
比如:
move_and_slide()
velocity
is_on_floor()
如果写:
extends Node2D
那它就没有角色物理移动能力。
这跟前端类比不完全一样,更像:
class Player extends CharacterBody2D {}
常见脚本结构
extends CharacterBody2D
@export var speed: float = 120.0
@onready var sprite: Sprite2D = $Sprite2D
func _ready():
print("Player ready")
func _physics_process(delta):
var direction = Vector2.ZERO
if Input.is_action_pressed("ui_right"):
direction.x += 1
if Input.is_action_pressed("ui_left"):
direction.x -= 1
if Input.is_action_pressed("ui_down"):
direction.y += 1
if Input.is_action_pressed("ui_up"):
direction.y -= 1
velocity = direction.normalized() * speed
move_and_slide()
重点解释
@export
把变量暴露到 Inspector 面板。
@export var speed = 120
这样你不用改代码,就能在编辑器里调速度。
类似前端组件的 props,但不是完全一样:
<Player speed={120} />
@onready
等节点 ready 后再获取子节点。
@onready var sprite = $Sprite2D
因为脚本刚加载时,子节点可能还没准备好。
Input.is_action_pressed
检测输入动作。
Input.is_action_pressed("ui_right")
注意这里检测的是“动作名”,不是具体键盘按键。
按键绑定在:
Project > Project Settings > Input Map
Godot 的官方 2D 入门教程也是通过 Project Settings 的 Input Map 添加移动动作。(Godot Engine documentation)
8. Signal:信号是什么
Signal 是 Godot 的事件系统。
官方文档说,Signal 是节点在特定事情发生时发出的消息,比如按钮被按下;其他节点可以连接这个信号,并在信号发生时调用函数。Signal 可以让一个游戏对象响应另一个对象的变化,同时减少彼此直接引用。(Godot Engine documentation)
前端类比
前端:
button.addEventListener("click", handleClick)
Vue:
<button @click="handleClick" />
React:
<button onClick={handleClick} />
Godot:
button.pressed.connect(_on_button_pressed)
内置信号
比如 Button 自带 pressed 信号。
func _ready():
$Button.pressed.connect(_on_button_pressed)
func _on_button_pressed():
print("按钮被点击了")
编辑器里也可以连接信号:
选中 Button
→ 右侧 Node 面板
→ Signals
→ pressed
→ Connect
Godot 会自动生成类似:
func _on_button_pressed():
pass
自定义信号
你也可以自己定义信号。
signal health_changed(current_health)
发出信号:
health_changed.emit(80)
连接信号:
player.health_changed.connect(_on_player_health_changed)
响应:
func _on_player_health_changed(current_health):
print("玩家血量变了:", current_health)
Signal 适合用在哪里
Signal 很适合“我发生了某件事,谁关心谁处理”。
比如:
玩家血量变化 → UI 更新血条
怪物死亡 → 掉落物品
宝箱打开 → 播放音效
按钮点击 → 切换界面
天气改变 → 地图颜色变化
时间到晚上 → 打开灯光
进入房间区域 → 切换室内音乐
不要这样写
player.ui.health_bar.value = player.health
这会让 Player 强依赖 UI。
更好的方式:
signal health_changed(value)
func take_damage(amount):
health -= amount
health_changed.emit(health)
然后 UI 自己监听:
player.health_changed.connect(_on_health_changed)
这就像前端里避免组件乱拿别的组件实例,改用事件或状态流。
第一部分核心心智模型
你先记住这张关系图:
Godot Project
└── Main Scene
└── Root Node
├── Child Node
├── Child Node
└── Instanced Scene
└── Node
├── Script
├── Resource
└── Signal
换成具体 RPG 项目:
FantasyRPG Project
└── main.tscn
└── Main Node2D
├── World TileMapLayer
├── Player CharacterBody2D
│ ├── Sprite2D
│ ├── CollisionShape2D
│ └── player.gd
├── Camera2D
├── WeatherManager Node
└── UI CanvasLayer
├── HealthBar
└── InventoryPanel
新手最容易混的几个点
1. Scene 不是只能表示地图
Scene 可以是地图,也可以是玩家、怪物、UI、道具、技能。
你以后要习惯:
能复用的东西,都可以做成 Scene。
2. Node 不是图片
Sprite2D 是节点,图片是它的 texture 资源。
Sprite2D = 显示图片的节点
Texture2D = 图片资源
3. Resource 不是 Node
Resource 不会出现在 SceneTree 里。
它只是被节点引用的数据。
4. Script 一般挂在 Node 上
Godot 不是先写一堆 JS 模块再渲染页面。
Godot 更常见的是:
创建节点 → 挂脚本 → 节点自己控制自己
5. Signal 不只是按钮点击
Signal 是 Godot 的事件通信方式。
以后你做 RPG,很多系统都可以用信号解耦:
玩家状态变化
天气变化
时间变化
任务变化
背包变化
地图切换
NPC 对话触发
建议你现在记住的最小知识集
先不用背太多,第一部分只要记住这些:
1. Project 是整个游戏工程。
2. Main Scene 是游戏启动入口。
3. Scene 是可复用的节点树,不只是地图。
4. Node 是 Godot 的功能单位。
5. SceneTree 是游戏运行时真正活着的节点树。
6. Resource 是资源和数据,不是场景里的对象。
7. Script 挂在节点上,控制节点行为。
8. Signal 是 Godot 的事件通信机制。
一个最小完整例子
假设我们要做一个最简单的玩家。
场景结构:
Player CharacterBody2D
├── Sprite2D
└── CollisionShape2D
脚本:
extends CharacterBody2D
@export var speed: float = 120.0
func _physics_process(delta):
var direction = Vector2.ZERO
if Input.is_action_pressed("ui_right"):
direction.x += 1
if Input.is_action_pressed("ui_left"):
direction.x -= 1
if Input.is_action_pressed("ui_down"):
direction.y += 1
if Input.is_action_pressed("ui_up"):
direction.y -= 1
velocity = direction.normalized() * speed
move_and_slide()
这里你能看到第一部分所有核心概念:
Player 是一个 Scene
CharacterBody2D 是根 Node
Sprite2D 是子 Node
CollisionShape2D 是子 Node
player.gd 是 Script
speed 是可配置属性
Input 是输入系统
_physics_process 是生命周期方法
move_and_slide 是角色移动方法
总结一句话
Godot 不是先写代码再生成画面,而是先用 Node 搭出 Scene,再用 Script 控制这些 Node,在 SceneTree 里运行起来。
如果您觉得这篇文章有帮助,请点个赞吧~
评论
请登录后发表评论
去登录