Game Jams

PROGRAMMING

  • HIGHLIGHTS

MUSIC PRODUCTION

Mini Jam 83

Dungeon Horror

A horror game where you have to avoid monsters while finding keys hidden in rooms to exit the level.

Sword & Fangs

Action Survival

An endless survival game where you play as a Dracula killing knights to satisfy your endless thirst for blood before you’re drained of it. Blood is used as a currency to upgrade stats and unlock new abilities in between waves.

Disaster is Coming

Puzzle

A turn-based escape game where you need to carefully make your move before you run out of actions to evacuate all the people out of the building that’s burning in flames!

Dread

In this project, I worked as lead programmer with two other programmers. We broke down the game system into 3 seperate modules: player controller, enemy AI and level generator which I implemented.

Dungeon Level Generator

The level generator sequence was inspired by the one in the Spelunky games, which goes like this:

This system was suitable for the game since we wanted unique level patterns with props to hide from enemies which chase you.

To further achieve this, there are several room layouts with different arrangements of props, which is randomly rotated in 90 degrees for more variety.

I applied more downward bias to encourage more of a straight path from start to end to have similar length for each randomly generated level. The level on the right is created with the bias.

LevelGenerator.Generate
public void Generate()
{
	Rooms = new List();
	map = new bool[levelSizeX, levelSizeZ];

	// Create entrance from top
	int roomPosX = Random.Range(0, levelSizeX);
	int roomPosZ = 0;
	CreateRoom(roomPosX * distanceX, roomPosZ, RoomType.Entrance, roomPosX);

	// Create rooms middle to exit
	while (roomPosX >= 0 && roomPosX <= levelSizeX - 1 &&
		roomPosZ <= 0 && roomPosZ >= -levelSizeZ + 1)
	{
		// Left, right or down (more downs to encorage more of a linear shape)
		int randomDirIndex = Random.Range(0, directions.Length);
		Vector3Int randomDir = directions[randomDirIndex];

		int newPosX = roomPosX + randomDir.x;
		int newPosZ = roomPosZ + randomDir.z;

		// Check if we're hitting the edge of the map
		if (newPosX < 0 || newPosX > levelSizeX - 1 ||
			newPosZ > 0 || newPosZ < -levelSizeZ + 1)
		{
			// If we're not hitting bottom edge, then move down and create a new middle room
			if (roomPosZ > -levelSizeZ + 1)
			{
				randomDir = directions[2];
				roomPosX += randomDir.x;
				roomPosZ += randomDir.z;

				CreateMiddleRoom(roomPosX, roomPosZ, randomDir);

				continue;
			}
			// If we are on bottom edge, then mark new room as exit and end generation
			else if (roomPosZ == -levelSizeZ + 1)
			{
				Room exitRoom = Rooms[Rooms.Count - 1];
				exitRoom.ApplyType(RoomType.Exit);

				CreateHallway(exitRoom, directions[2], PassType.Exit);
				CreateDoor(exitRoom, directions[2], PassType.Exit);
				break;
			}
		}

		// If chosen direction is blocked by another room, then pick another
		if (map[newPosX, -newPosZ] == true)
			continue; 

		// If we are just within the map, then create a middle room
		roomPosX = newPosX;
		roomPosZ = newPosZ;

		CreateMiddleRoom(roomPosX, roomPosZ, randomDir);
	}

	// Create path nodes for enemy AI
	mapHolder.CreateNodes();
}

Dracula

Combo System

The combo system features a range of attacks, from basic attacks to special charge burst and projectile attacks by stacking single taps and holding inputs. It's also compatible with PC, controller and mobile.

Basic Attacks
Charge Blasts
Blast Projectiles
Wave Spawner

For the endless game loop we needed a wave system that progressively gets harder as more new enemies are introduced.

The system makes creative use of the gradient component for editing and visualising the spawn probability of different enemies per wave. Enemies are spawned at a random position on the nav mesh.

Object Pooling

In this game jam, one of the biggest challenges we faced was low performance when gameplay gets more intense later on.

In order to maintain performance while many objects of different types are present in the scene, I implemented some methods including object pooling. The object pooler recycles many types of objects including: enemies, projectiles, combat text, particle effects and audio players. This greatly boosted performance during gameplay.

ObjectPooler.Get
T Get(T prefab, Dictionary> poolDictionary, int id, Vector3 pos, Quaternion rot,
		 ObjectData objectData) where T : MonoBehaviour, IPooledObject
{
	if (!poolDictionary.ContainsKey(id))
	{
		Debug.LogWarning("There is no existing " + prefab.name + " pool with id " + id);
		return null;
	}
	Queue pool = poolDictionary[id];
	return Get(prefab, pool, pos, rot, objectData);
}

T Get(T prefab, Queue pool, Vector3 pos, Quaternion rot,
	ObjectData objectData) where T : MonoBehaviour, IPooledObject
{
	T objectToGet = (pool.Count == 0 || !recycle)
					? Instantiate(prefab, pos, rot)
					: pool.Dequeue();
	objectToGet.transform.rotation = rot;
	objectToGet.gameObject.SetActive(true);
	objectToGet.transform.position = pos;

	objectToGet.Initialise(objectData);

	return objectToGet;
}

Disaster

Isometric Grid Movement

I implemented isometric grid-based movement for the avatars. On selecting a avatar, available path tiles appear, with detection of moveable and immovable objects.