Collision Problem


mac-10

Still Fresh
Joined
Oct 27, 2006
Messages
56
Age
44
Website
Visit site
Hi

I am having a bit of a problem will my collision. I am putting a simple space shooter together to help me get into the SDL. You’ll have to forgive for my C code this is the first C I have ever produced and I haven't got my 2X yet have it a Xmas gift so I have to wait.

The problem is the collision code is not running the way I expected it. Basically the bullets are hitting the sprites the enemies it seems really imprecise. I can’t tell if it the way I am looping through the sprites or the collide func.

here is the code:


Code:
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "SDL/SDL.h"
#define PHYSICSFPS 90

int gLastTick;
int shipSpeed = 2;
int shipX = 20, shipY = 120;
int shipMaxFrame = 3;
int Max_Main_Bullets = 10;
int Max_Small_Bullets_1 = 10;
int Max_Small_Bullets_2 = 10;
int MaxEmenies = 10;
int Max_Back_Grounds = 7;
int  exsFrame = 0;
int  gKeyLeft = 0;
int  gKeyRight = 0;
int  gKeyUp = 0;
int  gKeyDown = 0;
int  i = 0;
int  j = 0;

SDL_Surface *screen, *backgroundimage, *shipimage ,*bulletimage, *flameimage, *enemyimage;

typedef struct EnemySprite
{
int ObjNo, X, Y, Width, Height, Active, Frame;
}EnemySprite;

EnemySprite enemy[20];

typedef struct BulletSprite
{
int ObjNo, X, Y, Width, Height, Active;
}BulletSprite;

BulletSprite bullet[10];

typedef struct bgElements
{
int Frame, X, Y, Active;
}bgElements;

bgElements background[7];


// Load in an image and convert it to the display format
// for faster blitting.
SDL_Surface * ImageLoad(char *file)
{
  SDL_Surface *temp1, *temp2;
  temp1 = SDL_LoadBMP(file);
  temp2 = SDL_DisplayFormat(temp1);
  SDL_FreeSurface(temp1);
  return temp2;
}
void addBullet(void){
int flag = 1;
	for(i=1;i < Max_Main_Bullets; i++)
	{
	if(flag &&  bullet[i].Active == 0){ 
			bullet[i].Active = 1; 
			flag = 0;
			bullet[i].X = shipX + 45;
			bullet[i].Y = shipY + 15;			  
		  }
	   
	}
}

void DrawIMG(SDL_Surface *img, int x, int y)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_BlitSurface(img, NULL, screen, &dest);
}

void DrawIMG(SDL_Surface *img, int x, int y, int w, int h, int x2, int y2)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_Rect dest2;
  dest2.x = x2;
  dest2.y = y2;
  dest2.w = w;
  dest2.h = h;
  SDL_BlitSurface(img, &dest2, screen, &dest);
}

void animateflame(){
	 exsFrame += 1;
	if(exsFrame > 3) exsFrame = 0;
	 DrawIMG(flameimage,shipX - 8,shipY,22,26,21*exsFrame,0);   
}
/* Find out if we hint anything */


short int Sprite_Collide( int sprite1X , int sprite1Width , int sprite1Y , int sprite1Height, int sprite2X , int sprite2Width , int sprite2Y , int sprite2Height  )
{
double left1, left2;
double right1, right2;
double top1, top2;
double bottom1, bottom2;

left1 = sprite1X+sprite1Width*0.1;
left2 = sprite2X+sprite2Width*0.1;
top1 = sprite1Y+sprite1Height*0.1;
top2 = sprite2Y+sprite2Height*0.1;
right1 = sprite1X+sprite1Width*0.9;
right2 = sprite2X+sprite2Width*0.9;
bottom1 = sprite1Y+sprite1Height*0.9;
bottom2 = sprite2Y+sprite2Height*0.9;

if (bottom1 < top2) return 0;
if (top1 > bottom2) return 0;
if (right1 < left2) return 0;
if (left1 > right2) return 0;

return 1;

}

