Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
thedivergentai avatar

Godot Genre Tower Defense

  • 146 installs
  • 454 repo stars
  • Updated July 28, 2026
  • thedivergentai/gd-agentic-skills

Use godot-genre-tower-defense for development tasks

About

godot-genre-tower-defense: A skill for development. This provides functionality for development workflows.

  • godot-genre-tower-defense

Godot Genre Tower Defense by the numbers

  • 146 all-time installs (skills.sh)
  • +8 installs in the week ending Jul 27, 2026 (Skillselion tracking)
  • Ranked #2,526 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-genre-tower-defense

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs146
repo stars454
Last updatedJuly 28, 2026
Repositorythedivergentai/gd-agentic-skills

What it does

Use godot-genre-tower-defense for development tasks

Files

SKILL.mdMarkdownGitHub ↗

Genre: Tower Defense

Strategic placement, resource management, and escalating difficulty define tower defense.

Core Loop

1. Prepare: Build/upgrade towers with available currency 2. Wave: Enemies spawn and traverse path toward goal 3. Defend: Towers auto-target and damage enemies 4. Reward: Kills grant currency 5. Escalate: Waves increase in difficulty/complexity

NEVER Do (Expert Anti-Patterns)

Design & Strategy

  • NEVER make all towers have the same niche; strictly ensure distinct specialties: Aura Slow, Armor Piercing, Anti-Air, Burst Sniper, and Splash Damage.
  • NEVER allow a "Death Spiral" with no exit; strictly provide small comeback bonuses or interest on saved gold to prevent early inevitable failure.
  • NEVER make early waves feel like busywork; strictly provide an "Early Call" bonus to skip wait times and accelerate engagement.
  • NEVER trust client-side economy updates; strictly require the authoritative server to validate currency addition and tower purchases in co-op.

Pathing & Placement

  • NEVER allow the player to "Seal" the exit in mazing games; strictly validate path existence with `NavigationServer2D.map_get_path()` before finalizing tower placement.
  • NEVER use synchronous bake_navigation_polygon() for mazing; strictly offload to a worker thread to prevent 100ms+ frame hitches during placement.
  • NEVER use global coordinates for grid logic; strictly convert to Vector2i/Vector3i to ensure pixel-perfect tower alignment.

Performance & Systems

  • NEVER call get_overlapping_bodies() every frame; strictly use signals (body_entered/body_exited) to maintain a local target cache.
  • NEVER use _process() for projectile movement if count > 500; strictly use the PhysicsServer2D/3D directly for high-performance bullet-hell tiers.
  • NEVER spawn hundreds of projectiles as full Nodes; strictly use Object Pooling to reuse resources and avoid garbage collection stutters.
  • NEVER use standard Strings for priorities; strictly use StringName (&"first", &"strongest") for O(1) hash comparisons in targeting loops.
  • NEVER ignore the progress property on PathFollow nodes; strictly use it as the O(1) way to identify the target closest to exit.
  • NEVER process tower search logic every frame; strictly throttle ACQUIRE searches (e.g., every 5-10 frames) to save significant CPU cycles.
  • NEVER scale Tower CollisionShape non-uniformly; strictly adjust the radius property of the Shape resource to preserve collision math.
  • NEVER delete enemies immediately on death; strictly use set_deferred("disabled", true) and wait one frame to prevent physics server crashes.
  • NEVER hardcode waves in huge switch statements; strictly use Custom Resources (.tres) for clean balancing and sequence editing.

---

🛠 Expert Components (scripts/)

Original Expert Patterns

  • wave_manager.gd - Professional wave orchestrator with Resource-based enemy composition and cleanup.
  • tower.gd - Base turret class with FSM state management and firing logic.
  • tower_targeting_system.gd - Autonomous priority logic (First/Last/Strongest/Weakest) for efficient targeting.

Modular Components

  • tower_defense_patterns.gd - Collection of patterns for furthest-target logic and PhysicsServer projectile optimization.

---

PhaseSkillsPurpose
1. Grid/Pathgodot-tilemap-mastery, navigation-2dDefining where enemies walk and towers build
2. Towersmath-geometry, area-2dRange checks, rotation, projectile prediction
3. Enemiespath-following, steering-behaviorsMovement along paths
4. Managementstate-machines, loop-managementWave spawning logic, game phases
5. UIui-system, drag-and-dropBuilding towers, inspecting stats

Architecture Overview

1. Wave Manager

Handles the timing and godot-composition of enemy waves.

# wave_manager.gd
extends Node

signal wave_started(wave_index: int)
signal wave_cleared
signal enemy_spawned(enemy: Node2D)

@export var waves: Array[Resource] # Array of WaveDefinition resources
var current_wave_index: int = 0
var active_enemies: int = 0

func start_next_wave() -> void:
    if current_wave_index >= waves.size():
        print("All waves cleared!")
        return
        
    var wave_data = waves[current_wave_index]
    wave_started.emit(current_wave_index)
    _spawn_wave(wave_data)
    current_wave_index += 1

func _spawn_wave(wave: WaveResource) -> void:
    for group in wave.groups:
        await get_tree().create_timer(group.delay).timeout
        for i in group.count:
            var enemy = group.enemy_scene.instantiate()
            add_child(enemy)
            active_enemies += 1
            enemy.tree_exiting.connect(_on_enemy_died)
            await get_tree().create_timer(group.interval).timeout

func _on_enemy_died() -> void:
    active_enemies -= 1
    if active_enemies <= 0:
        wave_cleared.emit()

2. Tower Logic (State Machine)

