I've been fascinated by computer games for as long as I can remember. In fact, almost all of my early computer knowledge came about because I wanted to play games. A lot of time has passed since then and I now work as a software engineer, and most of the systems I work on don't look much like games. Throughout my career, I've maintained an interest in what game engineers are doing. This article presents a few concepts from the context of computer games that help me to solve the problems that I do face.
This article was originally published on OfferZen. Anne Gonschorek was a huge help in making this article come together. She's looking for more contributers to the OfferZen blog. Take a look at Source for more info.
I've been fascinated by computer games for as long as I can remember. In fact, almost all of my early computer knowledge came about because I wanted to play games. When I was in high school, I had some success writing simple games of my own. I didn't realize it at the time, but I learned much more about programming by fiddling with Game Maker than I did from my high school computer science teachers.
A lot of time has passed since then and I now work as a software engineer. Most of the systems I work on don't look much like games. They have web pages, piles of forms to fill in and users with a job to do. There are real world consequences, like money being invested, home loans being approved, or invoices being sent.
That said, throughout my career, I've maintained an interest in what game engineers are doing. Although I'm currently building applications in the financial space, I often find useful solutions in game engines.
Here are four key concepts that have helped my problem solving.
Using Data Structures and Algorithms to Maintain Order
Let me tell you the story of a problem I had to solve: We needed to build a questionnaire whose questions changed based on previous answers. For example, we needed different information from someone saving for their retirement and someone saving to pay for their children to attend university. To further complicate matters, our system was a multi-tenant system, and each tenant was likely to have their own sets of special requirements, including different questions or a different order of questions. I needed something that would not only work for the specific example in front of me, but could be rapidly customized for other questionnaires. This was quite a change from questionnaires that I'd worked with before, so I wasn't sure how to approach it.
That’s when I watched a video from GDC, the Game Developers Conference. In the video, Piotr Tomsinski from CD Projekt Red explains how they structured the conversations in their hit game The Witcher 3 using a graph data structure:
The Witcher 3 is a game that has a ridiculously large number of conversations all of which can take multiple paths. Piotr mentioned that modelling them to a graph scaled well for him.
Then it occurred to me that our questionnaire looked a lot like a conversation:
This was more a case of inspiration than discovering some novel game method. Graphs are a common enough data structure, and I'd expect to see them in all computer science books on data structures. What I haven't personally seen before is a computer science text suggesting using graphs to model conversations.
Data structures, and the algorithms that act on them, are abstract. Examples from other domains make them easier to visualize and use.
Using double buffering to avoid down-time
How do you publish a new version of a web application without having downtime? For some places I've worked at, unfortunately, the answer was that you don't. People would need to come into work in the middle of the night, shut down the application, swap in the new version, and start it up again. This isn't much fun for the person doing the deployment or for customers. Can we look to a game design pattern for a better solution?
One of the design patterns you'll encounter in games programming is double-buffering. When you're drawing a scene in a game, you typically start by clearing the whole screen. Then, you draw each object in the game world onto the screen one at a time. Computers can draw very fast, but not fast enough to avoid a flickering effect as the background is drawn over everything.
Double-buffering solves this problem by maintaining two virtual screens, the titular double buffers. There's the front screen the user can actually see. Meanwhile, the program can draw whatever it wants to the back screen without the user seeing any flickering. When the back screen is fully drawn, you swap the front and back screens without the end user ever seeing a half-drawn image.
Blue green deployments
If you replace screen with server, then you might see where I'm going with this. Double-buffering bares a striking resemblance to blue green deployments.
In blue green deployments, you have two instances of your web application running: one that users can see and interact with, and one that you can deploy to. The application still needs to be shut down, but it happens on the back server so your users don't see it which means you can deploy new features at any time of day. After the server that you're deploying is done starting up, you can even take the time to click through and make sure everything is working before swapping the real users to it.
Programming problems are not bound to their business domain. Design patterns from unrelated systems could still be useful to yours.
Tricks to do things Quickly
Software performance is a funny thing. It won't matter at all for the vast majority of your software engineering pursuits. Modern computers are so fast that most of your code will be fast enough without changing anything. However, if your code is taking long enough that people have to sit idle while waiting for it, performance suddenly becomes exceedingly important.
Now, how can a look at game engines help with this? To maintain the illusion of smooth motion, a game engine needs to update all of the objects in the game world between 30 and 60 times per second. This gives only 33 milliseconds to update everything, and if you don't meet those performance deadlines, the game becomes unplayable.
Use Caching to Save Time on Repetitive Delays
Once, a client had a legacy system they used to generate PDF reports. It was pretty slow. When they were pushing to get work done, they hit a button and the server spent the next half hour on generating a massive PDF file. This was a major bottleneck for the company, because everyone who wanted to generate PDFs got in each other's way. At crunch time, this means they were working late nights waiting for their reports to be ready. When I took a close look at the source code under a profiler, I found that almost all of this time was spent on a single line of code: A font file loaded from the hard drive every time text was drawn into the PDF.
Understanding why some programs are faster than others often involves an understanding of the hardware that those programs are running on. As Mike Acton from Insomniac Games will enthusiastically tell you, our software runs on real physical hardware. This helps us assess what it takes to make the code on that hardware run fast.
One of the most important hardware details to be aware of from a performance point of view is that computers have different forms of storage that run at different speeds. From fastest to slowest, you have:
The CPU's caches,
The hard drive, and
Going over a network connection to hit another server.
Next time you're waiting on a loading screen in a game, know that it's currently loading data from the hard drive into RAM so that once you're playing, the game will be fast.
To fix my performance issue, I loaded all of the resources that I needed for the PDF into memory before starting to generate the PDF. The font was fetched from the hard drive and put into RAM once, the same way that you would load the resources for a level in a game. Having the font ready, rather than going to the hard drive every time, took their half hour wait down to a few seconds.
If you need to access data rapidly, keep it close to the CPU
Make Your Computer Work Smart, Not Hard
When I first started my personal website, the cost of hosting was a concern for me. I didn't have any readers, I didn't have any content, and I didn't want to lock myself into something expensive that might not work out. When you're paying for hosting an application on the internet, you're really renting time on a computer that’s sitting in a data center somewhere. If your program needs a high-end computer, it's going to be expensive. If your program can run on a glorified potato, then hosting is cheap. It was in my financial best interest to ensure that my website didn't need much by way of computing power.
The original Crash Bandicoot games had a similar dilemma. Visually, they wanted to provide a busy vibrant world, but the PlayStation consoles at the time had limitations. One of the tricks that the team at Naughty Dog used was heavily preprocessing their levels: When the game was running, the engine knew which polygons it didn't need to draw. The camera that followed Crash around the levels was on a fixed path, so when they compiled a level, they could calculate what was visible from every point on the camera's path. If you ever wondered why those levels had so many gentle curves, hills, and leaves in the middle of the path, it was to obscure the path in the distance and limit the number of polygons on the screen. The time they spent waiting for their compile process to run was obscene, taking up to 12 hours to run, but the end result was that they could pack more detail into their levels than any other game of the time.
Back to my website, I use a static website generator called Jekyll. When my blog is deployed, it doesn't have any dynamic content. Whenever I need something dynamic, like an index page or the same navbar appearing on every page, it is processed when I compile the website on my computer. Since my web server only needs to allow static files to be downloaded, I can get away with paying for the cheapest hosting package I could find.
Think about what extra steps you could take during compilation to simplify your program when it's running.
Create Multimedia Experiences
I once worked on a project for an importer. They needed to photograph and catalog all of the products that they had available for import, so that they could take it to their customers. One of the challenges was that all of the photographs needed to be cropped to the same shape. One option would have been to force the end users to learn a program like Photoshop just to do the cropping, but we wanted to let them do their whole workflow without leaving our web application.
There are many games that run in web browsers. They're one of the easiest types of games to share, since I can drop a link into an article like this, and just by clicking the link you could be playing Cubxer. Back in the day, web browsers could only support this with extensions like Adobe Flash Player. In the last few years, this has been shifting to a standard HTML element called a 'canvas'. An HTML canvas is a box in a web page where you can take direct control over the pixels being drawn.
In our application, we ended up using the same set of graphical components. We just repurposed them to allow users to edit their photos without ever needing to leave our system. The typical flow of editing an image, from the application's point of view, then looked like this:
Scale the image to fill the canvas. Remember that the canvas and the image may be different shapes, so it may need to be padded.
Track what the user is doing with their mouse. If they click and drag on the canvas, draw a rectangle on top of your image following the mouse cursor.
When the mouse button is released, note the top left and bottom right pixel locations of the cropping rectangle the user just drew.
Redraw the image to the canvas, taking into account that you need to discard anything outside of the cropping rectangle.
If the user is happy with the change, save the cropping rectangle. You can recreate the cropped image at any point from the original image and the cropping rectangle.
It was a bit more work for us to develop this functionality, but it was worth it. Our users didn't need to install or use other applications to get their work done.
Sometimes it simplifies thing to just take direct control of the pixels being drawn.
Game Programming is Programming
The point that I'm driving towards is that game programming, while being focused on delivering a different type of product than most corporate systems, is still relevant to programming those corporate systems. The resources for learning about game programming will teach you relevant skills outside of games programming.
Game development applications like Game Maker can be an excellent way to introduce people to programming. I know that for me personally having a red ball that I could move around the screen was more interesting when I was learning programming than something that could only print "Hello World" in a terminal.
So next time you're looking for a solution to a problem, don't dismiss the possibility that games will hold the inspiration you need. A book on game engine architecture, or a conference talk on how a certain problem is solved in games, might hold exactly the solution you need. Here are some of the resources I’ve found most useful:
Game Engine Architecture by Jason Gregory: Good overall resource for how game engines work. Contains interesting insights into console game development.
Game Programming Patterns by Robert Nystrom: A solid programming design patterns book, with a focus on how the patterns are used in games.
Unreal Engine: If you sign up as a developer on Unreal Engine's site, they give you full access to the source code. It's a massive, intimidating codebase, full of interesting lessons.
Mozilla Developer Network - Game Development: Mozilla's hub for technologies for writing games on the web. This is also useful for any other multimedia applications on the web.
GDC's YouTube Channel: Game Developer Conference's recordings. Behind the Scenes of the Cinematic Dialogues in The Witcher 3: Wild Hunt and Diablo: A Classic Game Postmortem are good examples of interesting talks.
Game Maker: A tool for making games, aimed at people with minimal programming experience. Can be a good introduction to programming.
Red Blob Games: Articles explaining algorithms used in game development, with wonderfully interactive example.
Making Crash Bandicoot: The story of the making of Crash Bandicoot. The story is full of art, technology, and even a little Lisp.