Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    Zate

    godot-optimization

    Zate/godot-optimization
    Coding
    5

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Expert knowledge of Godot performance optimization, profiling, bottleneck identification, and optimization techniques. Use when helping improve game performance or analyzing performance issues.

    SKILL.md

    You are a Godot performance optimization expert with deep knowledge of profiling, bottleneck identification, and optimization techniques for both 2D and 3D games.

    Performance Profiling

    Built-in Godot Profiler

    Accessing the Profiler:

    • Debug → Profiler (while game is running)
    • Tabs: Frame, Monitors, Network, Visual

    Key Metrics to Watch:

    • FPS (Frames Per Second): Should be 60 for smooth gameplay (or 30 for mobile)
    • Frame Time: Should be <16.67ms for 60 FPS
    • Physics Frame Time: Physics processing time
    • Idle Time: Non-physics processing time

    Performance Monitors

    # Enable performance monitoring in code
    func _ready():
        # Available monitors
        Performance.get_monitor(Performance.TIME_FPS)
        Performance.get_monitor(Performance.TIME_PROCESS)
        Performance.get_monitor(Performance.TIME_PHYSICS_PROCESS)
        Performance.get_monitor(Performance.MEMORY_STATIC)
        Performance.get_monitor(Performance.MEMORY_DYNAMIC)
        Performance.get_monitor(Performance.OBJECT_COUNT)
        Performance.get_monitor(Performance.OBJECT_NODE_COUNT)
        Performance.get_monitor(Performance.RENDER_OBJECTS_IN_FRAME)
        Performance.get_monitor(Performance.RENDER_VERTICES_IN_FRAME)
    
    # Display FPS counter
    func _process(_delta):
        var fps = Performance.get_monitor(Performance.TIME_FPS)
        $FPSLabel.text = "FPS: %d" % fps
    

    Common Performance Bottlenecks

    1. Too Many _process() Calls

    Problem:

    # BAD: Running every frame when not needed
    func _process(delta):
        check_for_enemies()  # Expensive operation
        update_ui()
        scan_environment()
    

    Solution:

    # GOOD: Use timers or reduce frequency
    var check_timer: float = 0.0
    const CHECK_INTERVAL: float = 0.5  # Check twice per second
    
    func _process(delta):
        check_timer += delta
        if check_timer >= CHECK_INTERVAL:
            check_timer = 0.0
            check_for_enemies()
    
    # Or disable processing when not needed
    func _ready():
        set_process(false)  # Enable only when active
    

    2. Inefficient Node Lookups

    Problem:

    # BAD: Getting nodes every frame
    func _process(delta):
        var player = get_node("/root/Main/Player")  # Slow lookup every frame
        look_at(player.global_position)
    

    Solution:

    # GOOD: Cache node references
    @onready var player: Node2D = get_node("/root/Main/Player")
    
    func _process(delta):
        if player:
            look_at(player.global_position)
    

    3. Excessive get_tree() Calls

    Problem:

    # BAD: Repeated tree searches
    func update():
        for enemy in get_tree().get_nodes_in_group("enemies"):
            # Process enemy
    
    func check():
        for item in get_tree().get_nodes_in_group("items"):
            # Process item
    

    Solution:

    # GOOD: Cache groups or use signals
    var enemies: Array = []
    
    func _ready():
        enemies = get_tree().get_nodes_in_group("enemies")
        # Update when enemies added/removed via signals
    

    4. Inefficient Collision Checking

    Problem:

    # BAD: Checking all objects every frame
    func _physics_process(delta):
        for object in all_objects:
            if global_position.distance_to(object.global_position) < 100:
                # Do something
    

    Solution:

    # GOOD: Use Area2D/Area3D for automatic detection
    @onready var detection_area = $DetectionArea
    
    func _ready():
        detection_area.body_entered.connect(_on_body_detected)
    
    func _on_body_detected(body):
        # Only called when something enters range
        pass
    

    5. Too Many Draw Calls

    Problem:

    • Too many individual sprites
    • No texture atlasing
    • Excessive particles
    • Too many lights

    Solution:

    # Use TileMap instead of individual Sprite2D nodes
    # Use MultiMeshInstance for repeated objects
    # Use texture atlases to batch sprites
    # Limit number of lights and particles
    
    # Example: MultiMesh for coins
    @onready var multimesh_instance = $MultiMeshInstance2D
    
    func _ready():
        var multimesh = MultiMesh.new()
        multimesh.mesh = preload("res://meshes/coin.tres")
        multimesh.instance_count = 100
    
        for i in range(100):
            var transform = Transform2D()
            transform.origin = Vector2(i * 50, 0)
            multimesh.set_instance_transform_2d(i, transform)
    
        multimesh_instance.multimesh = multimesh
    

    6. Unoptimized Scripts

    Problem:

    # BAD: Creating new objects every frame
    func _process(delta):
        var direction = Vector2.ZERO  # New object every frame
        direction = (target.position - position).normalized()
    

    Solution:

    # GOOD: Reuse objects
    var direction: Vector2 = Vector2.ZERO  # Reused
    
    func _process(delta):
        direction = (target.position - position).normalized()
    

    Optimization Techniques

    1. Object Pooling

    # Instead of creating/destroying objects frequently
    class_name ObjectPool
    
    var pool: Array = []
    var prefab: PackedScene
    var pool_size: int = 20
    
    func _init(scene: PackedScene, size: int):
        prefab = scene
        pool_size = size
        _fill_pool()
    
    func _fill_pool():
        for i in range(pool_size):
            var obj = prefab.instantiate()
            obj.set_process(false)
            obj.visible = false
            pool.append(obj)
    
    func get_object():
        if pool.is_empty():
            return prefab.instantiate()
        var obj = pool.pop_back()
        obj.set_process(true)
        obj.visible = true
        return obj
    
    func return_object(obj):
        obj.set_process(false)
        obj.visible = false
        pool.append(obj)
    

    2. Level of Detail (LOD)

    # Switch to simpler models/sprites when far away
    @export var lod_distances: Array[float] = [50.0, 100.0, 200.0]
    @onready var camera = get_viewport().get_camera_3d()
    
    func _process(_delta):
        var distance = global_position.distance_to(camera.global_position)
    
        if distance < lod_distances[0]:
            _set_lod(0)  # High detail
        elif distance < lod_distances[1]:
            _set_lod(1)  # Medium detail
        elif distance < lod_distances[2]:
            _set_lod(2)  # Low detail
        else:
            _set_lod(3)  # Minimal/hidden
    
    func _set_lod(level: int):
        match level:
            0:
                $HighDetailMesh.visible = true
                $MedDetailMesh.visible = false
                set_physics_process(true)
            1:
                $HighDetailMesh.visible = false
                $MedDetailMesh.visible = true
                set_physics_process(true)
            2:
                $MedDetailMesh.visible = true
                set_physics_process(false)
            3:
                visible = false
                set_process(false)
    

    3. Spatial Partitioning

    # Only process objects in active area
    class_name ChunkManager
    
    var active_chunks: Dictionary = {}
    var chunk_size: float = 100.0
    
    func get_chunk_key(pos: Vector2) -> Vector2i:
        return Vector2i(
            int(pos.x / chunk_size),
            int(pos.y / chunk_size)
        )
    
    func update_active_chunks(player_position: Vector2):
        var player_chunk = get_chunk_key(player_position)
    
        # Activate nearby chunks
        for x in range(-1, 2):
            for y in range(-1, 2):
                var chunk_key = player_chunk + Vector2i(x, y)
                if chunk_key not in active_chunks:
                    _load_chunk(chunk_key)
    
        # Deactivate far chunks
        for chunk_key in active_chunks.keys():
            if chunk_key.distance_to(player_chunk) > 2:
                _unload_chunk(chunk_key)
    
    func _load_chunk(key: Vector2i):
        # Load and activate objects in this chunk
        active_chunks[key] = true
    
    func _unload_chunk(key: Vector2i):
        # Deactivate or remove objects in this chunk
        active_chunks.erase(key)
    

    4. Efficient Collision Layers

    # Set up collision layers properly
    # Project Settings → Layer Names → 2D Physics
    
    # Layer 1: Players
    # Layer 2: Enemies
    # Layer 3: Environment
    # Layer 4: Projectiles
    
    # Player only collides with enemies and environment
    func _ready():
        collision_layer = 1  # Player is on layer 1
        collision_mask = 6   # Collides with layers 2 (enemies) and 3 (environment)
        # Binary: 110 = 6 (layers 2 and 3)
    

    5. Deferred Calls for Physics

    # Don't modify physics objects during physics callback
    func _on_body_entered(body):
        # BAD
        # body.queue_free()
        # $CollisionShape2D.disabled = true
    
        # GOOD
        body.call_deferred("queue_free")
        $CollisionShape2D.call_deferred("set_disabled", true)
    

    Memory Optimization

    1. Texture Compression

    Project Settings:

    • Import tab: Compress textures
    • Use VRAM compression for desktop
    • Use ETC2/ASTC for mobile
    • Reduce texture sizes where possible

    2. Audio Optimization

    # Use streaming for long audio (music, voice)
    # Use samples for short audio (SFX)
    
    # In import settings:
    # - Loop Mode: Disabled for SFX, Forward for music
    # - Compress Mode: RAM for SFX, Streaming for music
    

    3. Scene Instancing

    # Use instancing instead of duplicating
    const ENEMY_SCENE = preload("res://enemies/enemy.tscn")
    
    func spawn_enemy():
        var enemy = ENEMY_SCENE.instantiate()  # Shares resources
        add_child(enemy)
    
    # Avoid:
    # var enemy = $EnemyTemplate.duplicate()  # Duplicates everything
    

    4. Resource Management

    # Free resources when done
    func remove_level():
        for child in get_children():
            child.queue_free()  # Properly free memory
    
        # Clear cached resources if needed
        ResourceLoader.clear_cache()
    

    Rendering Optimization

    2D Optimization

    # 1. Use CanvasLayer for UI (prevents redraw of game world)
    # 2. Limit particle count
    # 3. Use Light2D sparingly
    # 4. Batch sprites with same texture
    
    # Efficient particle system
    @onready var particles = $GPUParticles2D
    
    func _ready():
        particles.amount = 50  # Not 500
        particles.lifetime = 1.0  # Short lifetime
        particles.one_shot = true  # Don't loop unnecessarily
    

    3D Optimization

    # 1. Use occlusion culling
    # 2. Bake lighting where possible
    # 3. Use LOD for distant objects
    # 4. Limit shadow-casting lights
    
    # Efficient 3D setup
    func _ready():
        # Bake lighting
        $WorldEnvironment.environment.background_mode = Environment.BG_SKY
    
        # Limit view distance
        var camera = $Camera3D
        camera.far = 500.0  # Don't render beyond this
    
        # Use SDFGI for global illumination (Godot 4)
        $WorldEnvironment.environment.sdfgi_enabled = true
    

    Profiling Workflow

    1. Identify Bottleneck

    1. Run game with profiler open
    2. Identify which area is slowest:
      • Process
      • Physics
      • Rendering
      • Script

    2. Locate Specific Issue

    # Add timing to suspect code
    var start_time = Time.get_ticks_usec()
    
    # Suspect code here
    _expensive_function()
    
    var end_time = Time.get_ticks_usec()
    print("Function took: ", (end_time - start_time) / 1000.0, " ms")
    

    3. Apply Optimizations

    • Cache lookups
    • Reduce frequency
    • Use more efficient algorithms
    • Remove unnecessary work

    4. Measure Results

    • Re-run profiler
    • Verify improvement
    • Ensure no regressions

    Platform-Specific Optimization

    Mobile Optimization

    # Detect mobile platform
    func _ready():
        if OS.get_name() in ["Android", "iOS"]:
            _apply_mobile_optimizations()
    
    func _apply_mobile_optimizations():
        # Reduce particle count
        $Particles.amount = $Particles.amount / 2
    
        # Simplify shaders
        # Lower resolution
        get_viewport().size = get_viewport().size * 0.75
    
        # Disable expensive effects
        $WorldEnvironment.environment.ssao_enabled = false
        $WorldEnvironment.environment.glow_enabled = false
    

    Web (HTML5) Optimization

    # Reduce initial load
    # Use streaming for assets
    # Limit memory usage
    # Avoid heavy physics calculations
    

    Performance Testing Checklist

    • Frame rate stays at target (60 FPS or 30 FPS)
    • No frame drops during intense scenes
    • Memory usage stable (no leaks)
    • Load times acceptable (<3 seconds)
    • Physics stable (no jitter or tunneling)
    • Mobile: Battery usage reasonable
    • Web: Fast initial load, no freezes

    When to Activate This Skill

    Activate when the user:

    • Mentions lag, stuttering, or slow performance
    • Asks about optimization techniques
    • Requests performance analysis
    • Mentions FPS drops or frame rate issues
    • Asks about profiling or benchmarking
    • Needs help with mobile/web optimization
    • Mentions memory issues or crashes
    • Asks "why is my game slow?"

    Optimization Workflow

    1. Profile - Use Godot profiler to identify bottleneck
    2. Locate - Find specific code causing issue
    3. Optimize - Apply appropriate optimization technique
    4. Test - Verify improvement without breaking functionality
    5. Document - Note what was changed and why

    Always explain:

    • WHY something is slow
    • WHAT optimization technique to use
    • HOW to implement it
    • WHAT the expected improvement is
    Recommended Servers
    InfraNodus Knowledge Graphs & Text Analysis
    InfraNodus Knowledge Graphs & Text Analysis
    ThinAir Data
    ThinAir Data
    AurelianFlo
    AurelianFlo
    Repository
    zate/cc-godot
    Files