强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Godot 3 GDScript 教程 / 2D 基础:Sprite 与动画

2D 基础:Sprite 与动画

Sprite 节点

Sprite 是 Godot 2D 中最基础的显示节点,用于显示一张纹理(图片)。

基本属性

属性类型说明
textureTexture显示的纹理
offsetVector2纹理偏移
flip_hbool水平翻转
flip_vbool垂直翻转
region_enabledbool启用区域裁剪
region_rectRect2裁剪区域
centeredbool是否以节点位置为中心
modulateColor颜色调制
self_modulateColor自身颜色调制(不影响子节点)

代码中使用 Sprite

extends Sprite

func _ready() -> void:
    # 加载纹理
    texture = load("res://assets/sprites/player.png")
    
    # 或使用预加载
    texture = preload("res://assets/sprites/player.png")
    
    # 水平翻转(面向不同方向)
    flip_h = false  # 向右
    flip_h = true   # 向左
    
    # 区域裁剪(图集中选择单帧)
    region_enabled = true
    region_rect = Rect2(0, 0, 32, 32)  # 裁剪左上角 32x32 区域
    
    # 颜色效果
    modulate = Color.red  # 染成红色
    modulate.a = 0.5      # 半透明

func flash_damage() -> void:
    modulate = Color.red
    yield(get_tree().create_timer(0.1), "timeout")
    modulate = Color.white

AnimatedSprite

AnimatedSprite 用于播放帧动画,适合角色行走、待机等动画。

设置 AnimatedSprite

  1. 添加 AnimatedSprite 节点
  2. 在 Inspector 中新建 SpriteFrames 资源
  3. 在 SpriteFrames 编辑器中添加动画和帧

代码控制

extends KinematicBody2D

onready var anim_sprite = $AnimatedSprite

func _process(delta: float) -> void:
    var velocity = _get_input()
    
    if velocity.length() > 0:
        anim_sprite.play("walk")
        anim_sprite.flip_h = velocity.x < 0  # 根据方向翻转
    else:
        anim_sprite.play("idle")

func attack() -> void:
    anim_sprite.play("attack")
    yield(anim_sprite, "animation_finished")  # 等待动画完成
    anim_sprite.play("idle")

func _get_input() -> Vector2:
    var vel = Vector2.ZERO
    if Input.is_action_pressed("move_right"):
        vel.x += 1
    if Input.is_action_pressed("move_left"):
        vel.x -= 1
    return vel.normalized()

SpriteFrames 编辑器动画设置

动画名帧数FPS循环用途
idle48待机
walk612行走
run616奔跑
jump310跳跃
attack515攻击
hurt28受伤
die48死亡

图集(Atlas Texture)

图集将多个小图合并为一张大图,减少绘制调用(Draw Call),提升性能。

使用 AtlasTexture

# 在代码中使用 AtlasTexture
extends Sprite

func set_frame(frame_index: int, frame_size: Vector2) -> void:
    var atlas = AtlasTexture.new()
    atlas.atlas = preload("res://assets/sprites/characters.png")
    atlas.region = Rect2(
        frame_index * frame_size.x,
        0,
        frame_size.x,
        frame_size.y
    )
    texture = atlas

💡 提示:在 Godot 编辑器中,可以使用 Import 面板自动将图片切割为 AtlasTexture。

Camera2D

Camera2D 控制 2D 游戏中的视口显示区域。

基本设置

extends Camera2D

export var smoothing_speed: float = 5.0
export var look_ahead: float = 100.0

func _ready() -> void:
    # 启用平滑移动
    smoothing_enabled = true
    smoothing_speed = 5.0
    
    # 启用拖拽(相机延迟跟随,适合平台游戏)
    drag_margin_h_enabled = true
    drag_margin_v_enabled = true
    drag_margin_left = 0.2
    drag_margin_right = 0.2
    drag_margin_top = 0.2
    drag_margin_bottom = 0.2

func _process(delta: float) -> void:
    # 跟随玩家
    global_position = global_position.linear_interpolate(
        $Player.global_position,
        smoothing_speed * delta
    )

相机震动效果

extends Camera2D

var shake_amount: float = 0.0
var shake_decay: float = 5.0

func shake(amount: float) -> void:
    shake_amount = amount

