Milestone Tracker
7 / 12 milestones complete
Build Entries
Brutus Is Actually Scary Now
Three separate bugs were killing Brutus's ability to do anything. Tracked them all down and squashed them.
- ✕ No navmesh at runtime. The bake only existed in editor memory — not saved to the scene file. Created scripts/Act01.gd which calls bake_navigation_mesh(false) in _ready(). Now the navmesh is always available without a manual editor step.
- ✕ Broken FOV math. rad_to_deg(basis.z.dot(-dir)) — rad_to_deg converts radians to degrees, but a dot product is a cosine value, not an angle in radians. The player directly in front produced a "57°" result, which always exceeded the 30° half-FOV limit. Fixed to acos(clamp(dot, -1, 1)).
- ✕ Wrong attack target. get_node_or_null("PlayerController") searched for a child node named PlayerController, but die() lives on the player node itself. Changed to player.has_method("die").
- ✕ Route memory recorded AFK positions. Brutus stamped the player's position every 2 s regardless of movement, so he always knew exactly where to go even if you never moved. Fixed by gating the record on a flat speed threshold — Brutus now only builds a trail from footsteps, not from standing still.
NavMesh Actually Works
Godot's navmesh baker ignores CSGBox3D geometry entirely. Rewrote Act01.tscn from scratch using proper StaticBody3D → MeshInstance3D + CollisionShape3D nodes nested under NavigationRegion3D. Set geometry/parsed_geometry_type = 2 (Both) and source_geometry_mode = 0 (Root children). Blue overlay confirmed in editor.
Also added enemy gravity — EnemyBase had no velocity.y handling so Brutus clipped through the floor.
Flashlight + Scene Import Fixes
Removed the @export var light: SpotLight3D that required manual wiring in the Inspector (impossible from a parent scene because child nodes are locked). Replaced with auto-discovery: get_parent().get_node_or_null("Camera3D/SpotLight3D"). Flashlight toggle on F now works out of the box.
Fixed a missing SubResource reference that crashed the scene importer on open. Removed a stray WorldEnvironment node that referenced an undefined resource.
Enemy AI Framework
Built EnemyBase.gd — the shared base for all four animatronics. State machine with five states: PATROLLING → INVESTIGATING → CHASING → ATTACKING → IDLE. Detection uses both a raycasted FOV cone and the SoundBus signal. HUD warning vignette scales with proximity.
Implemented BrutusAI.gd: reacts only to FOOTSTEP sounds within a vibration radius. Builds a six-point circular route memory from the player's walking trail and patrols those spots when idle. Fixed class resolution — subclasses must use full resource paths not bare class names.
Player & Core Systems
PlayerController: CharacterBody3D with WASD movement, sprint (Shift), crouch (Ctrl), and mouse look. Capsule collision shrinks on crouch. Camera at Y=1.6 (eye level).
PlayerSoundEmitter: Converts movement state into timed SoundBus events. Walk = 5-unit radius, run = 12-unit radius, crouch = 1-unit radius. Fires every 0.5 s walking, 0.25 s sprinting.
SoundBus autoload: Global signal bus (sound_emitted(position, radius, type)). All enemies subscribe at spawn. GameManager autoload: Tracks marrow_death_memory for the Marrow mechanic, handles player death with a 2 s delay then scene reload.
Concept & Engine Decision
Designed the full game concept: a FNAF-inspired survival horror set in an abandoned Pine Barrens research facility. Four animatronics — each with a unique, asymmetric detection system so players can never develop a single counter-strategy:
BRUTUS
Detects floor vibrations. Remembers footpaths. Move quietly or he traces your trail.
VIXEN
Photosensitive. Activates only in darkness. Keep a light source on or she closes in.
AVIAR
Detects sound above a threshold. Crouch, don't sprint, don't bump objects.
MARROW
Learns from every death. Adapts. The more you die, the harder it becomes.
Started in Unity, moved to Godot 4.6 for accessibility. Full project scaffolded: project.godot with input bindings, autoloads, and scene structure defined.
Up Next
- → Death screen — CanvasLayer overlay with a "FACILITY BREACH" game-over message and restart button
- → HUD — flashlight battery bar + proximity vignette (logic already exists in EnemyBase and FlashlightController)
- → Patrol points — Marker3D waypoints in the scene so Brutus has a walking route when idle
- → Act 2 layout — corridors, locked doors, hiding spots; power rerouting puzzle
- → Vixen AI — light-detection enemy, inverse of Brutus's mechanic