Skip to content
sp1le.tech
UA
Playable build 2026 Co-op heist prototype · Photon Fusion + Voice

Getaway

This started as a test bed: I wanted to see how Photon Fusion 2 and Photon Voice 2 behave inside a real scene with doors, guards and a getaway, not inside a vendor demo. It grew into a playable solo heist - get into the pawn shop, crack the safe, carry the bags out past guards and police, drive away. The level, props, characters and UI are produced by editor code, which is the only reason one person could keep a project this size coherent. The multiplayer it was built to exercise has not been run yet: no Photon App Id, so no second player.

Unity 6 · C# · Photon Fusion 2 No public build yet. It runs on my machine, which is exactly as much as I claim.

Constraint

No art team, no level designer, no budget. Everything the player walks through has to come from free asset packs plus code, and the whole block has to be rebuildable from scratch after any change.

Decisions

  • 4 records
  • each with its bill

Decision record

The level, prefabs, characters and UI are generated by editor code

Instead of
Authoring scenes by hand in the Unity editor
Because
A hand-built scene is a binary blob that one person can edit and nobody can review. Generated content turns the entire block into a readable diff, and a full rebuild is one menu item or one batchmode command.
Costs me
3 918 of the 10 723 lines are tooling that ships to no player. Moving one wall means editing code, not dragging a handle.

Stairs are drawn as steps over a single hidden ramp collider

Instead of
Collidable step geometry
Because
The player walks a smooth slope, the NavMesh bakes a clean surface, and the eye still sees stairs. Real step colliders snag character capsules and give agents pathing they have to fight.
Costs me
Visual and physical geometry deliberately disagree, which has to stay written down or a future change quietly re-introduces the snag.

Photon Fusion 2 and Photon Voice 2 live behind compile defines

Instead of
Depending on the networking SDK everywhere
Because
The project has to open, compile and stay playable without a Photon App Id. Networking is a feature of the game, not a precondition for working on it.
Costs me
Two build configurations to keep compiling, and networked code paths that are easy to leave untested precisely because the default build never runs them.

C# is verified by running Unity headless in batchmode

Instead of
Opening the editor to check every change
Because
A headless compile takes a fraction of the time and runs from a terminal, which is the only practical way one person keeps a ten-thousand-line Unity project honest between play sessions.
Costs me
Batchmode proves it compiles and that content rebuilds. It proves nothing about how the game feels, so playtesting stays entirely manual.

Source

  • Assets/_Game/Scripts/Editor/GetawayLevelKit.cs

From the source

The stairs decision in code: each step is a cube with its collider stripped, and one hidden, rotated slab underneath is what the player and the NavMesh actually stand on. Two of the three comments are mine for this page; the rest is verbatim.

public static void Stairs(Transform parent, string name, Vector3 start, Vector3 direction, float run,
    float rise, float width, int steps, Material material)
{
    var root = Empty(name, parent, Vector3.zero).transform;
    Vector3 dir = new Vector3(direction.x, 0f, direction.z).normalized;
    Vector3 side = Vector3.Cross(Vector3.up, dir);

    float stepRun = run / steps;
    float stepRise = rise / steps;

    for (int i = 0; i < steps; i++)
    {
        float top = start.y + stepRise * (i + 1);
        Vector3 centre = start + dir * (stepRun * (i + 0.5f));
        centre.y = top - stepRise * 0.5f;

        var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
        go.name = "Step";
        go.transform.SetParent(root, false);
        go.transform.localPosition = centre;
        go.transform.localRotation = Quaternion.LookRotation(dir, Vector3.up);
        go.transform.localScale = new Vector3(width, stepRise, stepRun);
        go.GetComponent<MeshRenderer>().sharedMaterial = material;
        Object.DestroyImmediate(go.GetComponent<Collider>());   // the eye gets steps
    }

    BuildRamp(root, start, dir, side, run, rise, width);        // the feet get a slope
}

private static void BuildRamp(Transform parent, Vector3 start, Vector3 dir, Vector3 side,
    float run, float rise, float width)
{
    const float thickness = 0.4f;
    float slope = Mathf.Sqrt(run * run + rise * rise);

    Vector3 alongSlope = (dir * run + Vector3.up * rise).normalized;
    Quaternion rotation = Quaternion.LookRotation(alongSlope, Vector3.Cross(alongSlope, side).normalized);

    // Sit the ramp so its top face passes through the step noses.
    Vector3 topMid = start + dir * (run * 0.5f) + Vector3.up * (rise * 0.5f);
    Vector3 centre = topMid - rotation * Vector3.up * (thickness * 0.5f);

    var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
    go.name = "RampCollider";
    go.transform.SetParent(parent, false);
    go.transform.localPosition = centre;
    go.transform.localRotation = rotation;
    go.transform.localScale = new Vector3(width, thickness, slope);
    go.GetComponent<MeshRenderer>().enabled = false;            // never drawn, always walked on
}

Numbers

  • measured, not rounded

Size of the thing

10 723
lines of C#
3 918
of those are editor tooling
83
game scripts
91
materials converted to URP by script

Stack

  • Unity 6000.3.11f1
  • C#
  • Universal RP 17.3
  • Photon Fusion 2.1.2
  • Photon Voice 2.63
  • Input System
  • AI Navigation
  • Editor tooling

Retro

  • written after, not before

What I would change

Networking is integrated and compiles against the real SDKs, but it has never carried four players, because I do not have a Photon App Id yet. I will not call the multiplayer done until I have watched it break under real latency and packet loss - anything else would be a claim I cannot back. Generating everything from code was right for a solo project, and it is the first thing I would reconsider the moment a level designer joined: the tooling that liberates one person becomes a wall for the second.

Next

Contact

  • Full-cycle software engineer

Start a conversation

Tell me what you are building and what is in the way. I answer personally, usually the same day.