Godot 引擎采用“万物皆节点”的设计哲学,其 2D 游戏开发的核心在于对各种 2D 节点的理解和运用。这些节点提供了从基础绘制、运动控制到物理模拟、用户界面等一系列功能,它们通过层级结构(场景树)组织起来,共同构建出完整的游戏世界。本篇将详细介绍 Godot 中最常用的一些 2D 节点及其核心功能与典型应用。

核心概念:

  • 节点 (Node): Godot 中最小的功能单元,所有对象都是节点。
  • 场景树 (SceneTree): 节点以树形结构组织起来,形成一个场景。
  • 2D 节点基类: Node2D 是所有可见 2D 节点的基类,提供了位置、旋转、缩放等基本变换属性。

一、2D 节点基类:Node2D

Node2D 是所有需要在 2D 场景中拥有位置、旋转和缩放的节点的基类。它本身不渲染任何东西,但提供了所有 2D 对象的通用变换属性和方法。

1.1 核心属性

  • position (Vector2): 节点在父节点坐标系中的二维位置。
  • rotation (float): 节点相对于其父节点的旋转角度(弧度制)。
  • rotation_degrees (float): 节点旋转角度(角度制,更易读)。
  • scale (Vector2): 节点在 X 和 Y 方向上的缩放因子。
  • global_position (Vector2): 节点在世界坐标系中的位置。
  • global_rotation (float): 节点在世界坐标系中的旋转角度。
  • global_scale (Vector2): 节点在世界坐标系中的缩放因子。
  • transform (Transform2D): 包含 position, rotation, scale 的组合变换矩阵。

1.2 典型应用

  • 作为其他 2D 节点的父节点,用于组织和分组对象。
  • 作为仅拥有位置、旋转、缩放属性的空对象,用于定位或作为其他组件的参照点。
  • 继承创建自定义 2D 节点。

二、可见 2D 节点:CanvasItem 及其子类

CanvasItem 是所有 2D 可见节点的基类,它引入了绘图、Z 索引、可见性、材质、混合模式等与渲染相关的属性。

2.1 Sprite

Sprite 是 Godot 中最常用的显示 2D 图像的节点。

  • 核心功能: 显示一张纹理 (Texture)。
  • 关键属性:
    • texture (Texture2D): 要显示的图像资源。
    • flip_h (bool): 水平翻转图像。
    • flip_v (bool): 垂直翻转图像。
    • offset (Vector2): 图像相对于节点原点的偏移量。
    • modulate (Color): 调制精灵的颜色,可用于着色或制作闪烁效果。
    • region_enabled (bool), region_rect (Rect2): 如果设置为 true,则只显示纹理的指定区域,常用于图集 (atlas)。
  • 典型应用:
    • 游戏角色、敌人、道具、背景元素等静态或简单动画的图像显示。
    • UI 元素的图标或背景图。
1
2
3
4
5
6
7
8
9
10
# 示例: 动态改变 Sprite 的纹理和颜色
extends Sprite

func _ready():
# 假设有一个预加载的纹理
# texture = preload("res://assets/player.png")
modulate = Color(1, 0, 0, 1) # 将精灵变为红色

func _process(delta):
rotation += deg2rad(30) * delta # 每秒旋转 30 度

2.2 AnimatedSprite2D

AnimatedSprite2D 用于播放帧动画,它将一个 SpriteFrames 资源中的多个动画序列进行播放。

  • 核心功能: 播放动画序列。
  • 关键属性:
    • sprite_frames (SpriteFrames): 包含所有动画帧和动画序列的资源。可以在编辑器中创建和管理。
    • animation (StringName): 当前正在播放的动画名称。
    • frame (int): 当前显示的动画帧索引。
    • playing (bool): 控制动画是否播放。
    • speed_scale (float): 动画播放速度的缩放因子。
  • 核心方法:
    • play(name: StringName = "", custom_speed: float = 1.0, from_end: bool = false) 播放指定名称的动画。
    • stop() 停止当前动画。
  • 信号: animation_finished (当动画播放完毕且未循环时发出)。
  • 典型应用:
    • 玩家角色(行走、跳跃、攻击等)的复杂帧动画。
    • 敌人、NPC 的动画行为。
    • 各种特效动画(爆炸、拾取物品)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 示例: 播放动画
