3.9 KiB
title, description, date, type, series, series_order, languages, tags
| title | description | date | type | series | series_order | languages | tags | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Week 2: Projectiles, footprints, and a UI detour | Second week on the Odin tower defense prototype: homing projectiles, multi-tile towers, render split, and learning to stick with Raylib for UI. | 2026-06-19 | devlog | tower-defense-prototype | 3 |
|
|
Week two on the Odin prototype. Last week ended with instant-hit archers and a keyboard-driven HUD. This week the game started to look like a tower defense: arrows fly, towers occupy real space on the grid, and I got my first proper UI, after a brief and expensive detour through third-party UI libraries.
What I built
Projectiles. Archers no longer subtract health on the frame they fire. Towers spawn homing projectiles from a second object pool ([MAX_PROJECTILES]Projectile), track the target enemy by slot index, and deal damage on impact. Same pattern as the enemy pool from week one: acquire slot, reset fields, mark inactive when done.
Render split. render_world became a thin orchestrator: render_map, render_enemies, render_towers, render_projectiles. Each system owns its draw calls. Small refactor, big readability win as files grew.
Multi-tile towers. Tower archetypes gained footprint_w and footprint_h. The archer is 1×2 tiles, taller than a single cell. Placement now checks whether the full rectangle fits on build tiles and does not overlap other towers. Rendering draws a gold rectangle sized to the footprint instead of a fixed 28×28 square.
Memory cleanup. Added explicit delete calls for dynamic arrays (path, towers, events) when the game loop exits. Odin will not save you from leaking if you allocated with append.
Overlay bar. Moved gold and base health out of scattered DrawText calls into overlay.odin, a top bar with consistent padding and colors. Wave count and combat hints stayed in hud.odin for now.
Control panel. Bottom bar with clickable buttons: select Archer, start wave. poll_controls_command feeds into the existing command layer so UI clicks and keyboard shortcuts share one path. Added utils.odin with a reusable draw_button helper for centered labels.
What worked
- Projectile pool mirrored the enemy pool: copy the pattern, ship faster
- Footprint-based placement forced me to think in grid coordinates early; multi-tower-type layouts will need this anyway
- Command layer absorbed UI input cleanly: buttons return
Commandvalues just like keyboard handlers - Rip-and-replace on Clay was painful but left me with a simpler codebase than I started with
What broke
- UI library detour. Tried ImGui bindings, then Clay for two days. Gold moved to Clay, start-wave became a Clay button, then I deleted all of it and rewrote the overlay in pure Raylib. Lesson: for a small game HUD, immediate-mode Raylib is enough. Do not import a layout engine until you have a layout problem.
- Tower selection half-wired. The Archer button sets
world.selected_tower, butexecute_commandstill hardcodes.Archeron placement. UI looks done; logic is not. - Split HUD. Gold and health live in
overlay.odin, wave/enemy count inhud.odin, controls incontrols.odin. Three files for one screen; next cleanup pass needed. - Range bug (later fix). A typo in the distance function made archer range longer than intended. Caught at the end of the week; fix landed June 26.
Repo snapshot
June 13–19 added about 470 net lines across 18 files. New modules: projectile.odin, overlay.odin, controls.odin, utils.odin. Still one tower type, but the archer now shoots, occupies space, and has a shop button.
Next: end screens, fullscreen, more tower types, and enemy variety.
