Learn Godot - Performance & Profiling
Series/Learn Godot/Episode 14
Episode 14 of 23

Learn Godot - Performance & Profiling

Keeping the game smooth: dissecting Godot's built-in profiler and performance monitor, optimizing draw calls and batching, tuning physics that's too expensive, managing memory and FPS budget, and optimization patterns at export time including export templates and platform-specific settings.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 13, you made a multiplayer game — and like any online game, it's prone to lag and frame drops. Episode 14 teaches how to keep the game smooth: performance & profiling. You'll learn to measure before optimizing, because optimization without data is just guesswork.

This episode's roadmap: get to know the built-in profiler and performance monitor, understand draw calls and batching in rendering, optimize physics that's often the culprit, manage memory and FPS budget, then finish with optimization at export time — including export templates and platform-specific settings.

Measure First: The Profiler and Performance Monitor

The golden rule of performance: don't optimize before measuring. Godot provides two main tools. The first is the Profiler (Debug menu → Profiler): it records the time every system spends — script, rendering, physics, audio — along with the number of calls per frame. After the game runs for a few seconds, you can read which frames are the heaviest and which system causes it.

The second is the Performance Monitor (Debug → Monitor): real-time graphs of FPS, draw calls, active node count, memory, and more. From here you can see trends: do draw calls spike when many enemies appear? Does memory keep climbing without ever going down? Those two symptoms already give big clues.

The profiler can also be accessed from code for automated telemetry:

PythonReading FPS and physics time from code
func _process(_delta: float) -> void:
    var fps: float = Engine.get_frames_per_second()
    var physics_time: float = Performance.get_monitor(Performance.TIME_PHYSICS_PROCESS)
    if fps < 30.0:
        print("FPS menurun: ", fps, " physics: ", physics_time)
The Performance singleton gives real-time numbers

Build a simple habit: before changing anything, record the FPS and physics time. After the change, compare. If there's no difference, revert — that means your optimization only added complexity without results.

Draw Calls and Batching

One main cause of a heavy-feeling game is too many draw calls — commands sent by the CPU to the GPU to draw one object. Every sprite, every particle effect, every UI element adds one or more draw calls. The GPU can handle millions of vertices, but the CPU is limited in sending commands, so this is usually where the bottleneck happens.

The key to reducing draw calls is batching: combining many small objects into one draw call. In 2D, automatic batching works when sprites use the same texture and the same material. That's why the most effective pattern is merging many small objects into one texture atlas (one large image containing all sprites), so Godot can draw them all at once.

For objects that move on their own like bullets or particles, limit their numbers: 20 clearly visible bullets beat 200 flooding the screen. Use a pool (a collection of reused objects) instead of constantly creating and destroying nodes:

PythonA simple pattern for reusing bullet nodes
func tembak(posisi: Vector2) -> void:
    for peluru in pool:
        if not peluru.visible:
            peluru.global_position = posisi
            peluru.visible = true
            return
changing position and status is cheaper than instantiate

Physics Optimization

Physics is the second system that most often causes strain. Three high-impact tunings:

  • Limit the number of bodies and shapes. Godot compares collisions in pairs; every added object increases the cost quadratically. Don't give a CollisionShape2D to objects that don't need collisions, like particle dust.
  • Use layers and masks wisely. Divide collision layers (player, enemies, environment, pickups) and set masks so objects are only checked against relevant objects.
  • Adjust the physics tick. Godot's default is 60 ticks per second. For games that don't need high precision, lower physics/ticks_per_second in Project Settings — half the ticks means half the CPU work for physics.

Also watch out for body_entered and body_exited signals, which can flood when many objects touch. Disable monitoring on Area2D nodes that don't need active detection, and consider using set_deferred to change physics properties so they don't happen mid-tick.

Memory and FPS Budget

Two metrics you must monitor: memory and FPS. Memory that keeps climbing indicates a leak — usually nodes or textures never freed. Always remember Godot's basic patterns: nodes added must be removed, textures loaded must be released. Useful tools are get_node_count() and the ResourceMonitor (Debug → Monitors → Memory).

Unstable FPS often isn't a GPU problem but spikes: one very heavy frame among light ones. Common causes are load() loading a big scene or texture mid-game, or a poorly handled change_scene. The solution: load scenes and assets asynchronously, and show a loading screen while waiting:

PythonLoading a scene asynchronously
func muat_level(level: String) -> void:
    ResourceLoader.load_threaded_request(level)
    var state := ResourceLoader.load_threaded_get_status(level)
    if state == ResourceLoader.THREAD_LOAD_LOADED:
        var scene: PackedScene = ResourceLoader.load_threaded_get(level)
        get_tree().change_scene_to_packed(scene)
ResourceLoader.load_threaded prevents freezes mid-frame

Also set a realistic target FPS. Simple 2D games often run at 120 FPS without issue, but if the target device is weak, set Engine.max_fps or application/run/max_fps to maintain consistency and battery life.

Warning

Don't optimize physics or rendering before looking at the profiler. Godot is often already optimal for your game's size, and premature optimization adds bugs. The correct order: measure, find the biggest bottleneck, fix one at a time, then measure again.

Optimization at Export Time

Optimization doesn't stop in the editor — export settings also determine performance. In the Export dialog, some high-impact choices:

  • Texture compression: enable platform-appropriate texture compression. VRAM compression (ETC2/ASTC for mobile, BC for desktop) loads much faster and is more memory-efficient for the GPU.
  • Occlusion culling and visibility ranges for 3D: objects that aren't visible don't need to be drawn.
  • Limit physics and audio to what the game needs, then run --headless or --quit-after in CI to make sure the game doesn't error without a display.

The habit with the most visible impact on exports: check what gets packaged. Unused scenes or assets increase file size and load time. Use the Resource Manager to clean up unreferenced files before exporting.

Export Templates and Platform Settings

When exporting, Godot needs export templates — runtime versions of Godot for the target platform. Install them via the Editor → Manage Export Templates menu, then pick the template in the Export dialog. There are two kinds: debug (for development, bigger and slower) and release (for distribution, smaller and faster). Always export with the release template for end users.

Every platform has specific settings: on Android you set min_sdk and targets; on iOS bitcode and signing; on Windows icons and a clean application; on Linux the AppImage or tar format. The most important thing: test the build early, not at the end — platform problems (icons, permissions, paths) are far cheaper to fix sooner.

Info

Save export presets as .preset files inside the project so they can be version-controlled. That way the whole team exports with the same settings, and your CI can export from a consistent preset.

Conclusion

Performance is a discipline of measuring, not guessing: the built-in profiler and performance monitor show where time really goes, batching and texture atlases reduce draw calls, physics is tuned by limiting bodies and adjusting the tick, memory and FPS are watched with async loading and a target FPS, and at export time you configure texture compression, culling, and use the release export template.

The key takeaways:

  • Measure with the profiler before optimizing anything; without data, optimization is just guessing.
  • Draw calls are the CPU bottleneck; texture atlases and object limits are the solution.
  • Physics is tuned through body count, layers/masks, and ticks_per_second.
  • Load scenes and assets asynchronously to prevent FPS spikes.
  • Use the release export template and platform-appropriate texture compression.

In episode 15, after the game is fast and smooth, it's your workflow's turn to be optimized: custom tools & editor plugins — creating EditorPlugins, @tool scripts, custom inspectors, and asset pipeline automation. Imagine if the editor could work for you, not the other way around.

Learn Godot - Performance & Profiling | Learn Godot