第16章 粒子、线条与视觉特效
粒子系统结构示意图
GPUParticles2D 节点结构:
GPUParticles2D (节点)
└── ParticleProcessMaterial (材质)
├── Direction: Vector3(0, -1, 0) ← 发射方向(向上)
├── Spread: 45.0 ← 扩散角度
├── Initial Velocity: 100~200 ← 初始速度范围
├── Gravity: Vector3(0, 200, 0) ← 重力(向下拉)
├── Scale: 0.5~1.5 ← 大小范围
└── Color Ramp: 黄→橙→红→透明 ← 颜色渐变
粒子生命周期:
发射 → 飞行 → 减速 → 消失
● ● ● ·
(0s) (0.3s) (0.7s) (1.0s)
常见粒子效果参数:
爆炸:explosiveness=1.0, one_shot=true, spread=180°
烟雾:gravity=-50(上浮), scale递增, alpha递减
火花:velocity=300+, gravity=500, lifetime=0.3s
拖尾:amount=20, spread=5°, 跟随物体
16.1 GPUParticles2D
extends GPUParticles2D
# 基本属性
amount = 50 # 粒子数量
lifetime = 1.0 # 粒子寿命
one_shot = false # 是否一次性
explosiveness = 0.0 # 爆发性(0=均匀发射,1=同时发射)
emitting = true # 是否发射
# Process Material 设置(在检查器中)
# Direction: 发射方向
# Spread: 扩散角度
# Gravity: 重力
# Initial Velocity: 初始速度
# Scale: 大小变化曲线
# Color: 颜色变化曲线
# 代码创建粒子材质
func setup_explosion_particles() -> void:
var material := ParticleProcessMaterial.new()
material.direction = Vector3(0, -1, 0)
material.spread = 180.0
material.initial_velocity_min = 100.0
material.initial_velocity_max = 200.0
material.gravity = Vector3(0, 200, 0)
material.scale_min = 0.5
material.scale_max = 1.5
# 颜色渐变
var gradient := Gradient.new()
gradient.set_color(0, Color.YELLOW)
gradient.set_color(0.5, Color.ORANGE)
gradient.set_color(1, Color(1, 0, 0, 0))
material.color_ramp = gradient
process_material = material
# 爆炸效果
func explode() -> void:
one_shot = true
explosiveness = 1.0
emitting = true
await finished
queue_free()
16.2 Line2D 拖尾效果
extends Line2D
@export var max_points: int = 20
@export var target: Node2D
# 预创建 Gradient(避免每帧创建新对象)
var trail_gradient: Gradient
func _ready() -> void:
trail_gradient = Gradient.new()
width_curve = null # 使用默认宽度
func _process(delta: float) -> void:
if target:
add_point(target.global_position)
# 限制点数
while get_point_count() > max_points:
remove_point(0)
# 更新渐变透明度(只在点数变化时更新)
var point_count := get_point_count()
if point_count > 1:
# 重新设置渐变点数
trail_gradient.set_point_count(point_count)
for i in point_count:
var alpha := float(i) / (point_count - 1)
trail_gradient.set_color(i, Color(1, 1, 1, alpha))
trail_gradient.set_offset(i, float(i) / (point_count - 1))
gradient = trail_gradient
16.3 MultiMeshInstance2D(大量精灵)
# 当需要渲染数百/数千个相同精灵时使用
# 例如:弹幕、草丛、装饰物
extends MultiMeshInstance2D
@export var count: int = 1000
@export var area_size: Vector2 = Vector2(1000, 1000)
func _ready() -> void:
var mm := MultiMesh.new()
mm.instance_count = count
mm.transform_format = MultiMesh.TRANSFORM_2D
# 设置纹理
texture = preload("res://assets/sprites/grass.png")
# 随机放置
for i in count:
var s := randf_range(0.8, 1.2)
var xform := Transform2D(
0, # 旋转角度
Vector2(s, s), # 缩放
0, # 倾斜
Vector2( # 位置
randf() * area_size.x,
randf() * area_size.y
)
)
mm.set_instance_transform_2d(i, xform)
multimesh = mm
16.4 Tween 动画
# 基本 Tween
func move_to(target_pos: Vector2, duration: float) -> void:
var tween := create_tween()
tween.tween_property(self, "position", target_pos, duration)
# 并行动画
func complex_animation() -> void:
var tween := create_tween().set_parallel(true)
tween.tween_property(self, "position", Vector2(200, 200), 0.5)
tween.tween_property(self, "rotation", TAU, 0.5)
tween.tween_property(self, "modulate:a", 0.0, 0.5)
# 链式动画(顺序执行)
func chain_animation() -> void:
var tween := create_tween()
tween.tween_property(self, "position:x", 200, 0.3)
tween.tween_property(self, "position:y", 200, 0.3)
tween.tween_property(self, "modulate:a", 0.0, 0.2)
tween.tween_callback(queue_free)
# 循环动画
func pulse_animation() -> void:
var tween := create_tween().set_loops()
tween.tween_property(self, "scale", Vector2(1.2, 1.2), 0.5)
tween.tween_property(self, "scale", Vector2.ONE, 0.5)
# 等待信号(4.7 新增)
func wait_for_animation() -> void:
var tween := create_tween()
tween.tween_property(self, "position:x", 200, 1.0)
tween.tween_await(some_signal) # 等待信号后再继续
tween.tween_property(self, "position:x", 0, 1.0)
# 检查 Tween 是否还有动画(4.7 新增)
func _process(delta: float) -> void:
if tween and tween.is_valid() and tween.has_tweeners():
print("动画还在播放")
16.5 自定义绘制(_draw)
extends Node2D
@export var circle_radius: float = 50.0
@export var line_color: Color = Color.RED
func _draw() -> void:
# 绘制圆形
draw_circle(Vector2.ZERO, circle_radius, line_color)
# 绘制线段
draw_line(Vector2(-100, 0), Vector2(100, 0), Color.GREEN, 2.0)
# 绘制矩形
draw_rect(Rect2(-50, -50, 100, 100), Color.BLUE, false, 2.0)
# 绘制弧线
draw_arc(Vector2.ZERO, 80, 0, PI, 32, Color.YELLOW, 2.0)
# 属性改变时重绘
@export var radius: float = 50.0:
set(value):
radius = value
queue_redraw() # 触发重绘