func _process(delta: float) -> void:
    if shake_amount > 0:
        offset = Vector2(
            rand_range(-shake_amount, shake_amount),
            rand_range(-shake_amount, shake_amount)
        )
        shake_amount = max(0, shake_amount - shake_decay * delta)
    else:
        offset = Vector2.ZERO

相机边界限制

extends Camera2D

func _ready() -> void:
    # 设置相机限制(不超出关卡边界)
    limit_left = 0
    limit_top = 0
    limit_right = 2000
    limit_bottom = 1000
    
    # 启用限制平滑
    limit_smoothed = true

视口(Viewport)

Viewport 用于渲染到纹理、分屏、小地图等效果。

小地图实现

extends Control

# 通过 SubViewport 显示小地图
onready var minimap_camera = $SubViewport/Camera2D
onready var minimap_display = $MinimapRect

func _ready() -> void:
    # 配置小地图视口
    $SubViewport.size = Vector2(200, 200)
    $SubViewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS

func _process(delta: float) -> void:
    # 小地图相机跟随玩家
    minimap_camera.global_position = $Player.global_position
    minimap_camera.zoom = Vector2(3, 3)  # 缩小视野范围

2D 光照

Godot 2D 支持动态光照效果。

灯光节点

节点功能
Light2D点光源/聚光灯
Line2D线条绘制
Polygon2D多边形绘制

光照代码示例

extends Light2D

export var flicker_speed: float = 10.0
export var flicker_amount: float = 0.2
var base_energy: float = 1.0

func _ready() -> void:
    base_energy = energy

func _process(delta: float) -> void:
    # 火把闪烁效果
    energy = base_energy + rand_range(-flicker_amount, flicker_amount)
    texture_scale = 1.0 + rand_range(-0.05, 0.05)

⚠️ 注意:2D 光照需要被照亮的节点开启 Light → Enabled,并且需要设置适当的混合模式。

Parallax 背景

ParallaxBackground 和 ParallaxLayer 实现视差滚动效果,增加场景深度感。

设置方式

  1. 添加 ParallaxBackground 节点
  2. 添加 ParallaxLayer 子节点
  3. 设置滚动比率(motion_scale)

代码示例

extends ParallaxBackground

func _process(delta: float) -> void:
    # 自动滚动背景
    scroll_offset.x -= 50 * delta  # 永不停止的横向滚动
    
    # 或跟随相机自动滚动(默认行为)

ParallaxLayer 设置

# 远景层(移动慢)- motion_scale 较小
$ParallaxLayer.motion_scale = Vector2(0.5, 0.5)

# 中景层
$ParallaxLayer2.motion_scale = Vector2(0.8, 0.8)

# 近景层(移动快)- motion_scale 接近 1
$ParallaxLayer3.motion_scale = Vector2(0.95, 0.95)

多层背景示例

# 层级结构:
# ParallaxBackground
# ├── ParallaxLayer (远山, motion_scale = 0.2)
# │   └── Sprite
# ├── ParallaxLayer (中景树, motion_scale = 0.5)
# │   └── Sprite
# └── ParallaxLayer (近景灌木, motion_scale = 0.8)
#     └── Sprite

# 设置无限重复
func _ready() -> void:
    # 对每个 ParallaxLayer 设置重复
    $FarMountains.motion_mirroring = Vector2(1920, 0)
    $MidTrees.motion_mirroring = Vector2(1920, 0)
    $NearBushes.motion_mirroring = Vector2(1920, 0)

动画关键帧(AnimationPlayer)

AnimationPlayer 是 Godot 最强大的动画系统,可以动画化任何节点属性。

创建动画

extends Node2D

onready var anim = $AnimationPlayer

func _ready() -> void:
    # 播放动画
    anim.play("idle")
    
    # 播放动画(带过渡时间)
    anim.play("walk", 0.2)
    
    # 倒放
    anim.play_backwards("open_door")
    
    # 暂停/恢复
    anim.stop()
    anim.play()  # 从暂停位置继续
    
    # 设置播放速度
    anim.playback_speed = 2.0  # 2倍速

# 动画完成信号
func _on_AnimationPlayer_animation_finished(anim_name: String) -> void:
    match anim_name:
        "attack":
            anim.play("idle")
        "die":
            queue_free()