/* start things */
void init(){
	for(i=1;i < Max_Main_Bullets; i++)
	{
		bullet[i].X = 0;
		bullet[i].Y = 0;
		bullet[i].Active = 0;
		bullet[i].ObjNo = i;
		bullet[i].Width = 9; 
		bullet[i].Height = 9;
	}
	for(i=1;i < Max_Back_Grounds; i++)
	{
		background[i].X = rand()%500+52;
		background[i].Y = rand()%160;
		background[i].Active = 1;
		background[i].Frame = rand()%4;
	}
	
		for(i=1;i < MaxEmenies; i++)
	{
		enemy[i].X = rand()%500+400;
		enemy[i].Y = rand()%160;
		enemy[i].Active = 1;
		enemy[i].ObjNo = i;
		enemy[i].Frame = rand()%4;
		bullet[i].Width = 60; 
		bullet[i].Height = 60;
	}
	
shipimage = ImageLoad("data/ship/ship.bmp");
flameimage = ImageLoad("data/ship/exs.bmp");
bulletimage = ImageLoad("data/bullet/bullet.bmp");
backgroundimage = ImageLoad("data/bg/background.bmp"); 
enemyimage = ImageLoad("data/enemy.bmp");

  
SDL_SetColorKey(shipimage, SDL_SRCCOLORKEY,SDL_MapRGB(shipimage->format, 0, 255, 0));
SDL_SetColorKey(flameimage, SDL_SRCCOLORKEY,SDL_MapRGB(flameimage->format, 0, 255, 0));
SDL_SetColorKey(bulletimage, SDL_SRCCOLORKEY,SDL_MapRGB(bulletimage->format, 0, 255, 0));
SDL_SetColorKey(enemyimage, SDL_SRCCOLORKEY,SDL_MapRGB(enemyimage->format, 0, 0, 0));
SDL_SetColorKey(backgroundimage, SDL_SRCCOLORKEY,SDL_MapRGB(backgroundimage->format, 0, 0, 0));

gLastTick = SDL_GetTicks();
}
   
void bullethitenemies(){
for(i=1;i < Max_Main_Bullets; i++){
if(bullet[i].Active){
	for(j=1;j < MaxEmenies; j++){
			 if( enemy[j].Active ){
			   if(Sprite_Collide( bullet[i].X , bullet[i].Width , bullet[i].Y , bullet[i].Height, enemy[j].X , enemy[j].Width , enemy[j].Y , enemy[j].Height  )){
			  bullet[i].Active=0;
			  enemy[j].Active=0;  
			   }// if
			  }// if	 
	}// for 
}// if 
}// for 
}

void render(){

while(SDL_GetTicks() - gLastTick <= 1000/PHYSICSFPS);

bullethitenemies();
   
if (gKeyLeft) shipX -= shipSpeed;
if (gKeyRight) shipX += shipSpeed;
if (gKeyUp) shipY -= shipSpeed;
if (gKeyDown) shipY += shipSpeed;

if(shipX > 280) shipX = 280;
if(shipX < 1) shipX = 1;
if(shipY > 215) shipY = 215;
if(shipY < 1) shipY = 1;
	SDL_FillRect(screen,0,0);

	for(i=1;i < Max_Back_Grounds; i++)
	{
		 if(background[i].Active){
		 background[i].X = background[i].X - 1;
		 DrawIMG(backgroundimage, background[i].X ,background[i].Y,52,88,52*background[i].Frame,0);
		 if(background[i].X < -52){	
		 background[i].X=320+rand()%10; 
		 background[i].Frame = rand()%5; 
		 background[i].Y = rand()%160;
		 } 
		 } 
	}

	for(i=1;i < Max_Main_Bullets; i++)
	{
		if(bullet[i].Active){
		   bullet[i].X = bullet[i].X + 1;
		   DrawIMG(bulletimage, bullet[i].X ,bullet[i].Y,9,9,9*3,0);
		   if(bullet[i].X > 300)	bullet[i].Active=0;			 
		   }
	}

	for(i=1;i < MaxEmenies; i++)
	{
		 enemy[i].X = enemy[i].X - 1;
		 if(enemy[i].Active){
		 DrawIMG(enemyimage, enemy[i].X ,enemy[i].Y,34,33,34*enemy[i].Frame,0);
		 }
		 if(enemy[i].X < -52){	
		 enemy[i].X=320+rand()%10; 
		 enemy[i].Y = rand()%160;
		 enemy[i].Active = 1;
		 } 
	}
	
	 animateflame();
	 DrawIMG(shipimage,shipX,shipY,43,29,42*2,0);
	 SDL_Flip(screen);
	 gLastTick = SDL_GetTicks();
}

