Loading...
Loading...
Expert blueprint for tower defense games (Bloons TD, Kingdom Rush, Fieldrunners) covering wave management, tower targeting logic, path algorithms, economy balance, and mazing mechanics. Use when building TD, lane defense, or tower placement strategy games. Keywords tower defense, wave spawner, pathfinding, targeting priority, mazing, NavigationServer baking.
npx skill4agent add thedivergentai/gd-agentic-skills godot-genre-tower-defenseNavigationRegion2D.bake_navigation_polygon()NavigationServer2D.get_maps()NavigationServer2D.map_get_path(start, goal)Area2D.get_overlapping_bodies()bodies_enteredbody_exited| Phase | Skills | Purpose |
|---|---|---|
| 1. Grid/Path | | Defining where enemies walk and towers build |
| 2. Towers | | Range checks, rotation, projectile prediction |
| 3. Enemies | | Movement along paths |
| 4. Management | | Wave spawning logic, game phases |
| 5. UI | | Building towers, inspecting stats |
# 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()IdleAcquireTargetAttackCooldownFirstLastStrongestWeakestClosest# 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.progressPath2DPathFollow2DNavigationAgent2DNavigationRegion2Dfunc 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)NavigationServer2D.map_get_pathmonitorable/monitoringPhysicsServer2D