You are using a version without Ads of this website. Please, consider donating:

### Need help with space shooter in SDL!

Pages: 123
Yeah thanks, i guess...
But i don't really understand why it's not working.
x is the enemy position isn't it? ENEMY_WIDTH is the enemy width and BULLET_WIDTH is the bullet width so i'd think that if the bullet touches the enemy shipalive is set to false so it disappears?

I don't get why it would set shipalive to false automatically, it should only do that when it touches a bullet, the bullet hasn't even spawned yet? There's not really any good tutorials on this i can find, so i'm not really sure how i can fix this. Maybe i should just work on the collision later and completely fix the bullets first (making a list or something so that i can have multiple bullets at once).
 ``1234567891011121314`` ``````void Enemy::move() { x += xVel; y += yVel; if( shipalive == false ) return; if( ( x < 0 ) || ( x + ENEMY_WIDTH > BULLET_WIDTH ) ) { shipalive = false; } if( ( y < 0 ) || ( y + ENEMY_HEIGHT > BULLET_HEIGHT ) ) { shipalive = false; } }``````

x, y is the enemy position. What variables represent the position of the bullet?

I'm sorry you didn't understand my explanation for why shipalive = false is assigned every time the function is called. It's because this:
x + ENEMY_WIDTH > BULLET_WIDTH is always true.

I tried to help by giving a function for collision testing, but you said it doesn't work (without showing what you tried), so that's a dead end.

EDIT: If you would like to try again I can offer another collision function to you. Let me know about that.
A collision function will have to involve a Bullet object. It will be of the form `Enemy::hit( Bullet& );` or
`Bullet::hit( Enemy& );` depending on which class you wish to define it in.
Last edited on
Thanks,
Is what you're saying i need to do something like x+enemy_width > x+bullet_width? I'm really sorry for not giving you a proper explanation about the code you gave me for the target, i typed it all up and forgot to press submit and shutdown my computer and i had changed the code back by then so i wasn't bothered to do it again. I'll try again to see what the errors were and i'll edit this post. Assuming that my first sentence is wrong how would i do something like Enemy::hit( Bullet& );? I don't really know anything about collision because there was nothing on lazyfoo's tutorials about two classes 'colliding' with each other.
I think you should read up on C++ more. Make a SDL_Rect member of the 2 classes. Like so...

 ``12345678`` ``````class Bullet { public: SDL_Rect getBox() {return Box;} private: SDL_Rect Box; };``````

Add this code to both classes^

