
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

Opening animation

Room Generation & Infinite Loop

Player feels like exploring endless rooms, but only 10 rooms exist and keep recycling.
Fixing a real bug (Correct)

Not a bug (Wrong)

Happy ending: All the bugs are fixed.

Bad ending: Computer error message.

⚙️ 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

This allows child objects of a code-attached object to also have bug effects.
It also allows artists to design some complex special effects.

7 types of bugs
Missing Material

Object Shaking

Missing Object

Object Moved Or Clipping

Collision Missing

Object Flickering

Wrong Object

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);
Project Gallery