int main (int argc, char *argv[])
{
	char *msg;
	int done;
  srand(time(NULL));
	Uint8* keys;



	/* Initialize SDL */
	if (SDL_Init (SDL_INIT_VIDEO) < 0)
	{
		sprintf (msg, "Couldn't initialize SDL: %s\n", SDL_GetError ());
		free (msg);
		exit (1);
	}
	atexit (SDL_Quit);

	screen = SDL_SetVideoMode (320, 240, 16, SDL_SWSURFACE | SDL_DOUBLEBUF);
	if (screen == NULL)
	{
		sprintf (msg, "Couldn't set 320x240x16 video mode: %s\n",SDL_GetError ());
		free (msg);
		exit (2);
	}
	SDL_WM_SetCaption ("GET Lose", NULL);

/* Initialize Data */
init(); 

	done = 0;
	while (!done)
	{



   SDL_Event event;
	while (SDL_PollEvent(&event)) 
	{
	  switch (event.type) 
	  {
	  case SDL_KEYDOWN:
		switch (event.key.keysym.sym)
		{
		case SDLK_LEFT:
		  gKeyLeft = 1;
		  break;
		case SDLK_RIGHT:
		  gKeyRight = 1;
		  break;
		case SDLK_UP:
		  gKeyUp = 1;
		  break;
		case SDLK_DOWN:
		  gKeyDown = 1;
		  break;
		case SDLK_SPACE:
		 addBullet();
		  break;
		  
		}
		break;

	  case SDL_KEYUP:		  
		switch (event.key.keysym.sym)
		{
		case SDLK_ESCAPE:
		  return 0;
		case SDLK_LEFT:
		  gKeyLeft = 0;
		  break;
		case SDLK_RIGHT:
		  gKeyRight = 0;
		  break;
		case SDLK_UP:
		  gKeyUp = 0;
		  break;
		case SDLK_DOWN:
		  gKeyDown = 0;
		  break;
		}
		break;
			case SDL_QUIT:
				done = 1;
				break;
			default:
				break;
			}
		}


	   render(); 
	}

	return 0;
}


Thanks

Mac :)
 
When you are initialising the enemy you are setting the bullet width and height to what you want the enemy width and height to be.

EDIT : Array addressing starts a zero - your loops all seem to start a 1. You may be writing past the end of the arrays which could cause other issues.
 
Also, this is more for organization than anything, but why not change

int shipMaxFrame = 3;
int Max_Main_Bullets = 10;
int Max_Small_Bullets_1 = 10;
int Max_Small_Bullets_2 = 10;
int MaxEmenies = 10;
int Max_Back_Grounds = 7;

to

#define shipMaxFrame 3
#define Max_Main_Bullets 10
#define Max_Small_Bullets_1 10
#define Max_Small_Bullets_2 10
#define MaxEmenies 10
#define Max_Back_Grounds 7

- Alex
 
Some ideas that struck me:
In sprite_collide you are multiplying the width of the sprite by a factor, that means that for the enemies (width 60) you are talking 6 units but for the bullets, less than one. This may not actually be an issue (probably it isn't) but it is something to think about. I would also be inclined to use floats instead of doubles and explicitly cast the integers to float. Actually I would even just use integers for everything (esp. as the GP2x doesn't have an FPU).

If your sprites aren't rectangular then the collisions may seem inaccurate when actually they aren't. All your calculations are specific to rectangular sprites that are at most 10% smaller than the bitmap size.

The way you're looping through the sprites looks fine, as long as you don't interleave the movement with the collision detection.

I think it would be a good idea to add some printf (or other debugging statements) that output the arguments to sprite_collide and let you know actual numerical values for what is and is not considered a collision. That way you can see if the problem is your perception of collision on the screen not matching the code or if it is the code that is wrong.