extends AnimatedSprite2D

func _ready():
play("idle") # 播放名为 "idle" 的动画

func _process(delta):
if Input.is_action_pressed("ui_right"):
play("run") # 玩家移动时播放 "run" 动画
flip_h = false
elif Input.is_action_pressed("ui_left"):
play("run")
flip_h = true
else:
play("idle") # 否则播放 "idle" 动画

func _on_animation_finished():
print("Animation finished!")

2.3 TextureRect (UI 节点,但常用于显示图像)

TextureRect 是一种 UI 控件节点,它也用于显示纹理,但与 Sprite 的主要区别在于其对布局和 UI 属性的支持。

  • 核心功能: 显示纹理,并遵循 UI 布局规则。
  • 关键属性:
    • texture (Texture2D): 要显示的图像。
    • expand_mode (ExpandMode): 定义纹理如何适应节点的尺寸(如 EXPAND_FIT_WIDTH_PROPORTIONAL)。
    • stretch_mode (StretchMode): 定义纹理如何拉伸以填充 TextureRect 的边界。
    • flip_h, flip_v 翻转图像。
  • 典型应用:
    • UI 背景、图标、血条填充。
    • 作为 Control 节点的一部分,利用其布局容器进行尺寸和位置管理。

2.4 ParallaxBackgroundParallaxLayer

用于创建视差滚动效果,常用于游戏背景。

  • ParallaxBackground 视差背景的容器,通常作为 Viewport 的子节点或根节点的子节点。
  • ParallaxLayer 包含实际背景元素的层。每个 ParallaxLayer 都有自己的滚动速度。
    • motion_scale (Vector2): 决定该层相对于相机移动的速度。Vector2(0.5, 0.5) 表示移动速度是相机的一半。
    • motion_offset (Vector2): 层的偏移量,用于循环背景。
  • 典型应用:
    • 制作多层深度感的背景,例如,近景移动快,远景移动慢。
1
2
3
4
5
6
7
8
9
10
11
12
# Scene Tree Structure Example
- Viewport (or root Node)
- Camera2D
- ParallaxBackground
- ParallaxLayer (远景)
- Sprite (天空)
- ParallaxLayer (中景)
- Sprite (山)
- ParallaxLayer (近景)
- Sprite (树)
- Player
- World

三、碰撞与物理节点:CollisionObject2D 及其子类

CollisionObject2D 是所有 2D 物理对象的基类,它提供了碰撞检测和物理模拟所需的基础功能。其子类通常需要一个或多个 CollisionShape2DRectangleShape2D 等形状节点作为子节点来定义它们的碰撞区域。

3.1 Area2D

Area2D 节点用于检测与其他物理对象(RigidBody2D, KinematicBody2D, StaticBody2D, TileMap)的重叠(overlap),但不参与物理模拟本身。

  • 核心功能: 触发器、范围检测、拾取物品。
  • 信号:
    • body_entered(body: Node2D):当一个 PhysicsBody2D 进入 Area2D 时发出。
    • body_exited(body: Node2D):当一个 PhysicsBody2D 离开 Area2D 时发出。
    • area_entered(area: Area2D):当另一个 Area2D 进入时发出。
  • 典型应用:
    • 玩家进入一个区域,触发对话或事件。
    • 拾取物品(如金币、药水)。
    • 检测子弹是否击中敌人(子弹本身可能是 KinematicBody2DRigidBody2D,而敌人上的命中框可能是 Area2D)。
    • 陷阱区域、传送门。
1
2
3
4
5
6
7
# 示例: 拾取金币
extends Area2D

func _on_Coin_body_entered(body: Node2D):
if body.name == "Player": # 检查是否是玩家
print("Player picked up a coin!")
queue_free() # 销毁金币

3.2 StaticBody2D

StaticBody2D 是一个不动的物理对象,它不参与物理模拟,但会与其他物理对象产生碰撞。

  • 核心功能: 游戏世界的固定几何体。
  • 典型应用:
    • 地面、墙壁、平台、固定障碍物。
    • 任何不应移动或响应力的环境元素。
    • 注意: 如果需要有碰撞但没有物理模拟的地图,通常使用 TileMap 更高效。

3.3 KinematicBody2D

