CLAUDE.md 7.4 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project

A Unity project (Editor version 6000.3.7f1, Universal Render Pipeline, 2D/top-down via Physics2D) that procedurally generates a maze of connected rooms and populates it with autonomous agents that explore it. All project-specific code lives under Assets/Scripts/Maze/, namespace MazeGen. There is no code outside that folder worth touching except Assets/Scenes/SampleScene.unity, which wires up the single MazeSceneBuilder entry point component.

Commands

This is a pure Unity Editor project — there is no CLI build, lint, or test setup in the repo (no package.json, Makefile, .asmdef, or test assemblies). The only way to run/verify changes is:

  1. Open the project in Unity Editor 6000.3.7f1 (or compatible 6000.3.x).
  2. Open Assets/Scenes/SampleScene.unity.
  3. Press Play. The MazeSceneBuilder component on the scene's root object generates the maze and spawns agents automatically.

Compile errors surface in the Unity Console; there's no separate dotnet build/csc step to invoke. If you need to sanity-check C# syntax without the Editor, treat the generated Assembly-CSharp.csproj as a rough reference for compiled files, but it's Editor-generated and shouldn't be hand-edited.

Architecture

The maze is not a pre-built grid — it's a lazily-grown graph of Room nodes connected by Exit doors, generated on demand as agents actually walk through unresolved exits. Nothing beyond an unused exit exists until something tries to go through it. This is what makes "many agents, huge maze" cheap.

Core data model (Core/)

  • Room — a node with Position (free Vector2, not grid-snapped), Radius, Kind (Start/Normal/Hallway/Exit), and a list of Exits. CorridorWaypoints is set only on elbow-hallway rooms (see below) for stretched rendering.
  • Exit — one doorway. Grid-locked to the 4 cardinal directions (0/90/180/270°, DirectionDegrees), so a room has at most one exit per side and "the door facing room X" always has a single, exact answer. Exits are matched in connected pairs (ConnectedRoomId/ConnectedExitId) once resolved; IsUsed marks that.
  • MazeGraph — the registry of all rooms, id allocation, UnusedExitCount bookkeeping (must never hit 0 before an Exit room exists, or the maze could never be solvable), and spatial lookup via SpatialRoomIndex (a uniform grid bucket index, cell size = roomSpacing, used for overlap/collision queries — not a maze grid).

Generation (Generation/)

  • MazeGenerationSettings — a ScriptableObject with all tunables (room sizes, exit counts, hallway chance, start room count/spread, collision-avoidance search parameters, exit-room eligibility odds).
  • MazeGenerator — the core algorithm, called lazily from MazeAgentController.TryUseExit / AutonomousAgentBrain whenever an agent uses an unresolved exit (GenerateConnectedRoom). On each call it:
    1. Computes where a new room would go (fromRoom.Position + exit.Direction * roomSpacing).
    2. TryConnectViaExistingDoor: if that spot overlaps an existing room, looks for (or adds on-the-fly) a door on that room facing back toward fromRoom, and links into it instead of creating an overlapping room — this is what turns the tree into a maze with loops. The link is either direct (ConnectStraight, doors face each other head-on) or via a single 90°-bend elbow hallway room (ConnectWithElbow, perpendicular doors). All paths run IsPathClear (point-to-segment distance vs. every other room) so a corridor can never cut through unrelated geometry.
    3. If no door connection works but the spot still overlaps something, TryRouteAroundObstruction tries extending further out, then a single 90° bend, before falling back to FindClearPosition (a ring/spiral search) as a last resort.
    4. Otherwise builds the room normally via BuildRoom, rolling cardinal-direction exits (PickCardinalAngles).
    5. Known limitation: an elbow hallway's collision footprint is only a small circle at its bend corner, not its full rendered length — something can in rare cases still slip past a corridor without being caught.

Visualization (Visualization/)

  • MazeSceneBuilder — the scene entry point ([RequireComponent(MazeGenerator)]). Generates the configured start rooms, spawns agentCount agents on staggered coroutine delays, wires each agent's AutonomousAgentBrain, tracks win conditions (target finished-agent count and/or a time limit — at least one must be set, else it warns and defaults to requiring all agents), and draws a simple IMGUI HUD (elapsed time/finished count, a clickable agent list that makes the camera follow one, a "zoom to fit maze" button).
  • RoomView — placeholder square sprite per room; renders elbow hallways as stretched/rotated rectangles along CorridorWaypoints instead of a single small square.
  • ExitMarker — placeholder door sprite. Purely informational (not clickable — movement is autonomous now): green/blue/gray coloring is relative to whichever agent the camera is currently following.
  • PathVisualizer — one LineRenderer per hop; only the followed agent's trail is shown, with the most recent hop bright and earlier hops dimmed.

Agents (Agent/)

  • MazeAgent — plain C# (no MonoBehaviour) state: current room, path history, visited-room/used-exit sets, IsFinished. Cheap enough to run many of.
  • MazeAgentController — scene-facing wrapper: positions a GameObject, tints it a per-agent color, and drives TryUseExit (which calls MazeGenerator.GenerateConnectedRoom lazily if the exit isn't resolved yet).
  • AutonomousAgentBrain — per-agent decision loop. Waits a randomized initial delay (so agents don't all decide in lockstep), then ticks a steady decisionInterval. ChooseExit is explore-biased: prefers an unused exit (fresh territory) > a used exit into a room this agent hasn't personally visited (following another agent's discovery) > backtracking into an already-visited room, with exploreChance adding randomness. This is the intended extension point for smarter decisions later (e.g. "peek" a room and weigh danger before choosing).

Camera & input (Camera/, Input/)

  • MazeCameraController — manual WASD pan / middle-mouse drag, plus FocusOn (smooth glide, used only while following an agent) and FrameBounds (instant zoom-to-fit, used by the HUD button). The camera does nothing on its own unless something calls one of those.
  • MazeInput — input-backend-agnostic helpers (ENABLE_INPUT_SYSTEM vs legacy Input), since this project has the Input System package installed and legacy APIs like OnMouseDown/Input.GetKey silently break if Active Input Handling is set to "Input System Package (New)" only.

Conventions worth preserving

  • Rooms/exits are cardinal-only by design (not free-angle) — this is what makes "which side faces which room" and 90°-bend hallway routing well-defined. Don't reintroduce arbitrary exit angles without reworking the routing/merge logic that depends on exact cardinal matching.
  • All visuals (RoomView, ExitMarker, agent sprites) are intentionally flat placeholder sprites sharing RoomView.SharedSquareSprite — swappable for real art later without touching generation logic.
  • Generation must stay lazy: don't pre-generate rooms beyond an unused exit "just in case."