Now you can proceed.
 ``1234`` ``````if(checkcollision(Bullet.getBox(), Enemy.getBox()) { // Add your collision code here. }``````
Fredbills approach would work fine. The SDL_Rect Box member may be easy to initialize using the image data. This beats using the global variables ENEMY_WIDTH, BULLET_WIDTH and so on. I was assuming that the values for ENEMY_WIDTH etc. are equal to the actual image dimensions. I was just going with what you already had, trying to change as little as possible to get it working.

But Fredbill is just dropping short clues, not detailed code.
A global function such as he is suggesting may be a great idea too. You'll have to choose your method.

Going with the previous approach:
 ``123456789`` ``````Enemy::hit( Bullet& b ) { if( ((b.x+BULLET_WIDTH) > x) && (b.x < (x+ENEMY_WIDTH)) )// horizontally alligned if( ((b.y+BULLET_HEIGHT) < y) && (b.y > (y+ENEMY_HEIGHT)) )// vertically alligned { shipalive = false; b.alive = false; } }``````

The enemy position is x, y and the bullet position is b.x, b.y.
The code above will cause the enemy to disappear after the 1st hit. The bullet should vanish when the enemy is hit as well.
Add `Enemy::hit( Bullet& b );` to your class definition as well.
Call the function as `myEnemy.hit(myBullet);` after the move() calls and before the draw() calls in main().

This ought to work. If you have trouble, post all of your code again so we can see exactly what's going on.
Last edited on
^ better yet, make the bullet a collision box also. Then you don't have to deal with nasty `#define yourshiz ` 's. ;)
Thanks guys,
fun2code the problem with your Enemy::hit( Bullet& b ) is that it says 'declaration is incompatible with "void Enemy::hit(<error-type> &b)" (declared at line 34) and overloaded member function not found in 'Enemy'. Maybe i'll have to go with fredbill's approach i'll try it out now...

EDIT: fredbill where should i put the if(checkcollision)? and what class should i initialize (int) it in enemy or bullet or in main?
Last edited on
Put that in the update function. Make it a global function or make a class for it.
 Put that in the update function.
Um... where would that be?
In your game loop. It should be like this:

 ``1234`` ``````void Update() { // Place the collision code here }``````

And your game loop should look like
 ``1234567891011`` ``````void Loop() { Init(); while(Running) { Input(); Update(); Render(); } Cleanup(); }``````
fun2code:
I fixed your code for the bullet::hit but it's not working even though it's compiling. I changed `Enemy::hit( Bullet& b )` to `Enemy::hit( class Bullet& b )` and it's compiling but when i play the game and the bullet hits the ship nothing happens? Any idea why?
This is my enemy class:
 ``12345678910111213`` ``````class Enemy { private: SDL_Rect Box; int x, y; int yVel, xVel; public: bool shipalive; Enemy(); void Enemy::hit( class Bullet& b); void move(); void show(); };``````

 ``1234567891011121314151617181920212223242526272829303132333435363738`` ``````Enemy::Enemy() { shipalive = true; x = 0; y = 80; yVel = 0; xVel = 1; } void Enemy::show() { if(shipalive == true) { apply_surface( x, y, enemy, screen ); } } void Enemy::hit(class Bullet& b) { if( ((b.xb+BULLET_WIDTH) > x) && (b.xb < (x+ENEMY_WIDTH)) )// horizontally alligned if( ((b.yb+BULLET_HEIGHT) < y) && (b.yb > (y+ENEMY_HEIGHT)) )// vertically alligned { shipalive = false; b.alive = false; } } void Enemy::move() { if(shipalive == false) return; x += xVel; y += yVel; if( ( x < 0 ) || ( x + ENEMY_WIDTH > LEVEL_WIDTH ) ) { shipalive = false; } if( ( y < 0 ) || ( y + ENEMY_HEIGHT > LEVEL_HEIGHT ) ) { shipalive = false; } }``````

Here's my bullet class just to make sure:
 ``12345678910111213`` ``````class Bullet { private: public: int xb, yb; int xVelb, yVelb; bool alive; Bullet(); void handle_input(int xFire, int yFire ); void move(); void show(); };``````

 ``123456789101112131415161718192021222324252627282930313233343536373839`` ``````Bullet::Bullet() { alive = false; xb = yb = 0; xVelb = 0; yVelb = -3; } void Bullet::handle_input(int xFire, int yFire ) { if( alive == true ) return; if( event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_SPACE ) { xb = xFire; yb = yFire; alive = true; } } void Bullet::move() { xb += xVelb; yb += yVelb; if( alive == false ) return; if( ( xb < 0 ) || ( xb + BULLET_WIDTH > LEVEL_WIDTH ) ) { alive = false; } if( ( yb < 0 ) || ( yb + BULLET_HEIGHT > LEVEL_HEIGHT ) ) { alive = false; } } void Bullet::show() { if(alive == true) { apply_surface( xb, yb, bullet, screen ); } }``````

And i put this in main:

 ``12345678`` `````` mySquare.move(); myBullet.move(); myEnemy.move(); myEnemy.hit(myBullet); apply_surface( 0, 0, background, screen ); mySquare.show(); myEnemy.show(); myBullet.show();``````

fredbill:
And your game loop should look like
 ``1234567891011`` ``````void Loop() { Init(); while(Running) { Input(); Update(); Render(); } Cleanup(); }``````

But don't i already have that in main?
 ``12345678910111213141516171819202122232425262728293031323334`` `````` while( quit == false ) { fps.start(); while( SDL_PollEvent( &event ) ) { mySquare.handle_input(); myBullet.handle_input(mySquare.x, mySquare.y); if( event.type == SDL_QUIT ) { quit = true; } } mySquare.move(); myBullet.move(); myEnemy.move(); apply_surface( 0, 0, background, screen ); mySquare.show(); myEnemy.show(); myBullet.show(); if( SDL_Flip( screen ) == -1 ) { return 1; } if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND ) { SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() ); } } clean_up(); return 0; }``````

Thanks,
Last edited on
I'm not sure why you had to add the keyword 'class' in
`Enemy::hit( class Bullet& b ) `. Perhaps you declared class Enemy before class Bullet. The snippets you gave suggest this but the code is broken up so I can't tell for sure. I guess you found a workaround to stop the compiler errors.
I did omit the void return type in my code (sorry about that) but I believe that was the only error I made. I see other changes like data members named xb which were named x before (reason for this?).
I thought I could help straighten things out for you quickly but we're just going around in circles and making no progress. I'm also tired of the silly tug of war with Fredbill over the methods.
I'm sorry, but I'm disengaging from this thread. Good luck. Perhaps Fredbill can guide you to a working result.
Well then put the collision handling there. I guess the way I design my code is different than most.
Hi guys,
 I guess you found a workaround to stop the compiler errors.
Yes i did and the game runs but when the bullet hits the ufo nothing happens, why?
 Perhaps you declared class Enemy before class Bullet.
I did declare class enemy before class bullet i had no idea it would make any difference? Why would it, and what difference would it make it i put `class` in `Bullet::hit( Bullet& b )`?
 like data members named xb which were named x before (reason for this?).
Yeah... Sorry about that i was just experimenting and changed it so it'd be easier to understand.
 I thought I could help straighten things out for you quickly but we're just going around in circles and making no progress.
I don't need it to be finished quickly i've got years to do it.
Thanks,
Last edited on
Hi again,
 Well then put the collision handling there.
Do you mean i put fun2code's collision code in `Update()` kinda like this:
 ``123456789`` ``````void Update(Bullet &b, Enemy &e) { if( ((b.xb+BULLET_WIDTH) > x) && (b.xb < (e.x+ENEMY_WIDTH)) )// horizontally alligned if( ((b.yb+BULLET_HEIGHT) < y) && (b.yb > (e.y+ENEMY_HEIGHT)) )// vertically alligned { e.shipalive = false; b.alive = false; } }``````

Anything like this or am i completely wrong?

Maybe you could give me more info about where to put it and where should i declare `void Update()`? How do i make it a 'global function'?
I decided to check out the Lazy Foo SDL tutorials at
http://www.lazyfoo.net/SDL_tutorials/
It looks like you just need to keep going through them. Judging from the content of your program I'm guessing you've reached lesson 16 (on motion). Lesson 17 is on collision detection!!
The methods shown there are just like what Fredbill has been recommending.
Perhaps this is where he learned them.
Forget my methods (which work, but I've made them up myself) and use the methods explained in the tutorials. Are you having trouble understanding lesson 17?
Thanks for checking out the tutorials and not leaving the thread :)
Anyway to answer your question i did all of lazyfoo's tutorials including lesson 17 but that tutorial was to make a wall and i couldn't figure out how to apply it to a class, i though that the code would be completely different and i couldn't find any tutorials about class to class collision? Do you have any ideas how i could do class to class collision.
Thanks.
The check_collision() function in the tutorial just checks for a collision of two SDL_Rect objects. You can make the response to a collision anything you like.
It doesn't matter if the colliding objects are different class types, as long as they both have SDL_Rect members.

I see you've added a `SDL_Rect box;` member to your classes. This is good. It replaces the x, y position members with box.x, box.y. You could remove the x, y members. Make sure to give values for the box.w, box.h members.
myBullet.box.w = BULLET_WIDTH;
myBullet.box.h = BULLET_HEIGHT;

Using the check_collision() function in the tutorial you could code a response like so:
 ``12345`` ``````if( check_collision(myBullet.box, myEnemy.box) )// collision detection { myBullet.alive = false;// collision response myEnemy.alive = false; }``````

This makes for a lot of changes to your code though.
Could you post your present code in one piece so I can see what you've got now? I might try rewriting it.
Hi fun2code,
 It doesn't matter if the colliding objects are different class types, as long as they both have SDL_Rect members.

Really?!
Anyway...

I hope you don't mind me saying so but is that kinda what fredbill was trying to say, but he said to add `SDL_Rect getBox() {return Box;}` do i need to add this and what does this do?
 It replaces the x, y position members with box.x, box.y. You could remove the x, y members.

Do you want me to remove int x,y from the class and replace it with int box.x, box.y?
Does this mean that everything i used x and y for i'll replace it with box.x and box.y?
 This makes for a lot of changes to your code though.

Doesn't matter it's not that bad i just want it to work really, it's ok if it changes things.
 Could you post your present code in one piece so I can see what you've got now? I might try rewriting it.

Thanks :)
Sure i'll do it in a few minutes if i can't get it to work.
EDIT: Ok i updated my code abit and theres a few problems, can you see if you can fix them, i'll put them in the post below this...
Last edited on
Here's my code!
 ``123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381`` ``````#include "SDL.h" #include "SDL_image.h" #include #include "Classes.h" // I split my code up into header files #include "Graphics.h" // But all just put all my code together here #include "Timer.h" const int SCREEN_WIDTH = 640; const int SCREEN_HEIGHT = 480; const int SCREEN_BPP = 32; const int FRAMES_PER_SECOND = 20; const int SQUARE_WIDTH = 16; const int SQUARE_HEIGHT = 12; const int LEVEL_WIDTH = 640; const int LEVEL_HEIGHT = 480; const int BULLET_HEIGHT = 5; const int BULLET_WIDTH = 5; const int ENEMY_HEIGHT = 11; const int ENEMY_WIDTH = 13; const int myBullet.box.w = BULLET_WIDTH; // This is what you told me to do const int myBullet.box.h = BULLET_HEIGHT; // It doesn't work, why? SDL_Surface *square = NULL; SDL_Surface *background = NULL; SDL_Surface *screen = NULL; SDL_Surface *bullet = NULL; SDL_Surface *enemy = NULL; SDL_Event event; class Enemy { private: SDL_Rect Box; public: int xVel, yVel; bool shipalive; Enemy(); void move(); void show(); }; class Square { public: bool squarealive; int x, y; int xVel, yVel; Square(); void handle_input(); void move(); void show(); }; class Bullet { private: SDL_Rect Box; public: int xVelb, yVelb; bool alive; Bullet(); void handle_input(int xFire, int yFire ); void move(); void show(); }; class Timer { private: int startTicks; int pausedTicks; bool paused; bool started; public: Timer(); void start(); void stop(); void pause(); void unpause(); int get_ticks(); bool is_started(); bool is_paused(); }; SDL_Surface *load_image( std::string filename ) { SDL_Surface* loadedImage = NULL; SDL_Surface* optimizedImage = NULL; loadedImage = IMG_Load( filename.c_str() ); if( loadedImage != NULL ) { optimizedImage = SDL_DisplayFormat( loadedImage ); SDL_FreeSurface( loadedImage ); if( optimizedImage != NULL ) { SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 0, 0xFF, 0xFF ) ); } } return optimizedImage; } bool init() { if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 ) { return false; } screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE ); if( screen == NULL ) { return false; } SDL_WM_SetCaption( "Move the Spaceship", NULL ); return true; } bool load_files() { square = load_image( "spaceship.png" ); background = load_image( "bg.png" ); bullet = load_image( "bullet.png" ); enemy = load_image( "enemy.png" ); if( square == NULL || background == NULL || bullet == NULL || enemy == NULL ) { return false; } return true; } void clean_up() { SDL_FreeSurface( square ); SDL_FreeSurface( background ); SDL_FreeSurface( bullet ); SDL_FreeSurface( enemy ); SDL_Quit(); } void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL ) { SDL_Rect offset; offset.x = x; offset.y = y; SDL_BlitSurface( source, clip, destination, &offset ); } bool check_collision(myBullet.box, myEnemy.box); Timer::Timer() { startTicks = 0; pausedTicks = 0; paused = false; started = false; } void Timer::start() { started = true; paused = false; startTicks = SDL_GetTicks(); } void Timer::stop() { started = false; paused = false; } void Timer::pause() { if( ( started == true ) && ( paused == false ) ) { paused = true; pausedTicks = SDL_GetTicks() - startTicks; } } void Timer::unpause() { if( paused == true ) { paused = false; startTicks = SDL_GetTicks() - pausedTicks; pausedTicks = 0; } } int Timer::get_ticks() { if( started == true ) { if( paused == true ) { return pausedTicks; } else { return SDL_GetTicks() - startTicks; } } return 0; } bool Timer::is_started() { return started; } bool Timer::is_paused() { return paused; } Bullet::Bullet() { alive = false; Box.x = Box.y = 0; xVelb = 0; yVelb = -3; } void Bullet::handle_input(int xFire, int yFire ) { if( alive == true ) return; if( event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_SPACE ) { Box.x = xFire; Box.y = yFire; alive = true; } } void Bullet::move() { Box.x += xVelb; Box.y += yVelb; if( alive == false ) return; if( ( Box.x < 0 ) || ( Box.x + BULLET_WIDTH > LEVEL_WIDTH ) ) { alive = false; } if( ( Box.y < 0 ) || ( Box.y + BULLET_HEIGHT > LEVEL_HEIGHT ) ) { alive = false; } } void Bullet::show() { if(alive == true) { apply_surface( Box.x, Box.y, bullet, screen ); } } Enemy::Enemy() { shipalive = true; Box.x = 0; Box.y = 80; yVel = 0; xVel = 1; } void Enemy::show() { if(shipalive == true) { apply_surface( Box.x, Box.y, enemy, screen ); } } void Enemy::move() { if(shipalive == false) return; Box.x += xVel; Box.y += yVel; if( ( Box.x < 0 ) || ( Box.x + ENEMY_WIDTH > LEVEL_WIDTH ) ) { shipalive = false; } if( ( Box.y < 0 ) || ( Box.y + ENEMY_HEIGHT > LEVEL_HEIGHT ) ) { shipalive = false; } } Square::Square() { squarealive = true; x = 0; y = 0; xVel = 0; yVel = 0; } void Square::handle_input() { if(squarealive == false) return; if( event.type == SDL_KEYDOWN ) { switch( event.key.keysym.sym ) { case SDLK_UP: yVel -= SQUARE_HEIGHT / 2; break; case SDLK_DOWN: yVel += SQUARE_HEIGHT / 2; break; case SDLK_LEFT: xVel -= SQUARE_WIDTH / 2; break; case SDLK_RIGHT: xVel += SQUARE_WIDTH / 2; break; } } else if( event.type == SDL_KEYUP ) { switch( event.key.keysym.sym ) { case SDLK_UP: yVel += SQUARE_HEIGHT / 2; break; case SDLK_DOWN: yVel -= SQUARE_HEIGHT / 2; break; case SDLK_LEFT: xVel += SQUARE_WIDTH / 2; break; case SDLK_RIGHT: xVel -= SQUARE_WIDTH / 2; break; } } } void Square::move() { if(squarealive == false) return; x += xVel; if( ( x < 0 ) || ( x + SQUARE_WIDTH > LEVEL_WIDTH ) ) { x -= xVel; } y += yVel; if( ( y < 0 ) || ( y + SQUARE_HEIGHT > LEVEL_HEIGHT ) ) { y -= yVel; } } void Square::show() { if(squarealive == false) return; { apply_surface( x, y, square, screen ); } } int main( int argc, char* args[] ) { bool quit = false; Square mySquare; Bullet myBullet; Enemy myEnemy; Timer fps; if( init() == false ) { return 1; } if( load_files() == false ) { return 1; } while( quit == false ) { fps.start(); while( SDL_PollEvent( &event ) ) { mySquare.handle_input(); myBullet.handle_input(mySquare.x, mySquare.y); if( event.type == SDL_QUIT ) { quit = true; } } mySquare.move(); myBullet.move(); myEnemy.move(); apply_surface( 0, 0, background, screen ); mySquare.show(); myEnemy.show(); myBullet.show(); if( SDL_Flip( screen ) == -1 ) { return 1; } if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND ) { SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() ); } } clean_up(); return 0; }``````

I may have mixed the code up a bit from copying it together from the header files but i don't think i did, sorry if i might have!

Also, in case you wanted to try the game without compiling it i set up a dropbox link here: https://www.dropbox.com/s/qvroqvskhfp3rc0/SDL%20Game.zip
Last edited on
Pages: 123

You are using a version without Ads of this website. Please, consider donating: