All work
GodotIn Development2026

Tower Defense

A 3D wave-based tower defense where you place defenders by hand into a village under siege: waves are built from designer-facing parameters rather than hand-authored spawn tables.

The village under wave one. Wave number and remaining enemy count are driven by signals from the wave manager rather than polled each frame.
The village under wave one. Wave number and remaining enemy count are driven by signals from the wave manager rather than polled each frame.
Client
Fiverr (@mallionaire)
Role
Gameplay Programmer
Platforms
PC
Year
2026

A 3D tower defense set in a low-poly village. Enemies enter from rotating spawn points and march on the base; the player answers by dragging defender pawns from an inventory onto the map, inspecting enemy stats mid-fight, and spending the gold that kills return.

The wave system is the core of it. Rather than authoring each wave by hand, waves are generated from a small set of exported parameters: surges per wave, base surge size, a growth multiplier, spawn interval, gap between surges, and how many entrances go live each wave. Changing the pacing of the whole game is a handful of numbers in the inspector, and every stage of the sequence broadcasts a signal so UI and audio can react without polling.

Enemies are Resources rather than hardcoded scenes. Each type carries its own stats, economy values, spawn weighting and description text, which is what lets the inspect panel populate itself from data instead of from a switch statement.

What I built

  • Built the wave manager: waves subdivided into timed surges, enemy counts scaling per wave, rotating spawn entrances, and a signal for every stage (wave and surge start/complete, per-enemy spawn, countdown ticks, and all-waves-cleared)
  • Made enemy types data-driven Resources carrying stats, reward and cost, spawn weighting and flavour text, so adding a unit means authoring a resource rather than editing code
  • Built the pawn placement system: drag from inventory, mesh highlighting on hover, range cone visualisation, ground validation and relocation of already-placed pawns
  • Wrote the inspect panel that reads its contents straight off the hovered enemy's resource, keeping UI and balance data in one place
  • Implemented combat: tower guns with target acquisition, projectiles with trails, grenades with arc preview, and camera shake on impact
  • Added the destructible-building layer using Voronoi fracture geometry, with burning and damage states for structures under attack
  • Built three self-contained minigames (a shell game, a pentagon shell variant, and a timing-based quick-click gauge) gated behind a global flag so they suspend the main loop while active
  • Centralised cross-system communication in a game manager singleton carrying the cursor, hover and drag state, inventory open/close, and minigame lifecycle

Gallery

The inspect panel reads health, speed, damage, reward and description straight off the enemy's resource: balance data and UI never drift apart.
The inspect panel reads health, speed, damage, reward and description straight off the enemy's resource: balance data and UI never drift apart.
Wave two, mid-surge. Enemy counts scale per wave from a growth multiplier instead of hand-authored spawn tables.
Wave two, mid-surge. Enemy counts scale per wave from a growth multiplier instead of hand-authored spawn tables.
A structure taking damage. Buildings use Voronoi fracture geometry, with burn and damage states as the health value falls.
A structure taking damage. Buildings use Voronoi fracture geometry, with burn and damage states as the health value falls.
Placement highlighting. Hovering a pawn swaps its material so the target reads clearly against a busy village.
Placement highlighting. Hovering a pawn swaps its material so the target reads clearly against a busy village.

Breakdown

A wave is generated rather than authored by hand. It is defined by a count of surges, a base size, a growth multiplier per wave, an interval between spawns, a gap between surges, and how many entrances go live, all exported to the inspector. Retuning the difficulty curve of the entire game means editing those numbers instead of rewriting spawn tables.

Every stage of the wave sequence emits a signal: wave started with its total, surge started and completed, each individual enemy spawned, the countdown ticking, and the final all-clear. UI, audio and camera work hang off those signals, so nothing polls the wave manager to find out what is happening.

Enemy types are Resources, not scenes with scripts attached. Stats, gold reward, spawn cost, spawn weighting and the description string all live on the resource, which is why the inspect panel can populate itself from whatever it happens to be hovering.

The minigames suspend the main loop through a single global flag rather than each system checking its own conditions. One place decides whether the tower defense is live, and everything else reads it.

Built with

  • Godot 4.7
  • GDScript
  • Forward+ renderer
  • Resource-driven data
  • Voronoi fracture