4 Attachment(s)
The mystery of the appearing enemy fighter
Hello to all, recently I have been working on a fairly complex game similar to the widely popular 1980's game "Space Invaders". Of course, I will deviate from this path and make it into a game that is clearly my own. Due to lack of creativity, and for the simplicity of the implementation, the movements and some graphics will be somewhat similar for the time being. Enough of my pointless babbling though, I need to get to what my question was. I had the code worked out, and everything was great. Then, when I tried to load in 30 enemy images for use later in the game, I noticed that, mysteriously, an enemy fighter had appeared without me ever blitting it onto the screen. I think that this problem also has to do with the mysterious "empty space" mystery also. It seems to go black occasionally, even though all my background images are never completely black. Originally I thought it was some kind of illegal memory access in my code, but it would've crashed with signal SIGSEGV if it was. Which leads me to believe it's my implementation somewhere in my code. I have no clue as to how the enemy fighter started appearing every time the "empty space" mystery occurs. I was hoping you would help me figure out what I'm doing wrong.
Here is the full source code, I commented out the enemy fighter parts for obvious reasons.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <process.h>
#include <SDL/SDL.h>
#define MAX_SPACE_IMAGES 3
#define SCREEN_WIDTH 920
#define SCREEN_HEIGHT 518
#define FRAMES_PER_SECOND 70
#define SPACE_UPDATE_FRAME 20
#define MAX_LASER_QUEUE 1
#define MAX_ENEMIES 15
#define TRUE 1
#define FALSE 0
typedef struct enemy_fighter
{
SDL_Surface * image;
int x;
int y;
unsigned is_dead:1;
unsigned move_right:1;
unsigned move_left:1;
unsigned move_down:1;
} enemy_fighter;
typedef struct laser
{
SDL_Rect shape;
int node_id;
int speed;
struct laser * next;
} laser;
void SDL_errorexit( const char * error_message )
{
char buffer[BUFSIZ];
sprintf(buffer, "%s : %s", error_message, SDL_GetError());
fprintf(stderr, "%s", buffer);
exit(1);
}
void init_SDL( )
{
if ( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
SDL_errorexit("SDL_Init");
return;
}
laser * create_list_node( )
{
laser * temp = NULL;
if ( !(temp = malloc(sizeof(*temp))) )
{
perror("Malloc");
exit(1);
}
return temp;
}
laser * append_list( laser * lasers, int id, int speed, int x, int y, int w, int h )
{
laser * temp = NULL;
if ( !lasers )
{
lasers = create_list_node( );
lasers->node_id = id;
lasers->speed = speed;
lasers->shape.h = h;
lasers->shape.x = x;
lasers->shape.y = y;
lasers->shape.w = w;
lasers->next = NULL;
} else
{
for ( temp = lasers; temp->next; temp = temp->next );
temp->next = create_list_node();
temp->next->node_id = id;
temp->next->speed = speed;
temp->next->shape.h = h;
temp->next->shape.x = x;
temp->next->shape.y = y;
temp->next->shape.w = w;
temp->next->next = NULL;
}
return lasers;
}
laser * remove_first_node( laser * lasers )
{
laser * temp = lasers;
lasers = lasers->next;
free(temp);
return lasers;
}
void release_list( laser * lasers )
{
laser * temp = NULL;
while ( lasers )
{
temp = lasers;
lasers = lasers->next;
free(temp);
}
return;
}
SDL_Surface * load_image_opt( char * image_path, int is_transparent )
{
SDL_Surface * image = NULL;
SDL_Surface * temp_image = SDL_LoadBMP(image_path);
if ( !temp_image )
SDL_errorexit("SDL_LoadBMP");
if ( is_transparent )
SDL_SetColorKey( temp_image, SDL_SRCCOLORKEY, SDL_MapRGB(temp_image->format, 255, 255, 255) );
image = SDL_DisplayFormat( temp_image );
SDL_FreeSurface( temp_image );
return image;
}
SDL_Surface * set_video_mode( const char * window_title )
{
SDL_Surface * console = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 24, SDL_SWSURFACE | SDL_DOUBLEBUF );
SDL_WM_SetCaption( window_title, "N/A" );
if (!console)
SDL_errorexit("SDL_SetVideoMode");
return console;
}
void game_intro( SDL_Surface ** space, SDL_Surface * title, SDL_Surface * console )
{
SDL_Rect title_rect;
int offset = 0;
int iteration;
title_rect.x = (console->w - title->w) / 2;
title_rect.y = (console->h - title->h) / 2;
title_rect.h = title->h;
title_rect.w = title->w;
for ( iteration = 0; iteration < 9; iteration++ )
{
offset = (offset > MAX_SPACE_IMAGES - 1 ? 0 : offset + 1);
SDL_FillRect(console, 0, SDL_MapRGB(console->format, 0, 0, 0)); /* Clear the screen */
SDL_BlitSurface(space[offset], 0, console, NULL );
SDL_BlitSurface(title, 0, console, &title_rect);
SDL_Flip(console); /* Update the screen */
SDL_Delay(250); /* Delay the loop */
}
return;
}
void handle_events( int * quit, int * laser_request, int * x )
{
SDL_Event event;
/* While-loop was from SDL documentation : http://www.libsdl.org/release/SDL-1.2.15/docs/html/guideinputkeyboard.html */
while( SDL_PollEvent( &event ) )
{
switch( event.type )
{
case SDL_QUIT :
*quit = 1;
break;
/* Keydown event */
case SDL_KEYDOWN :
switch( event.key.keysym.sym )
{
case SDLK_LEFT :
*x = -5;
break;
case SDLK_RIGHT :
*x = 5;
break;
case SDLK_SPACE :
*laser_request = 1;
break;
case SDLK_ESCAPE :
*quit = 1;
break;
default : break;
}
break;
/* End keydown event */
/* Keyup event */
case SDL_KEYUP:
switch( event.key.keysym.sym )
{
case SDLK_LEFT :
if( *x < 0 )
*x = 0;
break;
case SDLK_RIGHT :
if( *x > 0 )
*x = 0;
break;
case SDLK_SPACE :
if ( *laser_request > 0 )
*laser_request = 0;
break;
default : break;
}
/* End keyup event */
break;
default : break;
}
}
return;
}
inline void move_fighter( SDL_Surface * console, SDL_Rect * fighter, int * x )
{
if ( fighter->x >= console->w - 75 )
{
*x = 0;
fighter->x = console->w - 76;
}
fighter->x += *x;
return;
}
inline void update_space_array_offset( int * offset, int * frame )
{
if ( *frame == SPACE_UPDATE_FRAME )
*offset = (*offset > MAX_SPACE_IMAGES - 1 ? 0 : *offset + 1);
return;
}
inline void reset_frame( int * frame )
{
*frame = *frame == SPACE_UPDATE_FRAME ? 0 : *frame + 1;
return;
}
inline void wait( unsigned time_start, unsigned time_end )
{
if ( (time_end - time_start) < (1000 / FRAMES_PER_SECOND) )
SDL_Delay( (( 1000 / FRAMES_PER_SECOND ) - ( time_end - time_start )) );
return;
}
void game_start( SDL_Surface ** space, SDL_Surface * space_fighter, SDL_Surface * console )
{
unsigned time_start = 0;
unsigned time_end = 0;
SDL_Rect fighter;
int frame = 0;
int offset = 0;
int quit = 0;
int x = 0;
int laser_request = 0;
int laser_queue = 0;
int amount_of_lasers = 0;
fighter.x = (console->w - space_fighter->w ) / 2; /* Set the fighter in the middle of the game */
fighter.y = console->h - 100; /* Set it 100 pixels minus the screen size */
fighter.h = 75;
fighter.w = 75;
laser * lasers = NULL;
laser * temp = NULL;
srand(time(NULL)); /* Randomize our node identifiers, may be needed in the future */
while ( !quit )
{
time_start = SDL_GetTicks(); /* Start the timer here to cap our frame rate later. */
update_space_array_offset( &offset, &frame ); /* Update the background image array if needed */
handle_events( &quit, &laser_request, &x ); /* Handle all the events and store the results in the parameters */
move_fighter( console, &fighter, &x ); /* Handle moving the fighter according to the result of the event handler */
/* Limit the lasers to a preset constant, can be any number,
/ but obviously super high amounts will do nothing. */
if ( amount_of_lasers < MAX_LASER_QUEUE )
laser_queue += laser_request;
for ( ; laser_queue; --laser_queue, ++amount_of_lasers )
{
lasers = append_list( lasers, rand(), 1, ((fighter.x + fighter.w / 2) - 2), fighter.y, 2, 16 ); /* Create a linked list to store all the valid requests */
spawnl(P_NOWAIT, "sound.exe", "sound.exe", "laser.txt", NULL); /* This was simpler than trying to multithread to play it at the same time. */
}
if ( lasers )
{
for ( temp = lasers; temp; temp = temp->next )
{
if ( temp->shape.y > 0 )
{
temp->shape.y -= 20;
}
else
{
lasers = remove_first_node(lasers); /* Needs to be updated to have the ability to remove nodes in the middle of the list */
--amount_of_lasers; /* Decrease this, so it always represents the exact amount of lasers in existence */
}
}
}
/* Drawing starts here */
SDL_FillRect(console, 0, SDL_MapRGB(console->format, 0, 0, 0)); /* Clear the screen */
SDL_BlitSurface(space[offset], 0, console, NULL );
SDL_BlitSurface(space_fighter, 0, console, &fighter);
for ( temp = lasers; temp; temp = temp->next )
SDL_FillRect(console, &temp->shape, SDL_MapRGB(console->format, 0, 255, 0)); /* Update each laser */
SDL_Flip(console); /* Update the screen */
/* End drawing */
time_end = SDL_GetTicks();
wait( time_start, time_end ); /* Wait until our FPS limit has been reached if executing faster than the limit */
reset_frame( &frame );
}
if ( lasers ) /* There shouldn't be any lasers left, but we need to make sure */
release_list(lasers);
return;
}
int main( int argc, char * argv[] )
{
/* Variable declarations */
/*
enemy_fighter enemies[MAX_ENEMIES] = {{NULL}};
*/
SDL_Surface * space[MAX_SPACE_IMAGES] = {NULL};
SDL_Surface * title = NULL;
SDL_Surface * space_fighter = NULL;
SDL_Surface * console = NULL;
int offset;
/* Initializing SDL... */
init_SDL();
if (atexit(SDL_Quit) )
{
perror("atexit");
exit(1);
}
/* Screen configurations */
console = set_video_mode( "Space Fighter" );
/* Image loading */
/*
for ( offset = 0; offset < MAX_ENEMIES; offset++ )
{
enemies[offset].image = load_image_opt( "enemy_fighter.bmp", 1 );
enemies[offset].is_dead = FALSE;
enemies[offset].move_down = FALSE;
enemies[offset].move_left = TRUE;
enemies[offset].move_right = FALSE;
enemies[offset].x = 0;
enemies[offset].y = 0;
}
*/
space[0] = load_image_opt( "space_dark.bmp", 0 );
space[1] = load_image_opt( "space_lighter.bmp", 0 );
space[2] = load_image_opt( "space_light.bmp", 0 );
title = load_image_opt( "space_fighter_intro.bmp", 1 );
space_fighter = load_image_opt( "space_fighter.bmp", 1 );
/* Start the game introduction */
game_intro( space, title, console );
SDL_FreeSurface(title); /* We no longer need the introduction title */
/* Start the game */
game_start( space, space_fighter, console );
/* Free allocated memory */
for (offset = 0; offset < MAX_SPACE_IMAGES - 1; offset++ )
SDL_FreeSurface(space[offset]);
/*
for ( offset = 0; offset < MAX_ENEMIES; offset++ )
SDL_FreeSurface(enemies[offset].image);
*/
SDL_FreeSurface(space_fighter);
return 0;
}
Here's exhibit A, the game screen after the title screen with no "empty space" :
Attachment 12926
Here's exhibit B, the game screen after the title screen with the mysterious enemy_fighter in the top left corner.
Attachment 12923
Here are the background images, and as you can see, none of them should be completely black.
This is space_dark in .png format :
Attachment 12924
This is space_lighter in .png format :
Attachment 12925
This is space_light in .png format :
Attachment 12925
Any help is appreciated.