Game Programming – getting started (transcript)

If you’re going to follow this series, the expectation is that you have at least a basic understanding of how to program. Specifically, you should have a basic familiarity with Python and the concepts of object-oriented programming. As long as you’re comfortable with basic uses of variables, functions, classes, if statements, while statements, etc. you should be fine.

It will also help to have some understanding of how text and numbers are represented as bits, in particular the accuracy issues with floating-point numbers.

It’s also good to have an understanding of the context in which a program runs, namely the operating system and hardware: what’s the difference between a process and a thread, what’s a file, what’s a system call, what’s the relationship between memory and the CPU caches? Things of that nature.

You’ll also want some basic familiarity with using a command line. Nothing advanced, just navigation between directories, starting programs, and possibly modifying the system PATH.

As for math, we may cover a bit of linear algebra, which mainly concerns matrices, but the only expectation coming in is that you at some point in your life covered basic geometry and trigonometry. If you can’t remember which is sine and which is cosine, don’t worry: I’ll probably remind you when it comes up.

And finally, if you want to follow along with the code and play with it yourself, the code will all be made available on bitbucket, which uses the Mercurial versioning system, so you’ll want to familiarize yourself with Mercurial’s basic usage.

Now if you’re totally new to programming, you can work your way through the videos at codeschool.org, but that’s quite a lot of material. So if you just want to do the bare minimum, you should watch the videos called, ‘ a first programming language’, ‘the javascript language’, and ‘the python language’, in that order. (The javascript video is necessary not because we’re going to use javascript but because the python video won’t make sense without it.)

And of course, if you need help with any material in this series or the codeschool series, you can post questions in the codeschool discussion group (the link to which is on the codeschool front page).

 

As you may know, C++ is generally considered the language of choice in most professional game development. Virtually every commercial PC and console game of the last decade and earlier has been written mostly or wholly in C++, and this language preference will probably continue into the indefinite future. However, well into the 90’s, many PC games and almost all console games were written in assembly. (Assembly, remember, refers to any language in which the programmer specifies the precise CPU instructions one-by-one.) The use of assembly was of course mainly for efficiency reasons. Games are among the most taxing programs around, so in order to maximize the current hardware, it makes sense to use the language with the least performance overhead. In theory, the precise control of assembly provides sufficiently smart programmers the ability to produce the most eff icient code possible. By about the mid-90’s, though, consumer hardware, PC’s especially, became fast enough that the tiny efficiency gains programmers can eek out when using assembly instead of languages like C and C++, those efficiencies began to matter less and less in the overall performance picture. Past a certain point of code size and complexity, small efficiencies stop making much of a visible difference and so just aren’t worth the programmer time and effort. Besides, when code is slow on today’s hardware, the first culprit is the choice of algorithms, not the minute details of the exact instructions. So better to use a more convenient language like C and C++ that makes it easier for programmers to experiment and find the right algorithms. Today, we leave minute optimizations of the precise CPU instructions up to compilers.

But now, why the choice of C++ rather than some other non-assembly language? Well first, C++ still allows the programmer a high-degree of control over precisely what goes on in memory: the programmer in C++ explicitly manages memory and can create data types that use minimal space. Also, the built-in numeric types–int, short, long, double, and so forth (‘primitive types’, as they are called in some other languages)–these types can generally be processed efficiently. For example, the int type is defined as an integer which is the same size as the CPU’s general purpose registers, the optimum size for basic computations like addition and subtraction.  And lastly, because C++ is statically typed and gets compiled to machine code, the compiler can aggressively optimize the compiled code in a way that cannot be done for dynamic and interpreted code (and in fact, this aggressive optimization is often the slowest part of the compilation process).

In Python, in contrast to C++, the programmer does not explicitly manage memory. Instead, the interpreter is responsible for garbage collecting any created objects; for the most part, the Python programmer creates objects without worrying about deallocating the memory which the objects occupy. On the down side, this lack of control means the Python programmer cannot specify the precise, byte-by-byte memory usage of data types.

Python’s numeric types also favor convenience over efficiency: a Python integer, for example, can be of arbitrary size, meaning that, say, two Python integers cannot generally be added to each other in a single CPU instruction the way that two ints in C++ can be added together with just a single CPU instruction.

And lastly, in a dynamic and interpreted language, a lot of information about what will happen at runtime only gets discovered at runtime, and it’s this lack of fore-knowledge that makes aggressive compiler optimizations not possible with Python code the way they are with C++ code.

So, the cost of Python’s conveniences are overhead, both in space (meaning extra memory usage) and time (meaning extra CPU work). If you’re trying to push current hardware to its limits, C++ is the clearly preferable choice.

However, today’s PC hardware is sufficiently powerful that less-taxing games should run perfectly well in Python, especially games with just 2D graphics. To give you an idea of what’s possible, here’s a game called Save the Day, programmed not in Python but Javascript, which has all the same basic efficiency disadvantages compared to C++.  This game actually runs in the web browser, but it uses OpenGL for its graphics rendering like we will be doing in Python. Here’s another Javascript game in the browser, this time 3D. Though certainly not up to snuff with the latest and greatest here in 2012, this is just a proof of concept experiment; more work and better art assets seem like they might  produce results comparable in graphical fidelity to a shooter of the early to mid-2000’s.

If after this series, you’re interested in learning more about game programming in C++, I recommend starting with this book, Game Engine Architecture by Jason Gregory. Gregory covers many of the same fundamentals we’ll cover here, but he also covers many topics applicable to C++ but not Python, such as manual memory management.

 

