본문으로 건너뛰기
cccskills
무료GitHub에 공개

godot-optimization

Use when optimizing Godot games — profiler, draw calls, physics tuning, memory management, and common bottlenecks

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(5)

  • SKILL.md12.0 KB
  • references/cpu-bottlenecks.md9.1 KB
  • references/draw-calls.md7.4 KB
  • references/memory-management.md10.5 KB
  • references/physics-tuning.md5.4 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Godot Optimization

This skill covers performance optimization for Godot 4.3+ projects in both GDScript and C#. It covers the built-in profiler, draw call reduction, physics tuning, GDScript performance patterns, memory management, object pooling, and a reference table of common bottlenecks.

Related skills: godot-debugging for systematic debugging and profiling, godot-code-review for performance review checklist, export-pipeline for release build optimization, physics-system for collision shapes, layers, and physics body types, 2d-essentials for 2D mesh optimization, particle performance, and draw order tuning, multithreading for moving work off the main thread, mobile-development for mobile performance budgets.


1. Using the Profiler

Frame Time Budget

At 60 fps, the entire frame (update, physics, rendering) must complete in 16.6 ms. At 30 fps the budget is 33.3 ms. Any single system that consumes the majority of that budget is a bottleneck.

Target FPSFrame budget
1208.3 ms
6016.6 ms
3033.3 ms

Reading Profiler Output

Open Debugger > Profiler, click Start, play through the scenario you want to measure, then click Stop.

  • Frame Time — total wall-clock time for that frame in milliseconds.
  • Self — time spent inside that function excluding callees. This is the primary hotspot indicator. A function with a high Self time is doing expensive work directly.
  • Total — time including all callees. Useful for identifying expensive subtrees.
  • Calls — call count per frame. A function called thousands of times per frame (even if each call is cheap) can dominate the frame.
  • Click any function name to jump to its source in the script editor.
# Manual micro-benchmark for a specific block
var start := Time.get_ticks_usec()
_run_expensive_operation()
var elapsed := Time.get_ticks_usec() - start
print("_run_expensive_operation: %d µs" % elapsed)

C#:

// Manual micro-benchmark using Stopwatch (high-resolution timer)
using System.Diagnostics;

var sw = Stopwatch.StartNew();
RunExpensiveOperation();
sw.Stop();
GD.Print($"RunExpensiveOperation: {sw.Elapsed.TotalMilliseconds:F3} ms");

// Alternative using Godot's built-in timer (microsecond precision)
long start = (long)Time.GetTicksUsec();
RunExpensiveOperation();
long elapsed = (long)Time.GetTicksUsec() - start;
GD.Print($"RunExpensiveOperation: {elapsed} µs");

Monitors Tab

Debugger > Monitors shows real-time engine metrics while the game is running. Click a monitor name to open a live graph. Key monitors to watch:

MonitorWhat to watch for
Time > FPSBelow target — frame budget overrun
Time > ProcessHigh — _process() callbacks are expensive
Time > Physics ProcessHigh — _physics_process() or physics sim is expensive
Raster > Total Draw CallsAbove ~500 (mobile) or ~2 000 (desktop) — needs batching
Video > Video MemSteadily growing — unfreed textures or meshes (memory leak)
Object > ObjectsGrowing across scene reloads — nodes are not being freed
Physics 3D > Active ObjectsLarge count in simple scenes — bodies not sleeping
# Query any monitor at runtime from code
var fps := Performance.get_monitor(Performance.TIME_FPS)
var draw_calls := Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME)
var video_ram := Performance.get_monitor(Performance.RENDER_VIDEO_MEM_USED)
print("FPS: %d | Draw calls: %d | VRAM: %.1f MB" % [fps, draw_calls, video_ram / 1_048_576.0])

C#:

// Query any monitor at runtime from code
double fps = Performance.GetMonitor(Performance.Monitor.TimeFps);
double drawCalls = Performance.GetMonitor(Performance.Monitor.RenderTotalDrawCallsInFrame);
double videoRam = Performance.GetMonitor(Performance.Monitor.RenderVideoMemUsed);
GD.Print($"FPS: {fps:F0} | Draw calls: {drawCalls:F0} | VRAM: {videoRam / 1_048_576.0:F1} MB");

2. Draw Call Optimization

Every distinct mesh, sprite, or canvas item that cannot be batched with its neighbours costs one draw call. Reducing draw calls is one of the highest-leverage optimisations, especially on mobile. 2D batching is automatic (Compatibility; Forward+/Mobile from 4.4) while neighbouring items share texture and material — so atlas sprites, keep unique-material count low, and cull off-screen work. CanvasGroup does not batch: it renders its children to a backbuffer.

See references/draw-calls.md for the full recipes (2D batch-breaking rules, shared materials with per-instance uniforms, texture atlases, VisibleOnScreenNotifier2D/3D culling, and 3D LOD swapping).


3. Physics Optimization

Physics tuning hinges on minimising broadphase work and avoiding mesh colliders on moving bodies. Trim collision masks to only the layers each body actually needs, replace ConcavePolygonShape3D with primitives on anything that moves, and prefer Area2D/3D over per-frame raycasts for overlap detection.

See references/physics-tuning.md for the full recipes (layer/mask bit examples, collision-shape cost table, Engine.physics_ticks_per_second tuning, Area-vs-raycast patterns).


4. GDScript Performance

Hot-path GDScript wins come from eliminating per-frame allocations, comparing StringName instead of String, using typed arrays / PackedArrays, and preload-ing resources at class scope. The same allocation discipline applies to C# (with List<T> in place of typed Array[T] and static readonly StringName fields).

