First real post in a while, how about that eh? Well, as you may or may not know I made a Game of Life clone this morning (find it in my programming portfolio,) and I thought I’d explain what the code does, as kind of a tutorial. Or something. Anyway, here goes:
The first few lines should be easy to understand:
#define W 1260
#define H 792
#include
#include
SDL_Rect* SDLRect(int x, int y, int w, int h);
I define the width and height of the window, because if you use defines instead of typing the actual value, it’s easier to change later if you’d feel like it. This is something you shouldn’t do with most varables but because I didn’t know how much I needed I wanted to be able to change it easily. After that I include stdlib.h (for atexit()) and SDL.h, which is the graphics library header. Well, one of them. That’s irrelevant. I then define a function I often use t save time, because constructing SDL_Rects takes a few lines of code. Onwards:
int main(int argc, char **argv)
{
bool field[W/8+2][H/8+2];
bool field2[W/8+2][H/8+2];
bool paused = true;
int x,y;
for(int i = 0; i <= W/8+2; i++){
for(int j = 0; j <= H/8+2; j++){
field[i][j] = false;
field2[i][j] = false;
}
}
We enter the main function and I define most of the required variables – and initialize the matrices using a for loop.
// Initiera SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0){
fprintf(stderr, "Uguu~, SDL_Init failade: %s\n", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);
SDL_Surface* pWindow = SDL_SetVideoMode(W, H, 16, SDL_SWSURFACE);
SDL_WM_SetCaption("Game of Life - Use [SPACE] to (un)pause, [C] to clear and mouse to (in)activate cells.", "Game of Life");
This is where SDL is initialized. SDL_Init takes one argument, and you pass flags to it. Flags can be combined using the | operator, and if you want both video and sound you call SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO). We only want video here though. SDL_Init() returns a negative value if something goes wrong, so testing the return value is recommended. Here we write to stderr, which is usually a file, if something goes wrong. The next step is to hook SDL_Quit to atexit(). This makes sure that SDL_Quit is run if the program exits using exit(). We then create a video surface, sized W*H, using 16 colors and the software memory for rendering. We also set the caption of the program.
SDL_Event evtSdlEvent;
SDL_Rect* prRect;
bool bolKeepRunning = true;
while (bolKeepRunning){
We define a couple more variables and enter the main loop, which runs until the bool value is false.
// Logic
if(!paused){
for(int i = 1; i < W/8+2; i++){
for(int j = 1; j < H/8+2; j++){
int c = 0;
// Count all stuffs
for(int k = -1; k < 2; k++)
for(int l = -1; l < 2; l++)
if(field[i+k][j+l] == true && !(k == 0 && k == l))
c++;
// 1. Any live cell with fewer than two live neighbours dies, as if by loneliness.
if(field[i][j] && c < 2)
field2[i][j] = false;
// 2. Any live cell with more than three live neighbours dies, as if by overcrowding.
else if(field[i][j] && c > 3)
field2[i][j] = false;
// 3. Any live cell with two or three live neighbours lives, unchanged, to the next generation.
else if(field[i][j] && (c == 2 || c == 3))
field2[i][j] = true;
// 4. Any dead cell with exactly three live neighbours comes to life.
else if(!field[i][j] && c == 3)
field2[i][j] = true;
else
field2[i][j] = false;
}
}
This is the actual game code. This controls the squares (if the game isn’t paused) and applies the rules. Notice that we write the new values to a second matrix. This is so that the values we change don’t affect the evaluation of other squares.
for(int i = 0; i <= W/8+2; i++)
for(int j = 0; j <= H/8+2; j++)
field[i][j] = field2[i][j];
}
We copy the values of the second array back into the first one so that we can draw them and evaluate them again later.
// Drawing
prRect = SDLRect(0,0,W,H);
SDL_FillRect(pWindow, prRect, SDL_MapRGB(pWindow->format, 0, 0, 0)); // Empty screen
delete prRect;
for(int i = 1; i < W/8+2; i++)
for(int j = 1; j < H/8+2; j++)
if(field[i][j]){
prRect = SDLRect((i-1)*8,(j-1)*8,8,8);
SDL_FillRect(pWindow, prRect, SDL_MapRGB(pWindow->format, (rand()%192)+64, 0, 0)); // Draw square
delete prRect;
}
if(paused){
SDL_GetMouseState(&x, &y);
prRect = SDLRect(((int)(x/8))*8,((int)(y/8))*8,8,8);
SDL_FillRect(pWindow, prRect, SDL_MapRGB(pWindow->format, 0, (rand()%192)+64, 0));
delete prRect;
}
// Flip surface.
SDL_Flip(pWindow);
We draw the squares. First we loop through each of them, creating a new SDL_Rect everytime. we then pass it to SDL_FillRect(), which fills the specified rectangle on the screen surface with a random red color. We then delete the SDL_Rect to save memory.
If the game is paused we also draw a green square to act as a pointer, useful when you place the squares. At last we flip the buffer to update the screen.
// Key polling
while(SDL_PollEvent(&evtSdlEvent)){
if(evtSdlEvent.type == SDL_QUIT || (evtSdlEvent.type == SDL_KEYDOWN && evtSdlEvent.key.keysym.sym == SDLK_ESCAPE))
bolKeepRunning = false;
if(evtSdlEvent.type == SDL_MOUSEBUTTONDOWN)
field[(int)(evtSdlEvent.button.x/8)+1][(int)(evtSdlEvent.button.y/8)+1] = !field[(int)(evtSdlEvent.button.x/8)+1][(int)(evtSdlEvent.button.y/8)+1];
if(evtSdlEvent.type == SDL_KEYDOWN && evtSdlEvent.key.keysym.sym == SDLK_SPACE)
paused = !paused;
if(evtSdlEvent.type == SDL_KEYDOWN && evtSdlEvent.key.keysym.sym == SDLK_c)
for(int i = 0; i <= W/8+2; i++){
for(int j = 0; j <= H/8+2; j++){
field[i][j] = false;
field2[i][j] = false;
}
}
}
These are the controls. Checking controls is done by looking for “events” with the SDL_PollEvent function. It populates the specified event structure so we can check if there has been an event, and what it was.
// Delay
if(paused)
SDL_Delay(10);
else
SDL_Delay(50);
}
We delay to make the game a bit slower (so you can actually see what happens.) The delay is a little shorter when paused, to make the game more responsive while placing squares.
// Frigör
SDL_FreeSurface(pWindow);
SDL_Quit();
return 0;
}
We are now outside of the loop again. This is where we end up when the user presses escape, or in any other way closes the application. We free the video surface and call SDL_Quit().
SDL_Rect* SDLRect(int x, int y, int w, int h){
SDL_Rect* r = new SDL_Rect;
r->x = x;
r->y = y;
r->w = w;
r->h = h;
return r;
}
Here’s the SDL_Rect-creating function. Very simple really, just dynamically create a SDL_Rect object, set the variables and return the pointer.
Well, that’s all. Feel free to post questions if you have them. Feel free to post corrections too, I never said I do everything right ;).