With our procedural generation engine capable of producing over 285 trillion unique dungeon layouts and our characters fully animated, the next critical step for Project Labyrinth was to give the game a memory.

Phase 2 focused on transforming a raw tech demo into a structured, persistent gameplay experience. This involved engineering a centralized interaction hub, resolving hard-teleport physics clashes, and building a robust, cross-platform JSON save/load architecture.


The Campfire Hub & The Dynamic Character Swap Loop

The campfire serves as our localized safe zone—a tactical pause where players can manage their party.

  • The Interaction State: Approaching the campfire triggers a CampfireInteraction system. This locks the player’s physical input and safely transitions the camera system to handle preview models.
  • The Hot-Swap Loop: Inside the GameController, I engineered a seamless prefab replacement loop. The script records the exact position and rotation of the active player, destroys the old class model, and instantiates the newly requested archetype (Knight, Ranger, or Cleric) on the exact same frame.
  • The Technical Hurdle: Initially, swapping characters caused the Cinemachine virtual camera to lose its tracking target, resulting in a broken visual state. I resolved this by establishing a strict “handshake” protocol: immediately upon instantiation, the GameController explicitly fetches a child CameraTarget transform from the new PlayerController and forces Cinemachine to re-focus, preventing any tracking dropouts.
Resolving Level Spawning & Physics Clashes

Moving a player through a procedurally changing world requires absolute mathematical alignment between the generation scripts and Unity’s internal physics system.

  • Automated Position Synchronization: I cleaned up the “stride math” between the variable room scales (10, 15, and 20 units) by creating an automated position sync function. This ensures the playerSpawnPoint and the world builder’s structural geometry align flawlessly on every new level generation.
  • The Technical Hurdle (The CharacterController Fight): During room transitions with the portal, the player would frequently snap back to older rooms or glitch out of bounds.
    • The Cause: Unity’s native CharacterController component processes physics on its own internal cycle and fights manual transform.position updates.
    • The Solution: I modified the teleport function to briefly disable the CharacterController component, execute the hard coordinate shift, and re-enable it on the next line of code. This forces the physics engine to respect the new spatial coordinates instantly.

Designing the Data-Driven JSON Save System

To allow players to save their progress at a campfire and return to the exact same procedural layout, I designed a lightweight save system using a Data vs. Assets strategy.

The Architecture (SaveData.cs & SaveSystem.cs)
  • Overcoming Unity Serialization Limits: Unity’s native Vector3 cannot be directly converted into plain JSON strings. To bypass this, I wrote a custom, purely serializable SerializableVector3 struct that breaks the coordinates down into simple float primitives (x, y, z).
  • Data vs. Assets (Future-Proofing): Instead of saving heavy, runtime-dependent assets like UI Sprites or 3D prefabs directly into JSON text, the system uses a string-mapping architecture. Items and challenges are reduced to lightweight text identifiers (itemID, challengeID) that map back to static ScriptableObject blueprints.
  • The File IO Engine: A static SaveSystem script serializes the data structure into a JSON string using JsonUtility and securely commits it to the operating system’s local memory directory with Application.persistentDataPath.

Bulletproofing the Loading Sequence

A save system is only as good as its restoration stability. Hooking the save functionality up to the Campfire UI revealed critical timing bugs that required deep-engine solutions.

  • The Technical Hurdle (The Ghost Player Bug): On initial load tests, the game would mistakenly spawn the player back at the map’s default origin instead of the saved campfire coordinates.
    • The Cause: Unity’s standard Destroy() function is asynchronous and waits until the end of the frame to clean up garbage. The loading script was executing faster than the cleanup, mistaking the dying player object for the active one.
    • The Solution: I switched the cleanup sequence to DestroyImmediate(). This cleanly vaporizes the old player object instantly, clearing the “Player” tag from the hierarchy so the loading function can safely execute a fresh spawn.
  • Camera Snap Polish: To eliminate a jarring 1-to-2 second camera interpolation delay when exiting the campfire menu, I forced Cinemachine to execute a hard .Warp() command, instantly snapping the framing rather than slowly panning back from the menu camera setup.

Technical Standards Demonstrated

Advanced Engine Architecture: Overriding Unity’s physics cycles (CharacterController locking) and frame execution order (DestroyImmediate).

Data Serialization: Designing custom serializable structs and decoupling asset references from state data with JSON.

Camera Systems Control: Real-time manipulation of Cinemachine target tracking and warp states.

State Management: Executing runtime prefab instantiation and state persistence pipelines.


Moving Into Phase 3

With our characters, procedural world, and persistence systems completely locked down, the foundation of Project Labyrinth is concrete.

Stay tuned for Dev Log #5, where we move into Phase 3: Loot & Narrative UI. I will be taking advantage of our newly built JSON placeholders to implement a shared inventory system, randomized NPC “Barks,” and daily game challenges.