Also can you describe in more detail how they are inaccurate. Are they not hitting when they should, are they hitting when they shouldn't, are they hitting from across the screen? Are all of the collisions inaccurate of just the occasional one (Mr. Jabberwocky's point about the arrays may cover this case) All of the above?

Charlie

---------------------------
Ok so there's something strange going on with the forum, please consider this a separate message!
---------------------------

Alex. posted on Dec 12 2006 at 10:04 AM said:
Also, this is more for organization than anything, but why not change
(snip)
int Max_Back_Grounds = 7;
to
(snip)
#define Max_Back_Grounds 7
Also any other number you write into the source should either be #defines or values derived from the arguments. It is not considered great style to hard code any values e.g.
Code:
	for(i=1;i < Max_Back_Grounds; i++)
	{
		/* What does this 500+52 mean? will you remember in 6 months */
		background[i].X = rand()%500+52;
		background[i].Y = rand()%160;
		background[i].Active = 1;
		background[i].Frame = rand()%4;
	}

	/* Better to have (with names that actually mean what they say
		not what I'm guessing they mean! :) */
	for(i=1;i < Max_Back_Grounds; i++)
	{
		background[i].X = rand()% BG_X_RAND_MAX + BG_X_OFFSET;
		background[i].Y = rand()% BG_Y_RAND_MAX;
		background[i].Active = 1; /* I think 1 and 0 are pretty self explanatory though */
		background[i].Frame = rand()% BG_MAX_FRAMES;
	}

Of course these are just C style points so feel free to ignore completely.
NB I also don't have a GP2X yet for the same reason, that doesn't stop me running my mouth though :)

Charlie
 
Last edited by a moderator:
mac-10 posted on Dec 12 2006 at 08:19 AM said:
/* start things */
void init(){
for(i=1;i < Max_Main_Bullets; i++)
{
bullet.X = 0;
bullet.Y = 0;
bullet.Active = 0;
bullet.ObjNo = i;
bullet.Width = 9;
bullet.Height = 9;
}
for(i=1;i < Max_Back_Grounds; i++)
{
background.X = rand()%500+52;
background.Y = rand()%160;
background.Active = 1;
background.Frame = rand()%4;
}

for(i=1;i < MaxEmenies; i++)
{
enemy.X = rand()%500+400;
enemy.Y = rand()%160;
enemy.Active = 1;
enemy.ObjNo = i;
enemy.Frame = rand()%4;
bullet.Width = 60;
bullet.Height = 60;
}


Just to clarify my previous post. No doubt you copy/pasted the 'bullet' initialising code to use for 'enemy' but missed changing 'bullet' to 'enemy' for the width and height. I think we have all been there many times. As it stands your enemy is not being defined properly and your missiles are 60 x 60. I don't think there is any need for outputting the values at this stage. It is plain why you are getting unexpected results.

Personally I would have reduced the collision areas by adding and subtracting integer values rather than multiplying by fractions.

Best of luck with your progress.
 
Last edited by a moderator:
To check for collision with two rectangular object you can add the width of one to the width of the other and then do the same with the height. The new bigger object has collided with the now point if the point is on or in the new rectangle.
 
Code:
for(i=1;i < MaxEmenies; i++)
{
enemy[i].X = rand()%500+400;
enemy[i].Y = rand()%160;
enemy[i].Active = 1;
enemy[i].ObjNo = i;
enemy[i].Frame = rand()%4;
bullet[i].Width = 60; 
bullet[i].Height = 60;
}
I am so sorry I did notice that at all (the wood for the trees syndrome).
I am surprised it event found one collision. Thanks for all your help
I am really grateful but at the same time a little embarrassed :D
 
mac-10 posted on Dec 12 2006 at 11:25 AM said:
I am so sorry I did notice that at all (the wood for the trees syndrome).
I am surprised it event found one collision. Thanks for all your help
I am really grateful but at the same time a little embarrassed :D

We've all done it and most of us have posted to public forums about it at some point as well.

C
 
Last edited by a moderator:
Back
Top