The hardware manual was a programming
tutorial, rather than a ‘how to use other people’s software’ book
There's a vast gulf of technical knowledge
between programmers, who are immersed in the day-to-day agony of coding huge
million-line PC games, and tech-savvy gamers, who know how to install new PC
hardware, update their drivers and configure software. Back when I started
making games for fun, this gulf didn’t exist, due to machines such as the
Sinclair ZX81 homw computer.
Old
Sinclair ZX81 programming code might look simple, but today’s gamers are
basically doing the same thing – just more of it
By modern standards, the ZX81 is a joke.
Its 1KB of RAM wouldn’t even hold a single icon for a modern smartphone, and
its 8-bit processor wasn’t much faster than using an abacus. However, it was
great for encouraging people to become programmers, because it shipped with no
software other than the basic operating system. If you wanted to see the words
‘hello world’ on the screen, you had to write a (simple) program to display it.
The manual for the hardware was actually a programming tutorial, rather than a
‘hwo to use other people’s software’ book. Those were the days.
As a games coder, when you chat to keen
hardcore gamers who want to know more about how modern games are made, the most
common question that comes up is ‘how on earth do I start?’ as well as ‘where
is the actual “game” bit?’. The slightly jaded and cynical answer these days is
to use Unity, and plug some models and scripts into it. That’s great, and much
easier than ground-up coding, but I’m old-school enough to suggest that to
really understand how games work, and to make something really fast, efficient
and original, you need to learn programming from the ground up.
Back in my ZX81 days, it seemed so simple,
since programming looked like this:
10 print
“Custom PC rocks!”
20 Goto 10
That’s a simple infinite loop, and a
complete program that all makes sense. But how do you go from that to Far Cry
3? The first big problem that people encounter is the idea of ‘object-oriented
programming’, which is often C++ in PC gaming terms. Object-oriented
programming isn’t as weird as it sounds; conceptually, it’s similar to the way
languages such as Sinclair Basic used ‘GOTO’ and ‘GOSUB’. In other words,
instead of writing a long list of instructions for the PC, the code that gets
to run is scattered all over the place, and the current location in that code
is constantly bouncing back and forth using the modern equivalent of ‘GOTO’,
‘GOSUC’ and ‘RETURN’
Object-oriented code such as C++ just makes
it much more explicit. For example, there’s some code in my last game (Gratuitous
Tank Battles) that draws all the units’ health bars, and that code is
effectively a ‘subroutine’. Modern debuggers let you ‘step through’ code, which
means you can watch the CPU bounce up and down, and back and forth, from one
subroutine to another as the game runs. In C++, we don’t call them subroutines;
instead, we bunch them into groups called classes. The wording we use to jump
in and out of their code is a little different, but it’s fundamentally the
same. Unless the game is multithreaded, the CPU is going through the
instructions one step at a time.
Newcomers to programming often grasp that,
but they still stare at the thousands of files in a modern game and think ‘yes,
but where on earth does it all start and end?’ This is a good question.
A PC game is normally an EXE file of some
sort. It might use code handily placed in other files (normally DLLs), but it
all starts with a single EXE file, even if it’s just a little ‘stub’ program
that asks you to choose a screen resolution and then launches the real EXE
file. Windows knows there’s a special location within every EXE file where the
action starts, and it calls this bit of code (or ‘function’ in C++) WinMain. If
you look inside any PC game, you’ll find the code for WinMain somewhere. What
happens next is anyone’s guess, because approaches vary, but WinMain is
definitely in there.
WinMain is effectively that ’10 print
“Custom PC rocks!”’ code from earlier. It’s a sequence of instructions through
which the PC steps, and the game ends when it reaches the bottom. The basic
gist of WinMain code is to initialize the game (probably quite a lot of work),
show a splash screen (or graphics card adverts) and then jump into an infinite
‘loop’, which can only ever be left when the game’s quit button is pressed (or
you shortcut the whole lot by pressing ALT-F4). The actual ‘game’ code – the
part that draws aliens and lets you shoot lasers – is somewhere inside that
loop.
Gratuitous
tank battles in action – the code that draws all the units’ health bars is effectively
a ‘subroutine’
In Windows, this is slightly complicated by
the fact that a game also needs to tell Windows to interrupt it if certain
other Windows events happen. Many games rely on Windows to handle tasks such as
minimizing or maximizing the game window, Alt-Tab support and keyboard and
mouse input. All of this is handled by ‘message’, which is the basis of how
Windows works.
Essentially, you say to Windows ‘if you a
message about the mouse, and I’m running, tell me here!’, and a message is sent
to you whenever the mouse moves and so on. You check your ‘message queue’ every
frame in that loop. If you think that means your game is sent a lot of messages
every second, you’d be right. Around 95 per cent of the code isn’t handling
messages though – it’s in that infinite loop I mentioned. In very simple form,
it goes something like this:
Start loop
Check for player input
Process background tasks
(such as AI, physics)
‘Lock’ the screen
(technically the backbuffer)
Draw to the screen
“Unlock’ and ‘Flip’ to show
the newly drawn frame to the player
Let Windows have control
back for a millisecond or two
End Loop
Of course, it’s ten billion times more
complex than that, but conceptually, that’s what’s happening, and that
represents a single frame of game time. Ideally, we have what we call
‘headroom’ in there, and we can pad out that millisecond at the end so that we
don’t run the game too fast.
Some games, such as Gratuitous Space
Battles, try to run as fast as they can, and scale their background tasks (bullet
movement and so on) accordingly. Others, such as Gratuitous Tank Battles,
assume a fixed amount of time has passed and skip entire frames of drawing if
they need to catch up with real-time instead. Some games make it horrendously
more complex by spinning off separate threads to handle physics, AI and even
input.
The latest versions of DirectX mean that
even rendering can be multithreaded too, but if you’re even considering that
approach, you’re probably already a senior coder with more than ten years of
experience.
In my last game, that loop is called ‘Game:
:GameProc () ’ and it has 80 lines of code. Of those 80 lines, 41 contain the
code that works out if Alt-Tab has been hit, and tries to recover from it. As
with all game code, you need to jump through around ten other hoops to get to
the actual game. In my case, there’s a special ‘object’ that represents the
current ‘mode’ of the game, such as the main menu, options screen or an actual
battle. If it’s the latter, the specific ‘battle’ objects code is called
effectively the equivalent of ‘GOTO battleobject’. A few more bits of bouncing
around gets us to code such as ‘SIM Battle: : Process () ’, where you find the
real meat and potatoes code such as ‘SIM_GetAirstrickes () -> Process () ’.
Game code is far more involved than most
people think. Imagine a million lines of algebra connected to each other by
tens of thousands of links, where a typo in just one of them will cause a
crash. That’s basically C++, but ultimately, behind the scary terms and complex
relationships, it’s just a lot of over the place. My apologies to any hardened
programmers reading this – I know I’ve simplified to oblivion.