LYRA SHILLABEER Level, World, Narrative and Graphic Designer

Meat Portal

Meat Portal is a roguelite level generation prototype with a first person player controller and a card based inventory system. The prototype consists of the level generator itself, which allows small ingame tweaks such as the amount of rooms in the level, and an item room containing placeholder items to showcase the inventory system, which revolves around the idea that the player can find new slots on the floor and add to their inventory size.

This project was inspired by a video by the satirical YouTube channel Clickhole which depicts a couple's reaction to finding a portal in their backyard that generates infinite raw meat. I entered the project asking myself where the meat from the portal came from, and decided upon a roguelite where every time the player dies, their corpse is harvested for meat and the scraps are ejected through a portal to act as bait.

This project was the first serious project I made using Unreal Engine, and has provided a solid foundational knowledge of the basics of the engine, as well as how to utilise procedural generation and level streaming.

Several years later, I created a GDD that fleshed out a lot of the game mechanics and describes the inventory system in full detail. The page relating to that can be found here

The latest prototype can be downloaded below:

Download Prototype

A generated level, viewed from outside the map. The glowing lines are placeholders for a larger boss room

Level Generator

The level generation algorithm is fairy simple: First of all, it creates an array that represents the grid that the level is placed on based on the world size variable. It then assigns a starting room by finding the central value of the grid array and changing its value to represent the starting room.

The level generator will then pick a random empty tile, and if it neighbours one room then that tile becomes a room. The fact that rooms are only allowed to have one neighbour is what causes the pattern of all the rooms branching from one central point. Once enough rooms have been placed, additional rooms (the treasure room and boss room) are placed.

Once this is done, the level generator then iterates through the array, placing a randomly selected room at the desired location based on its orientation and how many neighbours there are. The rooms themselves are maps, which are placed using level streaming.

A video I made showcasing the level generator, where each tile represents a room

The Player

The player is a simple first person movement system, with the ability to walk around, sprint, crouch and jump. The player can also pick up objects by looking at them and pressing the pickup button. Objects that can be picked up are highlighted with a glowing yellow outline.

The player's inventory consists of a hud filled with item cards, which each store an item and show its name and icon. The card list is based on an array on the player, and the item icons are iteratively spawned form it.

Inventory slots can be found in the world and picked up to expand inventory space

How I would improve this project:

I would completely remake the level generation script to utilise the knowledge I have gained from working on the level editor in Coffee with Cryptids (My latest Unreal project). Firstly, the actual level array would be changed to a map/dictionary matching a position to a room struct, which would contain the room's type, layout and orientation. In the current version, room types and layouts are represented by integers, and while that works perfectly fine, it would be a lot easier to read if I used enums for this.

I feel like while the height changes are good, they happen too often, so I would encourage flatter worlds by adding a 50% chance of failure while trying to place a room above or below another room.

Another problem with the level generator that could easily be solved is that there are no loops, since the rooms branch outwards and aren't allowed to rejoin. I would fix this by changing the level generator so instead of refusing to place rooms that have more than 1 neighbour, it instead has a low chance of successfully placing one, allowing rooms to sometimes bridge together to create loops.

Since I wasn't using enums, I needed to use sheets like this (Which shows every possible room configuration) to keep track of what each ID referred to, which was annoying.