See references/cpu-bottlenecks.md for the full recipes (cached group queries, reused vector locals, &"..." literals, PackedVector2Array, static typing, preload vs load, plus C# parity blocks).


5. Memory Management

Watch Performance.MEMORY_STATIC and OBJECT_COUNT across scene reloads — steady growth means leaked references. Resources loaded by path are cached and shared; call .duplicate() when you need per-instance mutation. Always prefer queue_free() for nodes; free() on a node that is emitting a signal is refused in the editor and debug exports ("Object is locked and can't be freed"), but release exports skip that check and free it mid-emission (undefined behaviour).

See references/memory-management.md for the full recipes (Performance singleton queries, ResourceLoader cache semantics, queue_free vs free table, plus the full GDScript and C# object pool implementations for bullets/effects/particles).


6. Common Bottlenecks

ProblemDiagnosis toolFix
Too many draw callsDebugger > Monitors Raster > Total Draw Calls; Viewport > Debug > Draw Calls overlayKeep 2D neighbours on one texture and material so they batch; MultiMeshInstance3D or merged meshes for 3D; use texture atlases; reduce unique materials
Heavy GDScript in _processProfiler > Self column shows script functions at topMove logic to _physics_process (fixed 60 ticks/s by default — less often than _process at high frame rates), cache queries, avoid per-frame allocations, consider C# for tight loops
Excessive signal connectionsProfiler shows signal dispatch overhead; manually audit get_signal_connection_list()Remove redundant connections; prefer polling over per-frame signals for high-frequency data; use CONNECT_ONE_SHOT for fire-and-forget
Unoptimised TileMapHigh draw call count while the map is on screenSplit into fewer layers; use a single atlas texture per layer; use TileMapLayer (Godot 4.3+) instead of legacy TileMap
Large uncompressed texturesMonitors Video > Video Mem is high; check Import dock for texture settingsEnable texture compression (VRAM Compressed) in the Import dock; use mipmaps; halve resolution of assets not viewed up-close
Too many active physics bodiesMonitors Physics 3D > Active Objects is high; slow _physics_process in ProfilerEnable sleeping on RigidBody3D (can_sleep = true); lower physics tick rate; replace distant bodies with fake animations; use layers/masks to narrow collision checks
String operations in hot pathsProfiler Self time in string-heavy functions; in C#, frequent GC pausesReplace String comparisons with StringName (&"..."); avoid String formatting in _process; build strings once and cache
instantiate() in hot pathsProfiler shows PackedScene.instantiate with high Self time (enable Editor Settings > Debugger > Profile Native Calls)Implement object pooling (see references/memory-management.md); preload scenes at startup; spawn during loading screens rather than during gameplay

7. Top Anti-Patterns

  • Allocating in _process — new Arrays, Dictionaries, Strings, or Vector constructors per frame. Cache the container, mutate fields in place. See references/cpu-bottlenecks.md.
  • Mesh colliders on moving bodies — ConcavePolygonShape3D on a CharacterBody3D or RigidBody3D. Use a capsule, box, or convex hull instead. See references/physics-tuning.md.
  • Unique materials per instance — material_override = SomeMaterial.new() in _ready() breaks batching. Share one material; vary via shader parameters. See references/draw-calls.md.
  • load() in hot paths — calling load("res://...") from _process or _physics_process. Use const X := preload(...) at class scope. See references/cpu-bottlenecks.md.
  • instantiate() + queue_free() for short-lived objects — bullets, hit effects, particles. Pool them. See references/memory-management.md.

8. Checklist

Work through this list before shipping or when investigating a performance complaint.

Profiler

  • Run the Profiler during the most demanding gameplay scenario.
  • Confirm no single function's Self time exceeds 30% of the frame budget.
  • Confirm total frame time stays under budget (16.6 ms at 60 fps).

Draw Calls

  • Draw call count is within target (≤500 mobile, ≤2 000 desktop).
  • 2D sprites drawn next to each other share one atlas texture and material, so they batch.
  • Textures are atlas-packed where possible; duplicate materials are eliminated.
  • Off-screen nodes use VisibleOnScreenNotifier2D/3D to pause processing.
  • 3D meshes have LOD enabled via import settings or manual swap logic.

Physics

  • Collision layers and masks are minimal — no body checks layers it never needs.
  • No moving body uses ConcavePolygonShape — replaced with capsule, box, or convex.
  • Physics tick rate is appropriate for the game type (30 Hz may be fine for turn-based or top-down).
  • Area2D/3D is used for overlap detection instead of per-frame raycasts.
  • RigidBody3D nodes have can_sleep = true where applicable.

GDScript

  • No Array, Dictionary, or String is allocated inside _process or _physics_process.
  • All hot-path string comparisons use StringName (&"...").
  • All arrays in hot paths are typed (Array[T] or PackedArray).
  • All function parameters and return types in hot paths are statically typed.
  • All scene and resource references use preload at class scope, not load per frame.

Memory

  • Performance.get_monitor(Performance.MEMORY_STATIC) is stable between scene reloads.
  • Resources that require per-instance mutation are .duplicate()d.
  • All node removals use queue_free() unless synchronous teardown is explicitly required.

Object Pooling

  • Bullets, hit effects, particles, and other frequently spawned objects use a pool.
  • Pool initial size is large enough to avoid runtime growth during normal gameplay.
  • Pooled objects reset all state on reactivation (position, velocity, signals).

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Use when working with 2D-specific systems — TileMaps, parallax scrolling, 2D lights and shadows, canvas layers, particles 2D, custom drawing, and 2D meshes in Godot 4.3+

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

jame581/GodotPrompter8622026년 10월 22일 업데이트

Use when working with 3D-specific systems — materials, lighting, shadows, environment, global illumination, fog, LOD, occlusion culling, and decals in Godot 4.3+

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

jame581/GodotPrompter8622026년 10월 22일 업데이트

Use when building character abilities — Resource-based abilities with cost/cooldown/cast, buffs/debuffs, stat modifiers, gameplay tags, and HUD binding

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

jame581/GodotPrompter8622026년 10월 22일 업데이트

Use when creating Godot editor plugins — EditorPlugin, @tool scripts, custom inspectors, and dock panels

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

jame581/GodotPrompter8622026년 10월 22일 업데이트

Use when implementing AI movement — NavigationAgent2D/3D, steering behaviors, behavior trees, and patrol patterns

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

jame581/GodotPrompter8622026년 10월 22일 업데이트

Use when implementing animations — AnimationPlayer, AnimationTree, blend trees, state machines, sprite animation, and code-driven animation

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

jame581/GodotPrompter8622026년 10월 22일 업데이트

jame581의 스킬 모두 보기

이 스킬의 문제 신고