Intro
A lot
of people have emailed me asking how I make games. So
I've decided to start this page to help people get
underway. Hopefully this page will help you make some
games, if it does please email me and tell me
about your game.
BTW
this page is going to cover making DOS games only =)
Learning a Programming
Language
The
first thing you need to do is learn a programming
language. I strongly suggest learning C. For one
thing C is a great language for programming games, a
lot of languages are very closely related to C, and
many helpful tools are written in C or some form of
it.
One
good way to learn C is to read some books. There are
many books out there but I'm going to have to
recommend C for Dummies. This book is
great, everything is explained excellently in the
books. There are two volumes to get of this book.
Some areas of C are not covered to closely in the
book however you can easily pick up the material
later, either through trial and error (like me =) or
find some more information some other place.
After
reading these books you should feel very comfortable
working in C.
Picking a Compiler
The
next thing you need to do is pick a compiler to make
your game on. Several things will effect your choice,
however it's probably just a question of money.
Different compilers might have different function
names, memory handling is a little different etc.
Anyway the compiler I use is Watcom 10.6. However if
you don't have enough money or don't want to buy it,
there are free 32 bit compilers available. 32 bit
meaning you're not going to run out of memory.
Programming
Concepts
Even
if you read all those C books, they're not going to
teach you programming concepts. Top Down Design is
the most important one. It's basically breaking
problems into smaller parts to make advanced problems
more simple and computer code easier to follow.
I
hope by now you're not writing programs with your
whole program in main(). I used to do this when I
first started and trust me, it's a big mistake!! It
is a very good idea to break your program into many
functions.
Another
good idea is to put all related stuff into different
C FILES. For example all your scrolling functions
should go in Scrolling.C & Scrolling.H. Don't have
everything in one big source code file.
Don't
ever be afraid to restart, throw out all your old
source code. Every time you do something you learn
and the next time you will probably do a better job.
There
is always a better way!
Using
Libraries
When
first starting out I really don't recommend using
libraries. You should learn to code without libraries
first and then use them when needed. A good time to
use a library is when there is no standard. A good
example is with sound. There are so many sound cards
out there making drivers for everyone or just some of
them is going to take way too much of your time. This
is a good time to use a library. Using libraries for
Initialization is just fine but using them to do
things during your game is a bad idea. There is a
video standard and it's called VESA. I really think
you use your own code anytime you draw to the screen.
The more libraries you use the harder your code will
be to follow.
Without
Libraries:
DrawImage();
ShowScreen();
KeyPress();
With
Libraries:
JoeSKicKinGfXDrAwIt(NULL,11,3,PUTINFORFUN);
SallyIsMeWifESaYSBrINGoUTSTAKE(NuLL,*EAT,HELLO);
Input78NPreSSOK(STARKEYS);
Now
why would you want that in your code? Although that
code isn't as bad as Windows
programming =). If you're going to use a
library make sure you get the source with it, and at
least look at the code and try to understand it.
Re-Writing
Code is a good idea. For example, usually sound libs
don't have a very simple interface for playing
sounds. I like to have it really nice and simple so I
usually combine functions until I get something like:
InstallSound();
PlayMusic("mysong.mid");
StopMusic();
PlaySound("mysound.snd");
RemoveSound();
Doing
this kind of thing makes your code a lot easier to
read.
Game Design
Before
you start coding your game you should definately
think about what you want your game to be like.
Setting limitations is something you're going to have
to decide before you start. Will this game have 256
colors or 16 million? Choose what video mode to use.
Take into account that you're choices are going to
effect your game a lot. It's not a good idea to code
your game for every possible situation, it can slow
down your game to do this. This is not to say, give
the player no choices, just set good limits for your
game. Like how many enemies on the screen at once
etc.
Plan
your game engine before you start.
Tools
& Editors
It's
now time to decide what tools or editors are going to
make the development of your game faster. Will you
code them? Or use something someone's already made?
The choice is up to you, but if you use someone
else's editor it will lack customization to your
game. Something that most games will need is a
map-editor. Making this will definitely speed up the
development of your game. Other tools should also be
made as well.
Game
Engine
This
stage is the longest one, it's basically just coding
your game. It's important to use code you've made
before in your game. Maybe your Font engine, or your
gfx code etc, use what you can to speed up making
your game.
Bugs
& Errors & #$@#$! Argh!?
No
one likes it when things go wrong but they do. Just
relax take a break (or take none) and look at your
code. Asking someone else to look at your code is a
good idea, it's sometimes hard to see your stupid but
simple how could I have been so
stupid mistakes. There is really
only two different kinds of Errors. The first is, I know what
I'm doing and this shouldn't be happening. This is
probably just a dumb mistake you've made, but often
very hard to spot in your code what you're doing
wrong. The other one is, I'm not to
sure what's going on I hope it works. There's
really nothing you can do except ask someone about
your problem or do some trial &
error.
Some
things to watch for are:
Going
past arrays. int n[50]; n[60]=1;
Copying past the limit. char *n = (char
*)malloc(100); memcpy(n,buffer,200);
These
are the most common things that can go wrong. Don't
be afraid to step through your code line by line like
the computer does.
Sample Code Main()
Here
is some sample code for a main() function in a game.
This code is taken from MarioPC Version 3. Can you
see how using a library makes the code harder to
understand? (XLiB)
void
main()
{
long size=0,zx; unsigned long
start_time=0,end_time=0,frames=0;
float fps=0; int z;
Mario.counter=0;
LoadLevel("test.lev");
LoadBack("cool.lev");
LoadMTiles(); LoadBTiles();
InitMario();
for(zx=0;zx<50;zx++)
{
size=x_sizeof_cbitmap(88,mtiles[zx].data);
tiles[zx]=farmalloc(size);
x_compile_bitmap(72,mtiles[zx].data,tiles[zx]);
}
for(zx=0;zx<8;zx++)
{
size=x_sizeof_cbitmap(88,mariopic[zx]);
mario[zx]=farmalloc(size);
x_compile_bitmap(72,mariopic[zx],mario[zx]);
}
for(zx=0;zx<40;zx++)
{
size=x_sizeof_cbitmap(88,back[zx].data);
cback[zx]=farmalloc(size);
x_compile_bitmap(72,back[zx].data,cback[zx]);
}
x_text_mode(); /* make sure VGA is in color mode, if
possible */
x_set_mode(13,256+32);
x_put_pal_raw(mariopal,256,0);
x_set_doublebuffer(240);
x_rect_fill(0,0,288,272,HiddenPageOffs,26);
DrawScreenBack();
DrawScreenWorld();
x_page_flip(0,0);
while(StartAddressFlag);
x_page_flip(16,0);
while(StartAddressFlag);
wx=0;
InstallKeyboardDriver();
start_time=clock();
while(!checkKey(KEY_ESC))
{
UpdateJoystick();
DoMario();
x_rect_fill(0,0,288,272,HiddenPageOffs,26);
DrawScreenBack();
DrawScreenWorld();
DrawMario();
x_page_flip(16,0);
while(StartAddressFlag);
frames++;
if(Mario.y>224) break;
}
end_time=clock();
x_text_mode();
RemoveKeyboardDriver();
fps= (frames * CLK_TCK) / (end_time-start_time) ;
printf("Frames per second=%f",fps);
}
Basically I just wanted to slow you that your main()
should be this:
void main()
{
Initialization();
GameLoop();
ShutDown();
}
Video Mode
No
matter what video mode you choose for your game you
still are going to have to set the video mode and
then set it back when your done in your program.
SetVideoMode(mymode);
DoGame();
SetVideoMode(textmode);
Setting
the video mode is part of your Initializing stuff. Here
is a function to set the video mode in Watcom 10.6:
extern void SetMode(int);
#pragma aux SetMode ="int
10h"\
parm
[eax];
To
set the video mode to 320x200x256: SetMode(0x13);
To set the video mode to text-mode: SetMode(0x03);
These are basic VGA modes and every video card out
there supports them.
Memory
This
is a really important topic. You've got to feel
comfortable dealing with memory to program games,
especially gfx. Here is how to allocate some memory
in C:
char *mem;
mem =
(char *)malloc(10); // This allocates 10 bytes for
mem
Now
mem is just like:
char
mem[10];
To
free the memory:
free(mem);
// Simple eh?
Memcpy & Memset
These
are very useful functions I use them all the time.
Here's how they work.
#include <string.h>
memcpy( DESTINATION , SOURCE, LENGTH );
memset( DESTINATION , VALUE2SET, LENGTH );
Lets
create to memory buffers, set one to one value, and
then copy that buffer to the other one.
char
*buffer1, *buffer2;
1)
buffer1 = (char *)malloc(10); // Create the first
buffer
2) buffer2 = (char *)malloc(10); // Create the second
one
3) memset(buffer1,5,10); // Set all the values in
buffer1 to 5
4) memcpy(buffer2,buffer1,5); // Copy 5 bytes of
buffer 1 into buffer2
What's
going on?
When
we allocate memory the values are going to be random.
So buffer1 could contain 2,233,4,40,5,2,3,4,5,100. So
lets say after doing line #1 buffer1 contains:
X,X,X,X,X,X,X,X,X,X
// Random Values
1)
Allocating 10 bytes of memory for buffer1. ( b1 =
X,X,X,X,X,X,X,X,X,X )
2) Allocating 10 bytes of memory for buffer2. ( b2 =
X,X,X,X,X,X,X,X,X,X )
3) Set 10 bytes of buffer1 to 5. ( b1 =
5,5,5,5,5,5,5,5,5,5 )
4) Copy 5 bytes of buffer1 into buffer2. ( b2 =
5,5,5,5,5,X,X,X,X,X )
Alternatively
we could have done this: (Without pointers)
char
buffer1[10],buffer2[10];
1)
Don't need this anymore
2) Don't need this anymore
3) memset(buffer1,5,10); // Set all the values in
buffer1 to 5
4) memcpy(buffer2,buffer1,5); // Copy 5 bytes of
buffer 1 into buffer2
I
hope you feel comfortable working with memory now.
The following should make sense to you:
char
name [] = "Noname" ;
char myname [] = "Nathan";
memcpy(name,myname,6);
printf("The name is now: %s",name);
This
prints "Nathan" on the screen. See how we
copied myname into name?
Accessing the Video Screen Part One
When
we set the video mode to 320x200, you can easily make
a pointer to the screen. A pointer meaning a memory
location. Then the video screen acts as 64000 bytes
(320x200) of unsigned chars. (Unsigned Char is a
value from 0 - 256). That's where we get 256 colors
from.
Making
a pointer to the screen in 320x200x256: (Watcom Code)
char
*VGA = (char *)0xA0000; // Make that pointer.
The
256 values that we can set on the video screen are
all different colors. It is called the palette. We
can set the palette. We'll learn more about this
later. The standard for palettes is that Color 0 is
black, and Color 255 is white. This is how you set
the screen to white:
1)
char *VGA = (char *)0xA0000; // Make that pointer.
2) memset(VGA,255, 320*200 ); // Set the screen to
white.
Remember
we must set the VGA pointer to equal the video screen
before we access it! Or your program will crash!
Because you are setting some random memory location
to 255 for 64000 bytes. That's going to overwrite
something important in memory.
What's
going on?
1)
Setting up the VGA pointer to be equal to the start
of the video screens memory.
2) Set the video screen to the value 255 (which is
white by default) and set 64000 bytes to this value.
64000 = 320 * 200. Which is the size of the video
screen in the mode we're working in. I could have
wrote 64000 instead of 320*200, which would be faster
but I wanted to show you 320*200, so you wouldn't be
wondering where 64000 came from.
How
the Your Monitor Works & Shearing Effect
How
come setting the Video Cards memory to 255 makes the
screen all white? How does the computer know I've
changed what's on the screen? Well it doesn't. What
happens is you computer constantly copys the
VideoCards memory to the actual screen. Taking a
short break in between each copy. This is called the
Vertical Retrace. Meaning Redrawing the screen. Have
you ever ran a program where the screen seems to
split slightly or get invisible lines when it
scrolls? That's what happens when you copy to video
memory when the Computer is trying to redraw it to
the monitor at the same time. Half the screen is the
new one, and half is the old one.
What
can we do about this? All you have to do is wait for
the screen to finish drawing before you draw to the
screen. You make a simple function that does this.
Here is the code for it.
void WaitRetrace(void)
{
while(inp(0x3DA)
& 0x08 );
while(!(inp(0x3DA)
& 0x08 ));
}
Don't worry about how it works, just remember it does
work and to call this function before you draw to the
screen it you want to avoid shearing. By the way,
waiting for the retrace slows down your game to the
monitors refresh rate. This is ok however, because it
allows you to achieve exactly 60 frames per second in
your game. Even if someone has a really fast computer
the game will still run at the same speed.
Here
is code to set the screen to white but avoid
shearing:
1)
char *VGA = (char *)0xA0000; // Make that pointer.
2) WaitRetrace();
3) memset(VGA,255, 320*200 ); // Set the screen to
white.
Notice
the code is basically the same except we wait for the
retrace to avoid shearing. We really only need to
Wait for the Retrace when we are constantly updating
the screen, which you are probably going to be doing
in your game.
More to Come, Stay Tuned....