All That Lies Below
All that lies below is a game about mining developed in Unity.
Specifically, it tries to play off the fantasy of being a gold digger at the beginning of the century. Of course, this means that the main activity is mining, but what else comes with this fantasy? Setting off searching for riches always meant sacrifice, it meant leaving something behind. We went for the most obvious trope: a family in need, because it is quick to understand and allows for great extrinsic motivation. The fantasy of being a gold digger seems about gold and fame, but in reality, the adventurous nature, the repetition and continuous failures and the responsibility and pressure to make something out of the situation, to not fail, are the stronger emotions. In these times of crisis and self-isolation, playing a man who has to keep moving forward, day after day, with minimal social contact and no guaranteed progress feels a lot closer that one might expect at first.
To gamify the fantasy and play of the setting we realized quicky that the urge to explore and the desire to find secrets underground would be a key factor in our experience.
These aspects ended up representing our initial core pillars:
· Mining
You are a miner and the challenges of the world are the challenges a miner face.
· Daily loop
You are a man with responsibilities, your family needs your help, and you need to keep going back in order to move forward.
· Sacrifice
The game is about making sacrifices. Initially the sacrifices where handled by a physical local: The Altar, which would reward you in exchange for game changing sacrifices. (like sacrificing your family or your happiness)
In the later stages sacrifice became more of a Motif:
Narratively you sacrificed being close to your family to be mining. You also sacrifice your precious time to keep exploring.
We soon realized that our attachment to sacrifices was caused by three important aspects:
The sacrifices where a way to talk about something more important: Everything has a cost.
How are you really paying in these hard times?
What is the real cost of the gold John finds?
The sacrifices also always included the other side of the coin:
The different types of rewards.
Is finding all the gold your family needs underground and becoming rich the reward you are seeking?
Is the greatest reward finishing the game or paying for your sons’ collage?
What about the mineshaft you have built? The consequence of your work in the pursuit of happiness, is that not the real reward the game gives you?
When playing the game myself, I quickly end up spending my resources for two things: A radio and a ball.
I’m not mining to find the gold, or to send money to my family. Of course, I feel the responsibility to do that, but primarily I am mining because I enjoy it. Because it is playful and calming.
That should, in many ways, reflect my stance towards game development and the end of the study. The bachelor thesis is the concluding act of our study. While developing the game we were not only faced with the challenge of finishing it, we were also faced with the challenge of accepting that the study is coming to an end and to understand what we want to do with it.
In the end Sacrifice made way for a new and more important pillar: that of exploration and discovery.
Who knows what lies at the end of the mine?
The idea that there might still be something, is often what makes you continue moving forward.
While the game was meant in no way as a metaphorical one, I have come to terms with the fact that our games end up saying more about ourselves than we expect.
For me the game is about responsibility, persistence and finding joy in the small things.
As of right now John cannot go back to his family, the only thing he can do is continue mining. The game also lacks a real ending.
Will he end up like the old miner in the cave?
Game Architecture
All that lies below was the third bigger platformer me and Lucas developed together following our internship at the HKW and Corona World.
This allowed us to save a lot of time understanding the developing pipeline and the work separation between us.
In the end the work separation boils down to a simple algorithm: Who feels like doing a task takes it.
This works well between us because we share different interests and both have a strong sense of completeness, meaning that uninteresting tasks were never left behind.
As we can both function as generalists, this meant that all development parts where somewhat covered and overlapping.
Because of Lucas artistic proficiency and my programmer knowledge the visuals we ended up naturally splitting these kinds of tasks. Lucas took care of the View of the game while i handled the Model.
For non-programmers it’s easy to forget the amount of time and energy that goes into making a game functional, stable and expandable, which are often the main factors in blocking a games growth and in lowering morale.
Here are some of the most important architectural decision.
Dependency Injection
I decided to switch to Zenject a few weeks into the development.
A main cause for bugs and slowed development is the presence of bad dependencies. Systems start relaying on other system become more and more intertwined and coupled over time. This is especially true for games with a lot of dynamics and a lot of small systems.
With the help of Dependency Injection all dependencies come in the same form: An Injected Object. With Zenject these are also always localized at the beginning of the file or in a so called “Installer”. This streamlined the development process of anything:
Create a new script, inject the necessary dependencies, implement the behavior.
If something needed to become a dependency on its own it always meant taking the intentional decision of adding it to an installer. This naturally forced the architecture to be vertical and to avoid circularities. It also meant that it was really difficult for Lucas to make an architectural mistake without knowing that it might happen, as dependencies can only be created very knowingly.
Clear Categorization of Classes
There are pretty much four types of classes in our project.
Data Objects, Handlers, Behaviors and UI scripts.
Data objects simply hold data. Things like player settings and Items attributes. That’s all they do and all they should do.
Handlers take care of abstract concepts. Often these are mapped to specific mechanics. There is a Progression Handler, a PlatformHandler, a Transition Effect Handler etc. Anything that is conceptually larger than a single thing gets its own handler. Handlers are also (nearly) the only thing that gets passed around as a dependency.
Behaviors are scripts that make a thing behave like that thing. For example, the Platform script makes the platform visually attach correctly and connects to the underlying map, but it’s the Platformhandler that takes care of connecting different Platforms together.
With every task the pipeline was simple:
Are all the abstract concepts in place?
Yes: Inject them with dependency injection and write a behavior
No: Write a handler for the abstract system, make it injectable and then write the behavior
Every bigger concept in the game probably has a Handler for it. (This connects back the Single responsibility discussion above)
Following S.O.L.I.D.
Over the last projects I’ve been working hard on understanding the SOLID principles. Specifically, it was important for me to understand how and when to use them in a small indie game development context. For our previous platformer: Corona World, I ended up trying too hard to stick to these principles.
I tried to program closer to what software-developers expert recommend for large software, which often does not work with games.
Here are my learnings from then, which I applied in this project.
S:
The single responsibility principle is everything. A game developer that doesn’t apply it constantly and think about it all the time will never be able to keep anything larger than a prototype under control. Most games are a large amalgam of smaller systems. The programmer needs to be able to think about the individual component in its small form, even more than software developers.
It’s all about understanding what the machine is doing and games are quickly to big to be understood in their completeness. If you have one object for one responsibility, a human can still make sense of it.
O:
The Open / Close principle should NOT be followed when the game is not fully defined (which, let’s be honest, we have learned is never the case.)
Designers NEED to have the space to change their mind. If a designer changes their mind it is often out of necessity. And we need to support this. Following the Open/Close principles encourages expansion and fights modifications. In the best of all worlds for games we would need an Open/Open principle, which is impossible.
Personally, I’ve come to adopt the following process when developing a new feature: Decide about the Open / Close principle by talking with the designer and understanding how sure they are about what I need to implement. If the designer is thinking (trying to understand and experience a design idea) than you should never build a closed system. If the designer is very sure and you know that probably there will be no going back on the idea, follow the Open Close principle as it results in cleaner and more sustainable architecture down the line.
L:
The Liskov substitution principle is in its pure form a necessity. The extended interpretation requires an excessive abstraction of ideas. This was one of the mistakes I made in Corona World and I ended up writing a huge number of interfaces which were only implemented by one class anyways.
My learning:
The Liskov substitution principle should be followed until the last days of production, where anything goes, but there is no need to excessively abstract concepts if you are in a small development group. (Overdoing even causes confusion. Do it only when necessary.)
I:
Interface segregation is really about team size and product size, if you are just a few programmers, who cares about the interfaces not being clearly separable.
As games are more complex than complicated, worst case scenario you have to refactor a few classes down the line.
D:
Dependency injection is the reason I can sleep well at night. Unity encourages terrible architectures. In Unity anything bigger that a prototype should use Dependency Injection if you want the program to still be stable a few months down the line. (I’ve been looking at a few Unity developing companies in Berlin recently and they all add “Knowledge of Dependency Injection frameworks” in their requirements, so I know they agree with me.)
The Save and Load System
The saving and loading system plays a central role in our game.
This is because both the normal saving and loading as well as the saving and loading of sub-scenes run through the same system.
Every underground cave as well as the overworld are developed in custom levels, where they are specifically assembled.
At the beginning of the game these are all loaded into the playing scene.
Al the necessary handlers are also spawned in as well as the Player and Camera. Making the game playable. This assembled final scene relies on a few core ideas to work.
Initially we would spawn the extra scene using Unitys “Additive Scene Loading”. This quickly resulted in 10-15 seconds of waiting time, for a few scenes, which made the development very painful.
For that reason, I reworked the saving system to allow saving and loading custom scenes.
At the beginning of a new game all our custom scenes are Loaded into the game at a specific offset. When the game is then saved by the player, by going to bed, the final, assembled scene is saved in one combined object.
This allows us to have procedural and semi-random generation, while still saving the scene correctly.
Key feature of the save system is the ability to save the tile system submaps as well, and to “imprint” them onto the playing map.
When loading additively not only do the objects get spawned, map areas get carved onto the main map as well.
Because of this the Main “PlayScene” only contains a sporadic amount of GameObjects in Editor and we can both play, test and develop in this scene without getting merge conflicts or stepping over each other’s feet.
The Dialog System and Miro Parser
I was interested in adding some form of narrative from the beginning. The first altar drafts already included branching conversations and topical responses. The problem was always the development pipeline. Throughout the process I wrote different dialogs systems. Starting from the first one, where I wrote the dialogs in excel and connected them with IDs similar to what you would find in an old design document.
Due to its cumbersome nature we soon reworked to a static structure where you don’t converse with the dialog but directly can decide what to sacrifice for.
The idea to parse data from Miro was in my head from day 1. My problem was always that I feared the implementation would take too long a be really problematic.
As is often the case, this did not happen. I achieved a functional Miro exporting script very quickly thanks to the great Miro Developer API. From there it took a few steps to get it to work as a Dialog Tree developing tool.
The core idea is very simple: The best way to visualize branching dialogs are trees. These are extremely easy to build in Miro, with the help of arrows to connect different sections. All i needed to do is to convert the Miro widgets into an internal tree representation. This was, of course, not as simple as it sounds.
Here are the steps I take to convert the Miro boards into in game dialogs:
1. Export the Miro board as JSON. For that I run a simple script in my Chrome Brower
2. Load the JSON file in Unity. For this I created a small custom Editor, which simply uses the default Windows file selector to select and then read the File.
3. With the help of Unities JSON library I convert the Board to a Usable C# object. This already filters out a lot of unnecessary data, as I only parse the data I need, such as Type, Id and arrow connections
4. Because I always export the whole board, the first step after loading is to remove all the widgets I clearly will not need, such as Frames, Text, Images etc. until I am only left with Cards and arrows
5. Now I am only left with card trees and individual cards. So I first remove all cards that are not connected by arrows
6. As we also tend to build card trees for other purposes outside of Dialogs, I need to only keep the Trees that start in “Dialog” or in “Option”.
7. To do that I need to convert the Widget list (including both Cards and Arrows) into a Tree structure of cards, where the arrows are replaced by connections between the different card nodes
8. Now I am left with a list of nodes and the connection they have to other nodes. As well as a list of Root nodes. (Which I internally called a StringTreeCollection)
9. I serialize this collection and save it into a file. This can be understood as my intermediate format. This way on GIT only a tiny file gets pushed on every dialog update instead of me pushing the entire board every time. This is also the last step done when loading a new board version.
10. Upon game startup the previously saved file gets loaded again and the next translation step starts.
11. Because the content of every node has a very specific behavior based on the displayed text (“Trigger” needs to trigger an event, “SetVariable” needs to set a variable), i convert the StringTrees into Dialog trees by parsing the content text and converting it into a dedicated Object. So, a Node containing: “Trigger CamShake1” becomes an object of type DialogTriggerNote with a variable set to “CamShake1”.
12. I now have an unusable logical tree of nodes with different behaviors. This is very close to a Behavior tree system. The final step is to simply run one of the root dialog nodes through an object which can execute it correctly. This involves a lot of architectural steps, but it basically boils down to: When the current node needs to display text tell the visualization to display text and then switches to the next node and so on.
While these steps sound very complicated, it was actually a quite quick and painless process, that was stable after 1-2 days.
I wish I tried this out earlier instead of waiting until I had no other option. I tend to overestimate the complexity of certain systems, which is something I also did for the Kernel rework for the crumbling system.
Letters and Love
I proposed the idea of exchanging letters with an external narrative figure very early into the prototyping. I was fascinated by the parallelism between modern Text messages and Tweets and
how a letter system would work in the game. The letters need to be short to be read (Around 100 words in the final version) and they usually get a response or follow-up in about 5-10 minutes. This allowed us to encourage the feeling of loneliness by using the thematically fitting concept of letters and still provide an experience that we can still identify with in the digital era.
We have all sent strong emotional short messages through WhatsApp or per message and know the feeling of waiting for a response. When Isabel sends you a letter, it’s the same experience as if you had just received a message, without the narrative setting being broken.
This postbox/letter system quickly expanded to other systems, like our shop.
We suddenly had this Postbox, which was our connection to the outside world.
Initially my plan was to write back and forward conversations between John and Isabel. Due to the explosive nature of this system and the fact that I was always busy taking care of the other priorities, we soon realized that writing these letters would have been a bit of a problem.
Cutting the system was not an option, as it really helped making the world believable and real and had grown into a central motivator for the player.
We thus decided to rework the system to reduce the number of letters needed and to find someone to write them for us.
Having someone write them for us ended up being a good decision. Rebecca’s (our writer) letters where to the point and better written then mine. Most importantly it allowed me to concentrate on other aspects of the game, while still maintaining a directing role in the overarching narrative, which was the aspect I really cared about. I loved the idea of conveying the problems of a distance relationship with these letters.
In my head this simple letter mechanic alone adds a completely new dimension of thoughts in the mind of the player.
Will i make it in time? How is my son doing? Will my wife leave me?
These are powerful questions, especially taking into consideration that none of these characters really exists.
Because letters arrive, in the mind of the player, an outside world gets created, even though our world only consists of a Hut and some underground structures.
Our development cycles always shifted the narrative aspects to last. Our first priority was to make the core game loop: Mine -> Discover -> Sleep fun and enjoyable. The bigger loops ended up having longer iteration cycles. This means that now, upon delivery, I am unsure how well the latest iteration of the letter system really works.
Because of the short development time and the fact that understanding how well the narrative works requires a lot of playtest, I think we took the right decision by prioritizing more central aspects.
Production-wise the fact that we had no time to iterate of the final state of the letters implies a very clear problem: We tried to do too much, with to little time.
Wanting to do too many things is a staple problem throughout this Project. As it is our final Project and we are finally capable to technically create what we design and desire, we really wanted to do everything in this game. (And I still plan to add Multiplayer as soon as I get a few days of break)
Is that production-wise a mistake?
Definitely.
But this is not a project where we need to make all the right choices, this is a project where we can have fun. Some of the most fun mechanics in the game: Like the Ball, Radio and Elevator came from one of us having fun, developing by intuition and by instinct.
Instrumental Convergence
At the beginning of the prototyping phase we intentionally decided to not design the games around our thesis topic. I had a few interesting ideas to apply the thesis of instrumental convergence in a very direct way, but they all required drastic limitations. Generally, using the theory in a production context, instead of a targeted game, more closely fits the ideas described in the thesis.
If you don’t know what I’m talking about, this section is refering to my Bachelor Thesis: Instrumental Convergence for Game Development, an analysis of the utility of the theory of Instrumental Convergence in games, which you can find here.
Concretely, we used the theory sporadically during the development.
The reasons behind that are a few:
· The theory itself, as a lens, is meant as a designer’s tool to be used when discussing motivation and predicting behavior. In most games, like this one, this will not be often the case.
· Because both Lucas and I spend most of our time developing, we spent a lot less time thinking about the game, than one would do in a traditional designer position. I probably spent around 60%+ of my time programming. With the rest of the time being split between, designing, planning and discussing.
· As it mainly is a theory of numbers, that works especially well with games targeted for large audiences, the game was also not a good fit.
Regardless, I was surprised by how often applying the theory in my thought process allowed me to understand certain situations better.
A good example of it being applied practically is in the discussion of the consequences of ingame-death.
Practically I have a few corrections to make to the thesis thanks to my experience with the project.
The basic drives play a minor role in the motivational aspects of a game. Leading factors like extrinsic motivations (quests, narrative goals, win conditions) combine with the basic drives and expectations to merge into a final amalgam of motivation. In the thesis i always observed them individually, while playtesting our game I often found that they had the greatest effect in combination with another motivation.
Here is a good example:
We had a few playtesters with a lot of experience in similar games like Minecraft and Terraria.
This was a huge problem! Because, even though the games have large mechanical similarities, they function in completely different ways. The punishment for death in normal Terraria is completely negligible. More importantly, the initial core loop of Terraria is all about going down as quickly as possible. These players entered our game adopting their basic drives from their experience playing terraria. They rushed downwards as quickly as possible and were completely punished by our death system. We don’t want our game to be rushed, as we thing that it is the most enjoyable when taken rather slowly. So we put in place multiple aspects to slow you down, our death being the main punisher. But these factors where put in place expecting the basic drives of a human: Terrified of death, cautious and careful. For these warped drives our design is terrible!
I discuss the topic of real-life drives being carried over into a game, but i never considered how effective this also was between similar games. A similar example happened with Minecraft. In Minecraft the creativity drive thrives, everything is so dynamically connected that finding creative solutions to problems is nearly always the right answer! The building mechanic is a big part of this.
That is not the case in our game, design wise our game definitely encourages the Efficiency drive over the Creativity drive. The game is very rational and is easily predictable, all factors that encourage the Efficiency drive. Minecraft players coming in with a boosted Creativity drive were quickly left disappointed. As we were not rewarding them for their creativity.
Did we fail to encourage the creativity drive or did we fail to communicated the core differences between our game and Minecraft?
Personally, I find these expectation-related morphed basic drives to be extremely fascinating.
But at the same it throws a bit shadow onto my theory. If the basic drives can be so easily morphed, that it requires a deep understanding of social expectations to predict them, how helpful are they really?
A second addition I would make to the thesis is on the chapter of Instrumental driven prioritization. In it I discuss how using this strategy is effectively the safest way to gamble a development decision. While I still consider that to be rationally sound, i have come to realize that it is not helpful.
My realization is the following:
If you find yourself gambling a decision in the development process something has gone very wrong. The events that lead to the situation should be analyzed and a decision should be drawn accordingly. So, you should never end up actually using the technique.
In my head, I compare my advice to the advice you get in case of a shark attack.
If you ever find yourself attacked by a Shark, your best bet is to hit it in the eyes. While that is probably true, a more helpful advice is to discuss how to NOT end up in a shark attack in the first place.
Instrumental driven prioritization is the same, while it might be and interesting technique, there is more useful advice around (see the entirety of Agile Processes), which should take priority over a last resort strategy. If you end up needing to take a development gamble, you have bigger problems than the decision itself.