KinematicBody2D 是一个由代码控制移动的物理对象。它受物理引擎的碰撞检测影响,但不受重力、摩擦力等物理力的影响。开发者需要手动计算其移动和处理碰撞响应。

  • 核心功能: 玩家角色、可移动平台、由 AI 控制的敌人。
  • 核心方法:
    • move_and_slide(linear_velocity: Vector2, up_direction: Vector2 = Vector2(0, -1), stop_on_slope: bool = false, max_slides: int = 4, floor_max_angle: float = 0.785398, infinite_inertia: bool = true) 根据给定的速度移动物体,并处理碰撞和滑动。常用于平台游戏中的角色移动。
    • move_and_collide(linear_velocity: Vector2, test_only: bool = false, collide_with_bodies: bool = true, collide_with_areas: bool = false, recovery_as_collision: bool = false) 移动物体并返回第一个碰撞信息,但不会滑动。
  • 典型应用:
    • 玩家角色: 精确控制玩家的跳跃、行走、冲刺等行为,同时与其他物体发生碰撞。
    • 移动平台: 按照预设路径移动,并与玩家或其他物体发生碰撞。
    • 子弹: 精准移动并检测碰撞。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 示例: KinematicBody2D 玩家移动
extends KinematicBody2D

const SPEED = 200
const JUMP_VELOCITY = -400
const GRAVITY = 800

var velocity = Vector2.ZERO

func _physics_process(delta):
# 处理重力
velocity.y += GRAVITY * delta

# 处理水平移动
var input_x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
velocity.x = input_x * SPEED

# 处理跳跃
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY

# 移动并滑动
velocity = move_and_slide(velocity, Vector2.UP)

3.4 RigidBody2D

RigidBody2D 是一个完全由物理引擎模拟的物理对象。它受重力、摩擦力、弹力以及外部力(如冲量、施加的力)的影响。

  • 核心功能: 模拟真实物理行为的对象。
  • 关键属性:
    • mode (Mode): RigidBody2D 的行为模式:MODE_RIGID (完全物理模拟)、MODE_STATIC (像 StaticBody2D 一样不动)、MODE_KINEMATIC (像 KinematicBody2D 一样由代码控制,但依然响应物理碰撞)、MODE_CHARACTER (特殊的 KinematicBody2D 模式)。
    • mass (float): 物体的质量。
    • friction (float): 摩擦系数。
    • bounce (float): 弹力系数 (0-1)。
    • linear_velocity (Vector2): 线性速度。
    • angular_velocity (float): 角速度。
    • gravity_scale (float): 对重力的敏感度。
  • 核心方法:
    • apply_central_force(force: Vector2) 在中心点施加一个持续的力。
    • apply_force(force: Vector2, position: Vector2) 在指定位置施加一个持续的力。
    • apply_central_impulse(impulse: Vector2) 在中心点施加一个瞬时冲量。
    • apply_impulse(impulse: Vector2, position: Vector2) 在指定位置施加一个瞬时冲量。
    • _integrate_forces(state: Physics2DDirectBodyState) 用于在物理引擎计算之前,自定义 RigidBody2D 的行为,比直接设置 linear_velocity 更安全。
  • 典型应用:
    • 弹球、箱子、可破坏的方块等受物理定律影响的对象。
    • 爆炸碎片、掉落的道具。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 示例: RigidBody2D 小球被点击后施加一个冲量
extends RigidBody2D

func _ready():
# 默认模式是 MODE_RIGID,受物理影响
pass

func _input(event):
if event.is_action_pressed("click"): # 假设点击事件映射为 "click"
# 获取点击位置的全局坐标
var click_pos = get_global_mouse_position()
# 计算从点击位置到小球中心的向量
var direction_to_center = global_position - click_pos
# 施加一个与方向相反的冲量,模拟“推”的感觉
apply_impulse(Vector2.ZERO, direction_to_center.normalized() * 500)

四、地图节点:TileMap

