Xenoplanet is a beautiful open-world adventure game. Players can choose to explore an infinite procedural world, or rifle with danger from hostile robots. Whether they choose to take back their world or simply enjoy the freedom to explore, Xenoplanet offers players endless possibilities.
Team: Martin Allsbrook, Lucca Chiappetta
Role: Procedural World Gen, AI, Scripting, QA, Sound
Tools used: Unity, C#, Audacity
Timeline: November 2022 – April 2023
My contributions to Xenonplanet include developing the game’s real time procedural generation systems from the ground up. Designing and implementing combat mechanics and enemy AI that aligned with our desired aesthetics. As well as working as the general scripter and designer, creating the inventory system, world interactions, item drops and more.
I’ve always had a love for large open game worlds, especially procedurally generated ones and the and the replayability they create in exploration games. Because of this one of my main goals for Xenoplanet was to explore implementing a procedural open world.
To create Xenoplanet’s infinite world new chunks have to constantly be loaded around the player. Normally generating a chunk’s heightmap or placing grass would take much longer than the length of a frame, constantly causing annoying lag spikes for the player.
I was able prevent lag spikes like this by breaking up large chunks of calculation with coroutines that allow them to be carried out over several frames. This allows a small amount of calculation to be done each frame rather than slowing down the game to do it all at once.
Each chunk in Xenoplanet needed 4096 grass objects placed at specific heights and orientations, and in Unity the Instantiate method used to create new objects is notoriously slow. This created a unique challenge, as even if I broke up the grass instantiation with coroutines it would take a impractical amount of time
The solution to this was to use object pooling. Rather than instantiating every grass object on each chunk I loaded the scene with 25 objects each containing one set of 4096 grass objects. I could then reuse these 25 objects by moving them to the 5x5 area of chunks around the player, making the world seem covered in grass without ever having to create any new grass.
When chunks are removed from the scene they are are deactivated and saved to a C# dictionary of every chunk that has been loaded so far. Whenever the player is loading new chunks the dictionary is checked first to make sure they chunk hasn’t already been generated. This allows the changes the player makes to chunks to be saved when the player returns to the chunk, and reduces loading time when visiting chunks that have already been generated.
One of my main aesthetic goals when creating Xenoplanet was to create intense encounters that put the player on the edge of their seat. Many survival games accomplish this by putting the players hours of hard work on the line but that conflicted with our goal of creating a shorter term less grindy experience. In order to accomplish both these aesthetics I had to look towards the combat mechanics and dynamics between the player and enemies.
Our first decision was to make the game permadeath. This immediately increased the stakes of every enemy encounter as all the players progress was put on the line. Even though the game only lasts 10 minutes dying to an enemy would put the player right back where they started
The second mechanic I implemented was enemy awareness. All enemies have a limited field of view and max view distance. However once the player is seen other enemies in the surrounding area are also alerted to the player's position and more difficult enemies are spawned. Together these two mechanics forced the player into a more methodical and sneaky playstyle where one wrong move meant serious consequences.
Finally we gave the player very limited ammo and added critical hit spots to the enemies. The player typically finds themselves with around 5 to 10 arrows in their inventory with a maximum of 20 (10 normal, 10 explosive). Without critical hits it takes around 3 to 6 arrows to kill an enemy depending on the type of arrow and enemy, around 60% of the arrows the player is typically holding. However by using critical hits the player can kill enemies in only 1 or 2 arrows greatly reducing the time to kill and the resources expended.