Towers act as autonomous agents.

  • States: Idle, AcquireTarget, Attack, Cooldown.
  • Targeting Priority: First, Last, Strongest, Weakest, Closest.
# tower.gd
extends Node2D

var targets_in_range: Array[Node2D] = []
var current_target: Node2D

func _physics_process(delta: float) -> void:
    if current_target == null or not is_instance_valid(current_target):
        _acquire_target()
    
    if current_target:
        _rotate_turret(current_target.global_position)
        if can_fire():
            fire_projectile()

func _acquire_target() -> void:
    # Example: Target closest to end of path
    var max_progress = -1.0
    for enemy in targets_in_range:
        if enemy.progress > max_progress:
            current_target = enemy
            max_progress = enemy.progress

3. Pathfinding Variants

A. Fixed Path (Kingdom Rush style)

Enemies follow a pre-defined Path2D.

  • Implementation: PathFollow2D as parent of Enemy.
  • Pros: Deterministic, easy to balance, optimized.
  • Cons: Less player agency in shaping the path.
B. Mazing (Fieldrunners style)

Players build towers to block/reroute enemies.

  • Implementation: NavigationAgent2D on enemies. Towers update NavigationRegion2D (bake on separate thread).
  • Pros: High strategic depth.
  • Cons: Computationally expensive recalculation, needs anti-blocking logic (don't let player seal the exit).

Key Mechanics Implementation

Targeting Math (Projectile Prediction)

To hit a moving target, you must predict where it will be.

func get_predicted_position(target: Node2D, projectile_speed: float) -> Vector2:
    var to_target = target.global_position - global_position
    var time_to_hit = to_target.length() / projectile_speed
    return target.global_position + (target.velocity * time_to_hit)

Economy

Money management is the secondary core loop.

  • Kill Rewards: Direct feedback for success.
  • Interest/Income: Rewarding saved money (risk/reward).
  • Early Calling: Bonus money for starting the next wave early.

Common Pitfalls

1. Death Spirals: If a player leaks one enemy, they lose money/lives, making the next wave harder, leading to inevitable failure. Fix: Catch-up mechanics or discrete wave difficulty. 2. Useless Towers: Every tower type must have a distinct niche (AoE, Slow, Armor Pierce, Anti-Air). 3. Path Blocking: In mazing games, ensure players cannot completely block the path to the exit. Use NavigationServer2D.map_get_path to validate placement before building.

Godot-Specific Tips

  • Physics Layers: Put enemies on a specific layer (e.g., Layer 2) and tower "range" Areas on a different mask to avoid towers detecting each other or walls.
  • Area2D Performance: For massive numbers of enemies, avoid monitorable/monitoring on every frame if possible. Use PhysicsServer2D queries for optimization if enemy count > 500.
  • Object Pooling: Essential for projectiles and enemies to avoid garbage collection stutters during intense waves.

---

🚀 Elite Technical Implementations (Batch 09)

1. Navigation-Path-Validation (Maze Sealing Prevention)

In mazing TD games, players must not be able to block the exit. Use AStarGrid2D to simulate building placement and verify that a valid path still exists from spawn to core.

class_name GridPathValidator extends Node

var _astar_grid: AStarGrid2D
@export var spawn_point: Vector2i = Vector2i(0, 0)
@export var core_point: Vector2i = Vector2i(20, 20)

func _ready() -> void:
    _astar_grid = AStarGrid2D.new()
    _astar_grid.region = Rect2i(0, 0, 40, 40)
    _astar_grid.cell_size = Vector2(64, 64)
    _astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER
    _astar_grid.update()

## Simulates placing a tower. Returns true if the path remains valid.
func can_build_tower_at(cell_coords: Vector2i) -> bool:
    if _astar_grid.is_point_solid(cell_coords):
        return false
        
    # 1. Temporarily mark the cell as solid
    _astar_grid.set_point_solid(cell_coords, true)
    
    # 2. Query path from start to finish
    var test_path: Array[Vector2i] = _astar_grid.get_id_path(spawn_point, core_point)
    
    # 3. If empty, maze is sealed. Revert and deny.
    if test_path.is_empty():
        _astar_grid.set_point_solid(cell_coords, false)
        return false
        
    return true

2. Burst-Searching (Frame-Sliced Targeting)

Towers scanning for enemies every frame create CPU spikes. Use Engine.get_process_frames() with a random offset to distribute targeting logic across multiple frames.

class_name BurstSearchTower extends Node2D

@export var search_interval_frames: int = 10
@export var attack_range: float = 250.0

var _frame_offset: int = 0
var _current_target: Node2D = null

func _ready() -> void:
    # Stagger search frame per tower
    _frame_offset = randi() % search_interval_frames

func _physics_process(_delta: float) -> void:
    # Execute expensive logic only once every N frames
    if (Engine.get_process_frames() + _frame_offset) % search_interval_frames == 0:
        _burst_search_for_target()

func _burst_search_for_target() -> void:
    var enemies: Array[Node] = get_tree().get_nodes_in_group("enemies")
    # ... distance squared logic to pick closest target ...

3. Bezier-Path-Follow (Organic Movement)

Smooth, curved enemy movement is achieved using Path2D and PathFollow2D. Increase the progress property to move the enemy along the spline.

class_name OrganicEnemyMovement extends PathFollow2D

@export var move_speed: float = 150.0

func _physics_process(delta: float) -> void:
    # Use 'progress' (Godot 4) to advance along the Curve2D
    progress += move_speed * delta
    
    if progress_ratio >= 1.0:
        # Reached the core
        queue_free()
  • Master Skill: godot-master

Related skills

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.