移动/游戏
Godot 项目出现掉帧、卡顿、内存或加载问题时…
在 Godot 游戏需要分析 FPS、帧时间、物理、渲染或内存瓶颈并实施性能优化时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
Godot 项目出现掉帧、卡顿、内存或加特定
解决思路
- 使用 Godot 内置 Profiler:在游戏运行时打开 Debug → Profiler,查看 Frame、Monitors、Network、Visual 标签。
- 监视关键指标:FPS 目标 60(移动端 30)、Frame Time 应 <16.67ms、Physics Frame Time、Idle Time。
- 在代码中启用性能监控:在 _ready() 调用 Performance.get_monitor(Performance.TIME_FPS)、Performance.TIME_PROCESS、Performance.TIME_PHYSICS_PROCESS、Performance.MEMORY_STATIC、Performance.MEMORY_DYNAMIC、Performance.OBJECT_COUNT、Performance.OBJECT_NODE_COUNT、Performance.RENDER_OBJECTS_IN_FRAME、Performance.RENDER_VERTICES_IN_FRAME。
- 显示 FPS 计数器:在 _process(_delta) 中用 Performance.get_monitor(Performance.TIME_FPS) 获取 fps,并更新 $FPSLabel.text,例如 'FPS: %d' % fps。
- 修复过多 _process() 调用:将昂贵检查改为计时器或降低频率,例如维护 check_timer 和 const CHECK_INTERVAL = 0.5,每 0.5 秒执行一次;不需要时在 _ready() 调用 set_process(false),仅在激活时启用。
- 修复低效节点查找:用 @onready var player: Node2D = get_node('/root/Main/Player') 缓存节点引用,不要在 _process 中每帧 get_node。
- 修复过多 get_tree() 调用:在 _ready() 缓存 get_tree().get_nodes_in_group('enemies') 到数组,并通过信号在敌人增删时更新;不要重复执行树搜索。
- 修复低效碰撞检查:不要每帧遍历所有对象并计算 global_position.distance_to;改用 Area2D/Area3D 自动检测,并连接 body_entered 信号到 _on_body_detected。
- 减少 Draw Calls:用 TileMap 替代大量独立 Sprite2D 节点,用 MultiMeshInstance 渲染重复对象,用 texture atlases 批处理 sprites,限制灯光和粒子数量。
- 使用 MultiMesh 示例做法:创建 MultiMesh,设置 multimesh.mesh = preload('res://meshes/coin.tres'),设置 multimesh.instance_count = 100,循环用 multimesh.set_instance_transform_2d(i, transform) 设置每个实例的 Transform2D,再赋给 $MultiMeshInstance2D.multimesh。
- 优化脚本对象创建:不要在 _process 中每帧创建 Vector2.ZERO 等新对象;在外部声明 direction: Vector2 = Vector2.ZERO 并复用。
- 使用对象池:创建 ObjectPool 类,包含 pool 数组、prefab: PackedScene、pool_size = 20;_fill_pool() 实例化对象并 set_process(false)、visible = false;get_object() 从池 pop_back 并启用;return_object(obj) 禁用处理、隐藏并 append。
- 使用 LOD:用 @export var lod_distances: Array[float] = [50.0, 100.0, 200.0],在 _process 中计算与相机 global_position 的距离,按阈值调用 _set_lod(0/1/2/3),切换 $HighDetailMesh/$MedDetailMesh 的 visible 以及 set_physics_process/set_process。
- 使用空间分区:创建 ChunkManager,设置 chunk_size = 100.0,用 get_chunk_key(pos) 返回 Vector2i(int(pos.x / chunk_size), int(pos.y / chunk_size));update_active_chunks(player_position) 激活玩家周围 -1..1 的 chunks,卸载距离 >2 的 chunks;_load_chunk 激活对象,_unload_chunk 停用或移除对象。
- 配置高效碰撞层:在 Project Settings → Layer Names → 2D Physics 定义层,如 Layer 1 Players、Layer 2 Enemies、Layer 3 Environment、Layer 4 Projectiles;在 _ready() 设置 collision_layer = 1、collision_mask = 6(二进制 110,表示 layers 2 和 3)。
- 物理回调中使用延迟调用:不要在 _on_body_entered 中直接 body.queue_free() 或禁用 CollisionShape2D;改用 body.call_deferred('queue_free') 和 $CollisionShape2D.call_deferred('set_disabled', true)。
- 纹理压缩:在 Project Settings 的 Import tab 中压缩纹理;桌面用 VRAM compression,移动端用 ETC2/ASTC,尽量减小纹理尺寸。
- 音频优化:长音频(音乐、语音)用 streaming,短音频(SFX)用 samples;导入设置中 SFX 的 Loop Mode 用 Disabled,音乐用 Forward;SFX 的 Compress Mode 用 RAM,音乐用 Streaming。
- 使用场景实例化:用 const ENEMY_SCENE = preload('res://enemies/enemy.tscn'),然后 ENEMY_SCENE.instantiate() 并 add_child(),以共享资源;避免 $EnemyTemplate.duplicate()。
- 资源管理:移除关卡时对每个 child 调用 child.queue_free() 正确释放内存;必要时调用 ResourceLoader.clear_cache() 清缓存。
- 2D 渲染优化:用 CanvasLayer 做 UI 防止游戏世界重绘;限制粒子数量;少用 Light2D;批处理相同纹理的 sprites。
- 高效粒子系统:设置 $GPUParticles2D.amount = 50(不是 500),lifetime = 1.0,one_shot = true,避免不必要循环。
- 3D 渲染优化:使用 occlusion culling;尽可能烘焙光照;远处对象用 LOD;限制投射阴影的灯光。
- 高效 3D 设置:设置 $WorldEnvironment.environment.background_mode = Environment.BG_SKY;设置 $Camera3D.far = 500.0 限制渲染距离;在 Godot 4 中启用 SDFGI:$WorldEnvironment.environment.sdfgi_enabled = true。
- 性能剖析工作流第 1 步 Identify Bottleneck:运行游戏并打开 profiler,识别最慢区域是 Process、Physics、Rendering 还是 Script。
- 性能剖析工作流第 2 步 Locate Specific Issue:在可疑代码前后用 Time.get_ticks_usec() 取时间,print 耗时毫秒,例如 (end_time - start_time) / 1000.0 ms。
- 性能剖析工作流第 3 步 Apply Optimizations:缓存查找、降低频率、使用更高效算法、移除不必要工作。
- 性能剖析工作流第 4 步 Measure Results:重新运行 profiler,验证改进,并确保无回归。
- 移动端优化:在 _ready() 中检测 OS.get_name() in ['Android', 'iOS'] 后调用 _apply_mobile_optimizations();将 $Particles.amount 减半;简化 shaders;将 get_viewport().size 乘以 0.75 降低分辨率;禁用 $WorldEnvironment.environment.ssao_enabled = false 和 glow_enabled = false。
- Web (HTML5) 优化:减少初始加载;对资源使用 streaming;限制内存使用;避免重型物理计算。
- 性能测试检查清单:确认帧率保持目标(60 FPS 或 30 FPS);密集场景无掉帧;内存稳定无泄漏;加载时间 <3 seconds;物理稳定无抖动或穿透;移动端电池消耗合理;Web 初始加载快且无冻结。
- 优化工作流总步骤:Profile → Locate → Optimize → Test → Document,并记录改动和原因。
- 解释与沟通:始终解释为什么慢、用什么优化技术、如何实现、预期提升。
适用边界 · 注意事项
- 不适用于非 Godot 项目,或无法使用 Godot Profiler/性能监控 API 的场景。
- 未先用 Profiler 确认瓶颈时,不要盲目套用全部优化,以免过早优化、增加复杂度或引入回归。
- 平台或引擎版本不确定时慎用平台专属设置;例如 SDFGI 等 Godot 4 特性在旧版本或低端设备上可能不可用。