动画关键帧轨道类型

轨道类型用途示例
Property属性动画位置、颜色、透明度
Call Method方法调用在特定帧播放音效
Bezier贝塞尔曲线复杂的缓动效果
Audio音频播放精确的音效时机
Animation动画嵌套复杂动画组合

AnimationPlayer 代码创建动画

extends Node2D

func _ready() -> void:
    var anim_player = AnimationPlayer.new()
    add_child(anim_player)
    
    # 创建新动画
    var anim = Animation.new()
    var track_index = anim.add_track(Animation.TYPE_VALUE)
    
    # 设置轨道路径: NodePath("Sprite:modulate")
    anim.track_set_path(track_index, NodePath("Sprite:modulate"))
    
    # 添加关键帧
    anim.track_insert_key(track_index, 0.0, Color.white)
    anim.track_insert_key(track_index, 0.2, Color.red)
    anim.track_insert_key(track_index, 0.4, Color.white)
    
    # 添加到动画库
    anim_player.add_animation("flash_damage", anim)
    anim_player.play("flash_damage")

Tween 补间动画

Tween 节点用于属性平滑过渡,适合 UI 动画和简单补间。

Tween 基本使用

extends Node2D

onready var tween = $Tween

func move_to(target_pos: Vector2, duration: float = 0.5) -> void:
    # 停止之前的补间
    tween.stop_all()
    
    # 补间属性: (对象, 属性, 起始值, 终止值, 时间, 过渡类型, 缓动类型)
    tween.interpolate_property(
        self,               # 目标对象
        "position",         # 属性名
        position,           # 起始值
        target_pos,         # 终止值
        duration,           # 持续时间
        Tween.TRANS_QUAD,   # 过渡类型
        Tween.EASE_OUT      # 缓动类型
    )
    tween.start()

func fade_out(duration: float = 0.3) -> void:
    tween.stop_all()
    tween.interpolate_property(
        self, "modulate:a",
        modulate.a, 0.0, duration,
        Tween.TRANS_LINEAR, Tween.EASE_IN
    )
    tween.start()

func bounce_effect() -> void:
    var original_scale = scale
    tween.stop_all()
    tween.interpolate_property(
        self, "scale",
        original_scale * 1.3, original_scale, 0.3,
        Tween.TRANS_ELASTIC, Tween.EASE_OUT
    )
    tween.start()

Tween 过渡类型

类型说明
TRANS_LINEAR线性
TRANS_SINE正弦
TRANS_QUAD二次方
TRANS_CUBIC三次方
TRANS_QUART四次方
TRANS_QUINT五次方
TRANS_EXPO指数
TRANS_ELASTIC弹性
TRANS_BOUNCE弹跳
TRANS_BACK回弹

Tween 缓动类型

类型说明
EASE_IN先慢后快
EASE_OUT先快后慢
EASE_IN_OUT中间快两头慢
EASE_OUT_IN中间慢两头快

Tween 连续与并行动画

extends Control

onready var tween = $Tween

func show_menu() -> void:
    tween.stop_all()
    
    # 连续动画(第一个完成后开始第二个)
    tween.interpolate_property(
        self, "rect_position:x",
        -200, 0, 0.5,
        Tween.TRANS_BACK, Tween.EASE_OUT
    )
    tween.start()
    yield(tween, "tween_all_completed")
    
    # 并行动画(同时进行)
    tween.interpolate_property(
        $Title, "modulate:a",
        0, 1, 0.3,
        Tween.TRANS_LINEAR, Tween.EASE_IN
    )
    tween.interpolate_property(
        $Button, "modulate:a",
        0, 1, 0.3,
        Tween.TRANS_LINEAR, Tween.EASE_IN
    )
    tween.start()

Tween 循环

# 循环播放
tween.set_repeat(true)

# 来回循环(yoyo 效果)
# 通过两次 interpolate 实现
func floating_effect() -> void:
    var base_y = position.y
    tween.interpolate_property(
        self, "position:y",
        base_y, base_y - 10, 1.0,
        Tween.TRANS_SINE, Tween.EASE_IN_OUT
    )
    tween.interpolate_property(
        self, "position:y",
        base_y - 10, base_y, 1.0,
        Tween.TRANS_SINE, Tween.EASE_IN_OUT,
        1.0  # 延迟 1 秒后开始
    )
    tween.set_repeat(true)
    tween.start()