Now if you want follow along with the code and try coding yourself, you’ll need to install a few programs. First, of course, you’ll need Python, and then two Python libraries: a framework for games called pyglet and the Python imaging LIbrary, which allows us to work with image files of many formats.

Though I cover Python 3 in my Python video, we’re actually going to use the older Python 2 for better compatibility with the pyglet framework. The apparent differences between Python 2 and 3 are small enough that this shouldn’t be a problem, and I’ll point out any differences along the way.

Specifically, I’ll be using Python 2.7.3, pyglet 1.2alpha1, and Python Imaging Library 1.1.7. If later versions are available when you’re listening to this, you’ll probably be fine with those later versions, but I can’t make any guarantees. Just be sure to use  a version of Python 2, not Python 3, as pyglet currently does not work with Python 3.

I also recommend installing Aptana and Notepad++. Aptana is a repackaging of the Eclipse IDE that includes the PyDev plugin. Pydev gives us some conveniences, like an easy-to-use python debugger. You could alternatively use Eclipse and install the pydev plugin yourself, but downloading aptana is easier. Both Aptana and Eclipse require Java, so you’ll need Java installed either way.

Notepad++ is my text editor of choice on Windows. If you don’t want to use a full IDE like Aptana, Notepad++ is a good choice for writing your Python code. Even if you use an IDE, Notepad++ is very convenient for viewing and editing text files.

 

Let’s talk a little about what the pyglet framework will do for us. First off, the term framework is used for libraries that act as incomplete programs into which we insert our own code. For example, most web development these days is done with web frameworks, wherein the skeleton, the framework, of a web app is provided along with many web-related conveniences, and we write our web app by filling in the pieces specific for our app. That said, pyglet is a very minimal framework, so it’s not obscuring too much of the underlying system from us, which is why I prefer it to another Python game framework called pygame. Pyglet spares us some bother without obscuring how game code really works. Moreover, pyglet handles many cross platform differences such that our code will run on Windows, Mac, and Linux.

So the features of pyglet include, first off, creation of our game windows, with an option for fullscreen mode.

Pyglet also handles the messages, the so-called events, which the operating system sends to our window. These include, mainly, the input events sent when the user clicks the mouse on the window, or hits a keyboard key while the window is focused. Pyglet also can capture game controller events, so our games can support joysticks and gamepads. We’ll talk more about window events in later videos.

To draw in our window, we use the opengl library, which as we’ll discuss in detail later, is a standard, cross-platform interface to the graphics hardware. The opengl library is provided as a C code library, but pyglet wraps the C code such that we can invoke it in Python. This wrapper isn’t 100% complete, but it’s close, and more than adequate for our purposes.  Though using opengl from Python instead of C or C++ naturally introduces some overhead, it’s very much the same opengl you would use in C or C++, so don’t worry that we’re covering a dumbed-down version of   directly, pyglet provides a simple abstraction layer for drawing what are called sprites. Sprites are simply 2d images positioned and rotated on the screen. Many 2d games can be rendered simply by composing the screen out of a bunch of sprites, some rendered on top of others. In a side scroller, for example, the background might be one or more sprites and the characters are sprites drawn on top. Be clear, though, that pyglet’s sprite abstraction itself uses opengl, so in fact you can easily mix these sprites with direct use of opengl.

Pyglet also provides for simple play back of sound and video.

Finally, pyglet provides the basic framework required in any game, what’s called the gameloop. As we’ll discuss in the next video, writing a game loop is easy; the hard part is handling the timing, and that’s where pyglet helps us out.

Pyglet is organized into a number of Python modules:

pyglet.app and pyglet.clock both concern the gameloop and timing just mentioned

pyglet.window creates our windows

pyglet.gl is the opengl wrapper, while pyglet.grpahics and pyglet.sprite provide a simple layer of abstraction as just discussed.

pyglet.text provides easy methods for drawing text, and pyglet.image provides for the loading and managing of image files

pyglet.media contains the sound and video facilities

pyglet.font is an older module for loading fonts and rendering text; it’ss mostly superceded by the newer pyglet.text

pyglet.resource provides a more convenient way to load resource files, namely sound and image files

and finally pyglet.input provides the ability to get game controller input

Note that a few of the modules share overlapping features. In some cases this overlap is for the purpose of providing both a low- and high-level api, such as pyglet.gl for direct opengl and pyglet.sprite for a convenient, simplified api. In other cases, though, the overlap is the result of changes to pyglet over the course of its development. Where there’s such overlap, I’ll try to steer us towards the most up-to-date features.

 

To use any module of pyglet, you could import it by its full name like any other module, but in the name of convenience, pyglet does some import trickery such that you only ever need import pyglet. All of the other pyglet modules get assigned to attributes of the pyglet package module, e.g. pyglet.app can be accessed as pyglet.app after having imported only pyglet, not pyglet.app itself.

I could go into a long explanation of how exactly pyglet does this, but it would require a long refresher on how exactly Python imports modules.

 

Before ending here, let me give you a quick sample of some pyglet programs. Here’s a program written with pyglet that displays balls bouncing around the window. By itself, the program is unimpressive, but for a program that displays text in a window, takes keyboard input, and animates moving objects, it’s impressive that the whole thing is written in just 60 lines. Similarly, here’s an asteroids clone written with Pyglet that, while merely competent as a simple game, the whole program, complete with proper menus, is written in less than 800 lines. We should have no trouble getting similar bang-per-line-of-code when we write our own clones of classic games, such as a tetris clone in less than 500 lines.

Comments are closed.