top of page

Bugged to Death

GMTK Game Jam 2025, 3D light puzzle game, Team Project (6 people)


Bugged to Death (GMTK Game Jam 2025, Theme: Loop), a light puzzle game that tells the story of a game developer who collapses in front of their computer from exhaustion while fixing bugs to meet a deadline. The player takes on the role of a character inside the game who must fix the bugs themselves to prevent the game from crashing.


Link to Web Play & Gam Jam:


Team & Responsibilities:

Weiqi Liu  — Lead & Sole Programmer, Technical Artist(Rendering, shaders, tools)

Jiawei Zhang  — Modeling, Scene Building, Level Design

Jiarui Wang  — Narrative Design, Cutscenes, Character Design

Kaixin Huang  — UI / UX Design

Zhexuan Wang  — Modeling, Room design

Min Pan ---- Project Manager


Develop Tools:

C#, Unity



🎬 Gameplay Video



🌌 Background & Gameplay Description


In Bugged to Death, you play as a mage cat in a video game, using newly

learned spells to eliminate the numerous bugs left by the game's developers,

preventing the game from crashing.

Endlessly exploring a wooden cabin trapped in a cycle due to severe bugs.

Remember the cabin's layout; you'll need it later.

Focus on observation and comparison and Find all the bugs

these coding errors are what trap you in this cabin. Some bugs make objects

lose their textures, while others cause models to move erratically. You need to

fix as many bugs as possible.


🔁 Game Flow


ree
Opening animation

ree
Room Generation & Infinite Loop

ree

Player feels like exploring endless rooms, but only 10 rooms exist and keep recycling.

Fixing a real bug (Correct)

ree
Not a bug (Wrong)

ree
Happy ending: All the bugs are fixed.

ree
Bad ending: Computer error message.

ree

⚙️ Technical Showcase


1. Room Generation & Infinite Loop


Initial Room Generation(csharp)

void CreateInitialRooms()
{
    if (roomPrefabs.Count == 0)
    {
        Debug.LogError("Room prefabs list is empty!");
        return;
    }

    int roomCount = Mathf.Min(visibleRoomCount, roomPrefabs.Count);  // 10 rooms
    int centerSequence = Mathf.RoundToInt(initialRoomCenter.x / roomSpacing);
    int startSequence = centerSequence - playerCenterPosition;

    for (int i = 0; i < roomCount; i++)
    {
        int sequenceNumber = startSequence + i;
        CreateRoomAtSequence(sequenceNumber, i);
    }

    currentRoomSequence = centerSequence;
}

void CreateRoomAtSequence(int sequenceNumber, int prefabIndex)
{
    float worldPos = sequenceNumber * roomSpacing;  // Position = Sequence × Spacing
    
    GameObject roomPrefab = roomPrefabs[prefabIndex];
    GameObject roomInstance = Instantiate(roomPrefab, transform);
    roomInstance.transform.position = new Vector3(worldPos, 0, 0);
    roomInstance.name = $"Room_Seq{sequenceNumber}_Type{prefabIndex + 1}";

    RoomInstance newRoom = new RoomInstance(roomInstance, sequenceNumber, prefabIndex, worldPos);
    roomInstances.Add(newRoom);
}

Player Position Detection(csharp)

void Update()
{
    if (!isFullyInitialized || player == null) return;

    // Detect player position every 0.1 seconds
    if (Time.time - lastDetectionTime >= detectionInterval)
    {
        CheckPlayerRoomPosition();
        lastDetectionTime = Time.time;
    }
}

void CheckPlayerRoomPosition()
{
    Vector3 playerPos = player.position;

    // Find closest room to player
    RoomInstance closestRoom = null;
    float closestDistance = float.MaxValue;

    foreach (var room in roomInstances)
    {
        if (room.gameObject != null)
        {
            float distance = room.GetDistanceToPlayer(playerPos);
            if (distance < closestDistance)
            {
                closestDistance = distance;
                closestRoom = room;
            }
        }
    }

    // If player entered new room
    if (closestRoom != null && closestRoom.currentSequence != currentRoomSequence)
    {
        OnPlayerEnterRoom(closestRoom.currentSequence);
    }
}

Room Recycling Logic(csharp)

void OnPlayerEnterRoom(int sequenceNumber)
{
    if (sequenceNumber == lastProcessedSequence) return;

    int previousSequence = currentRoomSequence;
    currentRoomSequence = sequenceNumber;
    int direction = sequenceNumber - previousSequence;

    if (direction > 0)
        HandleMovementRight(sequenceNumber);  // Player moving right
    else if (direction < 0)
        HandleMovementLeft(sequenceNumber);   // Player moving left

    lastProcessedSequence = sequenceNumber;
}

void HandleMovementRight(int currentSeq)
{
    RoomInstance rightmost = GetRightmostRoom();
    int distanceToRightEdge = rightmost.currentSequence - currentSeq;

    if (distanceToRightEdge <= 2)  // Trigger when close to edge
    {
        MoveLeftmostRoomToRight();
    }
}

void MoveLeftmostRoomToRight()
{
    RoomInstance leftmost = GetLeftmostRoom();
    RoomInstance rightmost = GetRightmostRoom();

    // Calculate new position
    int newSequence = rightmost.currentSequence + 1;
    float newWorldPos = newSequence * roomSpacing;

    // Update room
    leftmost.UpdatePosition(newSequence, newWorldPos, roomPrefabs);
    leftmost.ScanForBugObjects();  // Rescan bugs in recycled room
}


2. BugObject Tool for Artists


BugObject is a designer-friendly tool that allows artists to create visual bugs without writing any code. Simply attach the component to any GameObject in your scene, select a bug type from a dropdown, and configure visual parameters directly in the Inspector.


Artist Workflow

3-Step Setup

1. Select GameObject → Add Component → BugObject
2. Choose Bug Type from dropdown menu
3. Configure settings → Press Play → See bug effect!

Inspector Interface

ree

This allows child objects of a code-attached object to also have bug effects.


It also allows artists to design some complex special effects.



ree



7 types of bugs

Missing Material


ree

Object Shaking


ree

Missing Object


ree

Object Moved Or Clipping


ree

Collision Missing


ree

Object Flickering


ree

Wrong Object


ree



3. URP Missing Material Shader


Checkerboard Pattern Generation

hlsl

float Checkerboard(float2 uv, float scale)
{
    float2 grid = floor(uv * scale);
    return fmod(grid.x + grid.y, 2.0);
}

Color Interpolation

hlsl

half4 color = lerp(_SecondaryColor, _BaseColor, pattern);

Flash Animation

hlsl

float flash = sin(time * _FlashSpeed * 6.28318) * 0.5 + 0.5;
color.rgb += flash * 0.3 * _BaseColor.rgb;

Fresnel Edge Glow

hlsl

float3 viewDir = normalize(GetCameraPositionWS() - input.positionWS);
float fresnel = 1.0 - saturate(dot(input.normalWS, viewDir));
fresnel = pow(fresnel, 2.0);
ree

Project Gallery

bottom of page