TileMap 节点允许你在 2D 网格上快速绘制基于瓷砖的地图,并能自动生成碰撞体、导航区域等。

  • 核心功能: 快速构建关卡地图。
  • 关键资源:
    • TileSet (TileSet): 包含所有瓷砖及其属性(纹理、碰撞形状、导航形状等)的资源。
  • 核心方法:
    • set_cell(layer: int, coords: Vector2i, tile_id: int, alternative_tile: int = 0) 在指定层和坐标设置瓷砖。
    • get_cell_tile_data(layer: int, coords: Vector2i) 获取指定位置的瓷砖数据。
    • map_to_world(map_coords: Vector2i, use_center: bool = true) 将地图坐标转换为世界坐标。
    • world_to_map(world_coords: Vector2) 将世界坐标转换为地图坐标。
  • 典型应用:
    • 平台游戏的地面、墙壁、背景。
    • 俯视角游戏的地图。
    • 自动生成碰撞体,方便与物理节点交互。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 示例: TileMap 动态修改瓷砖
extends TileMap

func _ready():
# 假设有一个包含 tile_id 0 和 1 的 TileSet
# 在 (5, 5) 处放置 tile_id 为 1 的瓷砖
set_cell(0, Vector2i(5, 5), 1)

func _input(event):
if event.is_action_pressed("click"):
var world_pos = get_global_mouse_position()
var map_coords = world_to_map(world_pos)
var current_tile_id = get_cell_source_id(0, map_coords)
if current_tile_id != -1: # 如果该位置有瓷砖
# 切换瓷砖ID,模拟破坏或放置
set_cell(0, map_coords, (current_tile_id + 1) % 2)

五、相机节点:Camera2D

Camera2D 用于控制 2D 场景的视图。一个场景可以有多个 Camera2D,但通常只有一个是激活的。

  • 核心功能: 控制屏幕显示哪个区域,以及如何缩放。
  • 关键属性:
    • current (bool): 如果设置为 true,则此相机是当前激活的相机。
    • zoom (Vector2): 相机的缩放因子(Vector2(1,1) 为 1:1 像素比)。
    • offset (Vector2): 相机相对于其 position 的额外偏移量。
    • limit_left, limit_top, limit_right, limit_bottom (int): 限制相机移动的世界坐标边界。
    • smoothing_enabled (bool), smoothing_speed (float): 启用平滑跟随目标。
    • follow_viewport_enabled (bool): 当父节点是 Viewport 时,启用此属性可以让相机自动跟随 Viewport 的大小变化。
    • process_callback (ProcessCallback): 相机更新的模式 (IDLEPHYSICS)。
  • 典型应用:
    • 跟随玩家角色移动。
    • 实现缩放效果(如瞄准、视野变化)。
    • 提供固定区域的俯视视角。
    • 在分屏游戏中为不同玩家提供单独的视图。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 示例: Camera2D 跟随玩家
# 假设 Camera2D 是 Player 节点的子节点
extends Camera2D

# 不需要在 _process 或 _physics_process 中手动设置 position
# 因为 Camera2D 默认就跟随其父节点
# 可以设置 limit 来限制相机移动的范围

func _ready():
make_current() # 确保此相机是激活相机
smoothing_enabled = true
smoothing_speed = 5.0
# limit_left = 0
# limit_top = 0
# limit_right = 1000
# limit_bottom = 800

六、其他重要 2D 节点

  • Line2D 绘制 2D 线条。
  • Polygon2D 绘制自定义的多边形。
  • Light2D 用于 2D 光照和阴影效果。
  • CanvasModulate 全局调制整个 2D 渲染的颜色。
  • Node2D 家族的各种碰撞形状节点: RectangleShape2D, CircleShape2D, CapsuleShape2D 等,它们必须作为 CollisionObject2D 的子节点来定义碰撞区域。
  • RayCast2D 用于从一点发射射线,检测碰撞。常用于检测前方是否有障碍物或地面。

七、总结

Godot 2D 节点的设计理念是模块化和组合性。每个节点都专注于特定的功能,通过将它们组合成场景树,开发者可以灵活地构建出各种复杂的游戏逻辑和视觉效果。

掌握 Node2D 的基本变换,理解 SpriteAnimatedSprite2D 的图像显示,区分 Area2DStaticBody2DKinematicBody2DRigidBody2D 的物理行为,并熟练运用 TileMapCamera2D 进行世界构建和视图控制,是成功开发 Godot 2D 游戏的关键。随着经验的积累,你将能更深入地利用这些节点,并通过继承和自定义来创造出更独特和强大的游戏元素。