Master's Thesis project in Game Design and Technology at the University of Gothenburg
Experimenting with a new mix of game genres:
Developed with
Features generated dungeons
Various card and enemy types
Development period: Feb 2025 - May 2025
Status: Demo available
The only resource in the game is
The game is turn based, with each action (moving, playing or discarding a card, etc.) counting as one turn.
The player moves using the
Holding down a key allows for continuous movement to speed up traversing long corridors.
Cards are selected and played using
Cards can be
The prototype features
This area serves both as the main menu and the safe zone. The player can interact with objects to perform actions like managing their
Utils.GenerateLoot(
amount: 3
dropRate: new Dictionary<Type, int> {
{ typeof(SmiteCard), 40 },
{ typeof(ScoutCard), 40 },
{ typeof(ChainCard), 20 },
{ typeof(TeleportCard), 20 },
{ typeof(WoodenKeyCard), 5 },
{ typeof(GoldenKeyCard), 5 },
}
);
Loot is generated using the
This is an elegant way for generating loot for ›chests and enemies.
Card
.
Walls are just solid cells by default. To make them visually more appealing, one can use a sprite sheet that contains textures for all possible wall orientations.
In some styles (such as in this game), the used texture also depends on the surrounding 8 tiles. Since each tile can be either wall (true) or empty (false), the total combination of the possible surroundings is 28, which is 256. However, some wall textures can be re-used for multiple scenarios, as their layout does not depend on certain cells.
The figure below illustrates how a certain wall texture can be applied in multiple scenarios. In the top-right corner of the image, surrounding cells are categorized as follows:
To address all 256 possible combinations, a Dictionary
can be constructed, where keys are bitmasks representing the state of the surrounding tiles (empty ➝ 0, wall ➝ 1), and values are the corresponding wall texture's atlas coordinates.
Instead of populating the dictionary by hand, the above-mentioned patterns, consisting of the
AddBitmaskEntries(
atlasCoords: new Vector2I(3, 1),
pattern: new List<bool?>() {
null, false, null,
true, true,
false, true, true
}
);
This is what the
var bitmask = GetWallBitmask(x, y);
var atlasCoords = bitmaskToWallAtlasCoords[bitmask];
WallLayer.SetCell(new Vector2I(x, y), 0, atlasCoords);
Finally, the dictionary can be easily used to access the appropriate texture by providing the bitmask that describes a cell's surroundings.
The process is an extended version of the
First, a bunch of rectangular rooms are placed, which later get connected via mazes and doorways. Dead ends are removed to improve the exploration experience.
As the final step, enemies and objects are instantiated into each room. Their number and type depends on the room size, difficulty level, and other factors.