⚠️ 注意:Godot 4 中 Tween 的用法完全不同(不再使用 Tween 节点,改为 create_tween())。

2D 粒子(Particles2D)

Particles2D 用于创建 2D 粒子效果,如火焰、烟雾、灰尘等。

基本设置

extends Particles2D

func _ready() -> void:
    # 设置粒子材质
    var material = ParticlesMaterial.new()
    
    # 发射方向
    material.direction = Vector3(0, -1, 0)  # 向上
    material.spread = 15.0                   # 扩散角度
    
    # 初始速度
    material.initial_velocity = 100.0
    material.initial_velocity_random = 0.3
    
    # 生命周期
    material.lifetime = 1.0
    material.lifetime_random = 0.2
    
    # 重力
    material.gravity = Vector3(0, 98, 0)  # 向下
    
    # 颜色
    material.color_ramp = _create_color_ramp()
    
    # 缩放
    material.scale_curve = _create_scale_curve()
    
    process_material = material
    
    # 发射量
    amount = 50
    emitting = true
    lifetime = 1.0

func _create_color_ramp() -> GradientTexture:
    var gradient = Gradient.new()
    gradient.set_color(0, Color(1, 0.5, 0, 1))   # 橙色
    gradient.set_color(1, Color(1, 0, 0, 0))      # 透明红
    var texture = GradientTexture.new()
    texture.gradient = gradient
    return texture

常见粒子效果参数

效果directionspreadgravitylifetime
火焰(0, -1, 0)15°(0, -20, 0)0.5-1s
烟雾(0, -1, 0)30°(0, -10, 0)1-2s
灰尘各方向360°(0, 20, 0)0.3-0.5s
雨滴(0, 1, 0)(0, 500, 0)0.5-1s
星星(0, -1, 0)360°(0, 0, 0)0.5-1s
爆炸各方向360°(0, 50, 0)0.3-0.5s

代码触发粒子

extends Node2D

onready var particles = $Particles2D

func emit_explosion() -> void:
    particles.emitting = true
    # 或使用 one_shot 模式
    # particles.one_shot = true
    # particles.restart()

func stop_particles() -> void:
    particles.emitting = false

游戏开发场景

场景:角色动画状态机

extends KinematicBody2D

enum State { IDLE, WALK, JUMP, ATTACK, HURT }
var current_state: int = State.IDLE

onready var anim_sprite = $AnimatedSprite
onready var tween = $Tween

export var speed: float = 200.0
var velocity: Vector2 = Vector2.ZERO

func _physics_process(delta: float) -> void:
    match current_state:
        State.IDLE:
            _process_idle(delta)
        State.WALK:
            _process_walk(delta)
        State.JUMP:
            _process_jump(delta)

func _process_idle(_delta: float) -> void:
    velocity = Vector2.ZERO
    if Input.is_action_pressed("move_left") or Input.is_action_pressed("move_right"):
        _change_state(State.WALK)
    if Input.is_action_just_pressed("jump"):
        _change_state(State.JUMP)
    if Input.is_action_just_pressed("attack"):
        _change_state(State.ATTACK)

func _process_walk(delta: float) -> void:
    velocity.x = _get_input_direction() * speed
    velocity = move_and_slide(velocity)
    
    anim_sprite.flip_h = velocity.x < 0
    
    if velocity.x == 0:
        _change_state(State.IDLE)
    if Input.is_action_just_pressed("jump"):
        _change_state(State.JUMP)

func _change_state(new_state: int) -> void:
    current_state = new_state
    match new_state:
        State.IDLE:
            anim_sprite.play("idle")
        State.WALK:
            anim_sprite.play("walk")
        State.JUMP:
            anim_sprite.play("jump")
        State.ATTACK:
            anim_sprite.play("attack")
        State.HURT:
            anim_sprite.play("hurt")
            tween.interpolate_property(
                self, "modulate",
                Color.red, Color.white, 0.3
            )
            tween.start()

func _get_input_direction() -> float:
    var dir = 0
    if Input.is_action_pressed("move_right"):
        dir += 1
    if Input.is_action_pressed("move_left"):
        dir -= 1
    return dir

扩展阅读