📄 tutorial.txt
字号:
Introduction
************
In this chapter I'll first give a little more information about
this tutorial, then I'll make some points about modular
programming and explain the makefile we'll be using to build
the game.
Information about the tutorial
==============================
This tutorial is based around the source code. The text files
form a sort of commentary about the source code, and the code
itself is heavily commented too. In another game programming
tutorial, Allegro Vivace, I wrote a great deal of text with
hardly any code, at first. This time I wrote the code first
(up to chapter 7) and then the accompanying text.
Consequently, you should find that the code can just about stand
by itself. The text files are meant to point out to you what's
changed since the previous chapter, and then you can go and look
at the source code to see for yourself what the difference is.
If you want to ignore the text files and just read the code, by
all means do so. That's how I originally wrote the tutorial,
remember.
The tutorial is aimed at two main classes of people:
* Complete newcomers to game programming.
If you fall into this category, I hope you find the
tutorial is not hard to follow. I predict that some
people may have difficulty understanding the modularity of
the program; that's why I've explained it in some detail
below and throughout the tutorial. If you really can't
understand it, please do let me know so that I can explain
it more clearly. In particular, tell me which areas are
most difficuly to understand.
* People who have written a few games but are interested in
my suggestions on how to develop a game.
For these people I hope the tutorial doesn't seem too
basic. It should point out what problems need solving,
how I solve them, what solutions I consider but reject,
and why. If you fall into this category, then please let
me know if it's too mundane; I don't want it to appeal
only to newcomers.
Veteran programmers might also find something of interest here,
but they may be tempted to skim over most things. That's OK
too, of course -- you can use this tutorial in any way you
please.
Building the programs
=====================
You can build the program in any individual chapter by running
`make' within that chapter's directory. If you modify the
code, this is the most efficient way to apply your changes.
Note that if you modify any `#include' directives or add new
source files to the project you should run `make deps' before
`make', to update the dependency information. If you add new
source files to the project you'll need to add their filenames
to the `SRC' list in the makefile; it should be fairly obvious
where to put them.
Initially you'll probably want to build all of the examples in
one go, to save you the bother of doing it later. In this case,
run `make' from the tutorial's root directory. This will
invoke `make' on each chapter in turn. You can also run `make
deps' from the tutorial's root directory, to update all of the
dependency information, but I've already done that so you won't
need to initially.
You can also delete all created files by running `make clean',
if you have GNU fileutils, from either a chapter's directory or
the tutorial's root directory.
See the sections at the end of this file for more details of the
makefiles.
Reasons for programming in modules
==================================
I normally program in a modular fashion. This means splitting
the program into a large number of small modules, each with a
specific task, and minimising the intercommunication between
modules.
I find it useful to do this for a number of reasons; the code
is easier to write in sections (you can write one module at a
time), it is easy to add new features to existing modules, it
is easy to add new modules to the program, and it is easier to
locate bugs (since the modules are relatively small, and it's
normally pretty obvious which module is at fault).
No doubt I've missed out some advantages. The point is that
the advantages over having all your source code in one file
(ugh), or having a set of files which intercommunicate in a
spaghetti-like fashion, are many, while the cost (effort needed
to set up the structure) is small.
Of course, there are other programming styles with these
advantages, most notably object oriented programming. In my
opinion, OOP has its place, but for relatively simple games I
don't find that its advantages over simple modular programming
repay the effort needed in structuring the program. For larger
projects, especially with more than one programmer, it would be
more worthwhile. Regardless of whether or not I like OOP, I
don't much like C++. Live and let live, though -- everything I
write here should be easy to translate into C++, especially
because it's already modular, which is a big step towards OOP.
Perhaps someone will volunteer to do this.
Module guidelines
=================
Each module will generally have an `init' function and a
`shutdown' function, which will be called by a higher module.
The exception here is the `main' module, which calls its own
`init' and `shutdown' routines since it has no higher module.
Any functions and variables which are internal to a module will
be marked as `static', so that they don't clutter the
namespace. If a variable is not static then it should be
exported in a header file somewhere.
Each module will generally have one header file per module that
calls it. These headers will declare functions and data types
(e.g. `struct' definitions); in general they won't declare
variables, because I don't think global variables should be
used in this way, generally.
That said, I am not diametrically opposed to global variables
-- they have their uses. These uses, though, normally involve
them being either global to the whole project, or shared
between several modules which don't necessarily call each
other. In either case, they should be in a separate header
file from the module's main header file.
Modules should `#include' any header files that refer to
symbols defined in the module; this allows the compiler to
check that the definitions match the declarations.
Header file guidelines
======================
A header file is a file which will be included verbatim in
other source files. Because of this, several issues crop up:
* Anything defined by the header file will be linked into the
final executable file several times, so you should not
define anything in a header file.
* If header files are nested, a single header file might be
effectively included more than once in the same source
file; the compiler probably won't like this for several
reasons, so you should protect against this multiple
inclusion.
Note the distinction between "define" and "declare" -- a
definition of a variable allocates space for that variable,
perhaps initialised to a value, while a declaration just tells
the compiler that the variable exists, and what its type is. In
general, to make a declaration from a definition you just remove
any inital value assignment and add the keyword `extern' before
the type of the variable.
A standard way to prevent a header file from being included
more than once in one source file is to enclose it in the
following way:
#ifndef foo
#define foo
/* contents of header file */
#endif
The first time the preprocessor is told to include the file,
the macro `foo' is not defined and so it processes the rest of
the file -- defining `foo' and passing the rest (after
preprocessing it) to the compiler. Next time it is told to
include the file, the macro `foo' is already defined, so the
`#ifndef' fails and the rest of the file is ignored.
Of course, you have to make sure the preprocessor macros (e.g.
`foo') do not clash between header files, or with elements of
the code itself. Make them distinctive -- it doesn't really
matter what you use since you only use each one twice, on
consecutive lines.
The makefiles
=============
A lot of people are frightened of writing makefiles, it seems.
It's really not that hard. I'm not going to copy out the Make
manual here; you can read that yourself if you want to (`info
make'). I'm going to explain, though, how the makefile we're
using works.
At the simplest level, you just have to write rules like this:
pong.exe: main.o game.o
gcc -o pong.exe main.o game.o -lalleg
main.o: main.c game.h
gcc -o main.o -c main.c
game.o: game.c game.h
gcc -o game.o -c game.c
Then you notice that Make already knows how to do some things,
and that all you really need to write is:
pong.exe: main.o game.o
gcc -o pong.exe main.o game.o -lalleg
main.o: main.c game.h
game.o: game.c game.h
(since Make already knows how to create a .o file from a .c
file). You can control exactly how it does the compilation by
defining some variables:
CC = gcc
CFLAGS = -O2 -Wall -Werror -g
TARGET_ARCH = -m486
These affect how the internal rules behave.
Now it gets tedious to keep the dependencies in the makefile up
to date; every time we change a `#include' we need to update
the rules. However, gcc can do this for us. If you pass `-M'
(or `-MM') to gcc then instead of doing a full compilation, it
will just output a set of Make rules. `-M' lists all included
files as dependencies, including standard headers; `-MM' just
lists the nonstandard headers, included with `#include "foo.h"'
rather than `#include <foo.h>'.
So what we do in our makefile is use Make's `include' directive
to get it to include gcc's output in the makefile. This is
simpler than it sounds. By writing:
-include deps.mak
we cause it to include the file `deps.mak' if it exists, but
not complain if it does not. Then we write another rule, for a
phony target `deps':
deps:
gcc -MM $(SOURCES) > deps.mak
Now we can type `make deps' when we change the inclusion
structure of the program, and Make will tell gcc to update the
`deps.mak' file. Then next time we run Make it will be using
the new dependency information.
This is basically all our makefile does. It also supplies
another phony target, `clean', which deletes all of the object
files and the executable. It requires GNU fileutils, though
(`v2gnu/fil361b.zip').
The top level makefile
======================
You'll also have found a makefile in the top directory. This
makefile just chains to the ones in the lower directories; I
won't explain it in detail. It's just there so that you can
build all of the programs at once, if you want to.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -