Making a rudimentary game
Slow ThinkerMay 6, 2023
I have always been interested in computers and especially computer games. As a kid I would day dream about making my own games and imagined being employed at the companies making the games I was playing. I imagined back then that making any game was quite complicated and needed a lot of people. I couldn't imagine the rise of indie games and the possibility of small teams being able to ship compelling and interesting games.
Between then and now I learned programming and started working as a professional web developer. I never let go of my interest in making games, I've tried building them in my spare time, but never got anywhere. Maybe my games were to ambitious, maybe I was trying to jump to being an indie game developer without any experience in the industry. Maybe I wasn't a good enough programmer to actually do it, maybe I can't draw well enough to make some acceptable looking graphics.
I've recently started this journey of sharing what I'm learning and doing in order to strongly commit to becoming more independent and work on the things I genuinely enjoy. I also want to give my own perspective on things and the condensed knowledge of my research in the sincere hope it might bring some value, or to at least make it easier for others to learn. I attempt to do this by providing code that is a middle ground between complex production code and superficial tutorial kind of code. The code reflects my knowledge at the time of writing the article so I assume there will be gradual improvement over time.
I find it useful to document my journey from the perspective of someone who doesn't know how to make games to someone who, hopefully, can make a basic game and publish it.
The game
After my trials with somewhat more ambitious games that never even had a playable game loop, I believe I need a different approach. I think I will have to start at the bottom and work my way up the ladder of complexity. I will start by implementing the simplest games I can think of and improve with each one. One goal I have is to actually finish a game that is playable, has difficulty progression, has a win and lose condition, has some replay value and has a clear defined objective and end.
The simplest game I can think of that I believe I can build in the next 2 weeks (we'll see) is a card matching game.
Spoiler: It took way longer, almost two months, during which I had a bit of a burnout.
Technical aspects
I'm writing this just now, after finishing the game and play testing it a bit. I've set out my objectives beforehand so that I can compare my expectations before and after the implementation is finished.
My initial plan was to make this card matching game using HTML, CSS and Javascript. I wanted to use a functional approach, at least to the level of my FP knowledge. I planned on using Ramda for the FP utilities and Fontawesome for the card symbols. These objectives were mostly accomplished.
I've decided against using the canvas as HTML and CSS were more familiar territory. I was even thinking of porting it to a canvas version to understand how much of the code can be reused.
I've also wanted to add some configuration options for graphics and game difficulty, I've also planned on adding keyboard and full-screen support. I decided to skip these and focus on finishing the game as the development was dragging on long enough.
Rendering
I wanted to keep the rendering as an adapter so that potentially it could be switched with another one. The current implementation uses HTML and CSS to render the game but my thinking was that this approach gives me the option to implement a canvas adapter at some point.
For the UiHtmlAdapter
I decided to use an iife
to pass some specific functions that handle the only DOM interaction I need rather than passing the entire document object. This was an attempt at increasing the ratio of pure functions to impure functions in the overall codebase.
Game logic
The game logic is split between the GameRules
(core) and GameController
.
The core part is concerned with generating the game state and providing the internal logic of how the game state can be changed.
The controller part is the part that orchestrates together the UI changes and the state changes based on the latest interactions. Maybe these split isn't really necessary, I not super happy with it, but it was the way the code evolved during refactoring.
Initialization
This is the intentional impure part of the codebase, this is where the separate dependencies get initialized and injected. This section of code contains the side-effects of pushing the user actions to the queue and setting up the game clock. I knew I had to have some state mutations somewhere, I wanted to make them obvious and isolated and I think this was the right place for them.
I wanted the initialization part to be as small as possible and be concerned only with state mutation and bootstrapping the game.
The process
I was able to build the prototype for the game in a couple of hours, I think around six hours, but this was done over the period of an entire week. I work around 45 minutes to one hour and a half on side projects almost every day, sometimes more during the weekends.
It seemed to go pretty well, I had the game mechanics working in the first week and was thinking that during the next week I could finish a first version of the game, but needed one more week for polishing and refactoring.
The initial version was hacked together in a short amount of time (by my standards) while exploring how the final game mechanics would work.
After starting to add bells and whistles to the game, such as animations, level progression and so on, the code got much more complicated and work started getting slower.
For the initial implementation I tried to mimic React by re-rendering everything when the state changes, but this wasn't very performant. React has the virtual DOM to optimize DOM interactions but I didn't go that far with my implementation. One question that can come up is why not just use React?
The answer to this is that I wanted to avoid extra dependencies if I could help it and avoid strongly coupling my code to a certain library. I would say the usage of Ramda is different, as I can get rid if it without much effort at the moment and I also wanted to learn more about it.
In the end, I've decided to just go with granular DOM manipulation on the elements that need to update based on what was happening in the game. This made a significant difference in how the game performed and felt, but the implementation got less functional and harder to follow.
I started to refactor the code in order to separate the pure functions from the impure ones and group them in modules. After this refactoring the code got somewhat better, I had the UiHtmlAdapter
and the game core separated. The initializer was separated as well but it contained 40% of the code and it was ridden with side-effects.
I felt that this wasn't good enough, I wanted to reduce and isolate the side-effects even more but I wasn't sure how to go about it. That was the moment when I started to procrastinate and lose my discipline. I started being less organized and there were a few days and weekends when I didn't work on the game at all.
While procrastinating I watched some devlogs from other independent people making games and I heard the words "movement queue". Hearing about this I wanted to implement a concept like that in my game. I haven't researched what a "movement queue" actually means, but just hearing the words inspired me to change the way interaction and state updates are being handled.
This was the thing I needed to move forward, I've added an array to my game state to which I'm pushing the clicked card ids. I then added a loop to process queue, update the state and render changes. It was during this time I started to get back into the last refactoring phase, in which the codebase got into a better shape.
The game ended up split in four parts:
- The UI rendering
- The core game rules
- The game controller
- The initialization
There probably are more improvements I could think about and implement, but at some point I just have to stop while I'm ahead and consolidate what I have. I think this exercise served its purpose and I can move on to the next game.
I believe this was a good first step and I hope the next small game will be another good one in my journey.
The end result
The game seemed easy to implement initially, and it was, until I started to add animations and tried to reduce the number of impure functions. It took significantly more than expected.
See the Pen Functional card matching game by (@slowthinker) on CodePen.
Takeaways
I've time tracked most of my work and I usually work in focused increments of 45 minutes. I do most of my work in the morning, trying to invest one or two units of work each day into side projects.
My initial estimate was around 2 weeks. For me, that's about 20 units on work days and an average of 14 during weekends.
That's 34 units, which is approximately 26 hours. In total I've tracked around 36 units, but I'm pretty sure there was more work I forgot to track. I wasn't expecting the estimate to be so close, but the main issue is that I did these units in six weeks rather than the initial plan of two weeks.
Looking back on this, I believe it happened because I lost motivation and started to procrastinate due to the fact I started feeling lost when the code started to grow. I think refactoring complex code without a clear vision of how the end result should look like combined with a general feeling of exhaustion from other commitments brought me to a feeling of burnout.
In addition, I had no planned recreation activities and when I crashed I went to my usual mind numbing zone which is being lazy, not exercising, doom scrolling and online videos. I think I would have been able to recover quicker if I've just planned some timeboxed recreation activities each weekend or a few times a week.
Usually what I like to do is paly video games, but I spend much more time on them than I should and I've made a very strong commitment to stop playing video games until I've published an actual product.
I think I would have been better of playing video games as that's a bit more creative and engaging than passively scrolling while listening to online videos in the background.
I tried pushing myself really hard in the past and although I had great progress in the short term, I eventually burned out for a couple of months and didn't actually achieve anything during that time.
The process of making this game just confirmed this again. I think it's ok to push hard, but it's not sustainable for a longer period of time.
I believe that having intentional and planned time for recreation can help in maintaining the emotional balance one needs to consistently pursue and achieve one's objectives.
I will continue with the next game and apply the lessons I've learned by making this one. I'll start investing a few units in playing some games after publishing this article.