Need help with space shooter in SDL!

Pages: 123
Hi everyone,
I'm really new to SDL and i've been trying to make a spaceshooter game,
so far i've got a ship you can move around in using the arrow key (square shaped) and a scrolling background. I'm up to making my ship fire bullets at the moment and it's really not working, i've finished lazy foo's tutorials on SDL and there's nothing on this.
My Problems:
Please look at my LAST post for my current problems.
Now i'm only 11 so please don't make the answer too complex...

Here's my code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#include "SDL.h"
#include "SDL_image.h"
#include "SDL_mixer.h"
#include "SDL_ttf.h"
#include <string>
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;
SDL_Surface *square = NULL;
SDL_Surface *background = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *bullet = NULL;
SDL_Event event;
class Square
{
public:
	int x, y;
	int xVel, yVel;
    Square();
    void handle_input();
    void move();
    void show();
};
class Bullet
{
    public:
	int x, y;
	int xVel, yVel;

	bool alive;// bool is true/false
	Bullet();
	void handle_input(int xFire, int yFire );// pass where to fire bullet from
	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;
}

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 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" );
    if( square == NULL )
    {
        return false;
    }
    if( background == NULL )
    {
        return false;
    }
	    if( bullet == NULL )
    {
        return false;
    }
    return true;
}

void clean_up()
{
    SDL_FreeSurface( square );
    SDL_FreeSurface( background );
	SDL_FreeSurface( bullet );
    SDL_Quit();
}
Bullet::Bullet()// set initial values for all data members
{
    alive = false;
    x = y = 0;
    xVel = 0;
    yVel = 3;// this will be the bullet speed

}

void Bullet::handle_input(int xFire, int yFire )
{
	if( alive == true ) return;
    if( event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_SPACE )
    {
        x = xFire;
        y = yFire;
        alive = true;
    }
}
void Bullet::move()
{
	x += xVel;
	y += yVel;
    if( alive == false ) return;// do nothing and return
    if( ( x < 0 ) || ( x + BULLET_WIDTH > LEVEL_WIDTH ) )
    {
		alive = false;
    }
    if( ( y < 0 ) || ( y + BULLET_HEIGHT > LEVEL_HEIGHT ) )
    {
       alive = false;
    }
}
void Bullet::show()
{
	if(alive == true)
	{
            apply_surface( x, y, bullet, screen );
	}
}
Square::Square()
{
    x = 0;
    y = 0;
    xVel = 0;
    yVel = 0;
}

void Square::handle_input()
{
    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()
{
    x += xVel;
    if( ( x < 0 ) || ( x + SQUARE_WIDTH > LEVEL_WIDTH ) )
    {
        x -= xVel;
    }
    y += yVel;
    if( ( y < 0 ) || ( y + SQUARE_HEIGHT > LEVEL_HEIGHT ) )
    {
        y -= yVel;
    }
	x += xVel;// a second time?
    if( ( x < 0 ) || ( x + SQUARE_WIDTH > BULLET_WIDTH ) )
    {
        x -= xVel;
    }
    y += yVel;// a second time?
    if( ( y < 0 ) || ( y + SQUARE_HEIGHT > BULLET_HEIGHT ) )
    {
        y -= yVel;
    }
}

void Square::show()
{
    apply_surface( x, y, square, screen );
}



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;
}

int main( int argc, char* args[] )
{
    bool quit = false;
    Square mySquare;
	Bullet myBullet;
    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);// this is how bullet gets fired at ships position
            if( event.type == SDL_QUIT )
            {
                quit = true;
            }
        }
        mySquare.move();
	myBullet.move();
        apply_surface( 0, 0, background, screen );
        mySquare.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!
EDIT: I REMOVED THE SCROLLING BACKGROUND
Last edited on
Anyone?
Full program postings with a list of problems typically don't get many responses because they're so broad and time consuming to respond to.

Furthermore, I'm always a little disappointed to see people starting with SDL 1.2 because it's so horribly archaic and outdated.... I want to blame Lazyfoo for that because his tutorials are so popular... but until I have a better tutorial to recommend I can't really do anything about it (short of writing one myself, which I simply don't have the time or energy for).

EDIT: though even SDL 1.2 is infinitely better than trying to mimic this crap in the console. So I guess I shouldn't complain. /EDIT


That said... I'll try to help you a little bit. But considering how exhausting these types of questions are I can't guarantee I'll solve all your problems.



but for some reason alive turns to true as soon as i open the game


Look at your constructor:

1
2
3
4
Bullet::Bullet()
{
	if( alive = true )  // <-  !!
	{


= is assignment, so you are setting 'alive' to true here. This is why it's true immediately.

You probably meant == for comparison... however even that is wrong. This is the constructor, which means it's the very first function called when a Bullet is created. At this point.... 'alive' has not been initialized yet... so checking to see whether or not it's true is meaningless.

You must always initialize variables before you use them.

i also need help on how to get the bullet to move when it's spawned


Typical approach is to give the bullet an X/Y velocity ("speed"). Every logic update (once per frame), you go through all your bullets and update them. During this update, you would add that speed to the bullet's position, causing it to move in a direction gradually over time.

how to find the location of the ship so it spawns from there when it's fired.


Surely you know the location of the ship. All you have to do is pass that location to the bullet somehow when it's spawned. Like as a parameter or something.

1
2
3
4
5
void spawnbullet( int x, int y, int velx, int vely )
{
   // create a bullet at position x,y
   //  moving at speed velx,vely
}
Last edited on
Thank you so much!!!
I edited the code and now the bullet is only alive when i hold down space!
Now i just need to make it into a bullet...
Ok i've got the bullet to spawn at the location of the ship but it's not moving...
I want it to move upwards reasonably fast and when it hits the end of the map it disappears but it doesn't move at all and i can't get it to, why isn't it moving?
Here's my code for the x, y and velocity
1
2
3
4
5
6
7
8
9
10
Bullet::Bullet()
{
	int alive;
	{
	x1 = x;
	y1 = y + 3;
	xVel1 = 0;
	yVel1 = 5;
	}
}

Here's my collision code
1
2
3
4
5
6
7
8
9
10
11
void Bullet::move1()
{
    if( ( x1 < 0 ) || ( x1 + BULLET_WIDTH > LEVEL_WIDTH ) )
    {
       alive = false;
    }
    if( ( y1 < 0 ) || ( y1 + BULLET_HEIGHT > LEVEL_HEIGHT ) )
    {
       alive = false;
    }
}

Any suggestions?
Thanks
Last edited on
Can I ask why you derived Bullet from Square?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Square
{
    private:
    int x, y;
    int xVel, yVel;
    public:
    Square();
    void handle_input();
    void move();
    void show();
    void set_camera();
};
class Bullet: public Square// why derive from Square?
{
private:
		int x1, y1;
		int xVel1, yVel1;
public:
	int alive;
	Bullet();
	void handle_input1();
		void move1();
		void show1();
};

I'd like to help, but it's hard to understand why there is this duplication of data and function members here. Perhaps you have good reason. What is your thinking in setting up the 2 classes this way?

Your Bullet::move1() function needs to increment the bullet position, just like you do in the Square::move() function. So, something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
void Bullet::move1()
{
    x1 += xVel1;// increment bullet position.
    y1 += yVel1;
    if( ( x1 < 0 ) || ( x1 + BULLET_WIDTH > LEVEL_WIDTH ) )
    {
       alive = false;
    }
    if( ( y1 < 0 ) || ( y1 + BULLET_HEIGHT > LEVEL_HEIGHT ) )
    {
       alive = false;
    }
}


In your Bullet constructor you have declared a local variable 'alive'.
This is not the class data member. I think this is closer to what's needed.
1
2
3
4
5
6
7
8
Bullet::Bullet()
{
	alive = false;
	x1 = x;   // where do values for x and y come from?
	y1 = y + 3;
	xVel1 = 0;
	yVel1 = 5;	
}

You said: "Ok i've got the bullet to spawn at the location of the ship..."
How did you get that to work?

It's difficult to offer help because there are so many issues and problems.
In your Bullet::show1() function:
1
2
3
4
5
6
7
8
9
10
11
void Bullet::show1()
{
	if(alive = true)// = is assignment. Usually == is used here.
	{
    apply_surface( x1, y1, bullet, screen );
	}
	if(alive = false)
	{
		SDL_FreeSurface( bullet );// why do this?
	}
}
Last edited on
Thanks,
I'm really sorry because after Disch explained to me some of the problems i changed the code but didn't update my first post! :0
Can I ask why you derived Bullet from Square?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Square
{
    private:
    int x, y;
    int xVel, yVel;
    public:
    Square();
    void handle_input();
    void move();
    void show();
    void set_camera();
};
class Bullet: public Square// why derive from Square?
{
private:
		int x1, y1;
		int xVel1, yVel1;
public:
	int alive;
	Bullet();
	void handle_input1();
		void move1();
		void show1();
};

Well i changed that too...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Square
{
public:
	int x, y;
	int xVel, yVel;
    Square();
    void handle_input();
    void move();
    void show();
    void set_camera();
};
class Bullet: public Square
{
private:
		int x1, y1;
		int xVel1, yVel1;
public:
	int alive;
	Bullet();
	void handle_input1();
		void move1();
		void show1();
};

But i don't understand shouldn't i derive it from square? Why not?
You said: "Ok i've got the bullet to spawn at the location of the ship..."
How did you get that to work?

It's difficult to offer help because there are so many issues and problems.
In your Bullet::show1() function:
1
2
3
4
5
6
7
8
9
10
11
void Bullet::show1()
{
	if(alive = true)// = is assignment. Usually == is used here.
	{
    apply_surface( x1, y1, bullet, screen );
	}
	if(alive = false)
	{
		SDL_FreeSurface( bullet );// why do this?
	}
}

Firstly with the SDL_FreeSurface and the alive = true in Bullet::show1 i changed after Disch explained it to me so the code in show1 looks like:
1
2
3
4
5
6
7
void Bullet::show1()
{
	if(alive)
	{
    apply_surface( x1, y1, bullet, screen );
	}
}

I'll edit my code in the first post.
In your Bullet constructor you have declared a local variable 'alive'.
This is not the class data member. I think this is closer to what's needed.
1
2
3
4
5
6
7
8
Bullet::Bullet()
{
	alive = false;
	x1 = x;   // where do values for x and y come from?
	y1 = y + 3;
	xVel1 = 0;
	yVel1 = 5;	
}

Well i changed that too..
1
2
3
4
5
6
7
8
9
10
Bullet::Bullet()
{
	int alive;
	{
	x1 = x;
	y1 = + 3;
	xVel1 = 0;
	yVel1 = 5;
	}
}

The values x and y are the squares location, this is actually working when the bullet is first spawned but then when i move the ship and hold down space to spawn it again it spawns at the top left of the screen (where the ship spawned originally) so for some reason it mustn't track the square when it's moved. also even after editing the code the bullet becomes alive as soon as i start the game (take a look at the code in my first post i'll edit it to what it is now)
Your Bullet::move1() function needs to increment the bullet position, just like you do in the Square::move() function. So, something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
void Bullet::move1()
{
    x1 += xVel1;// increment bullet position.
    y1 += yVel1;
    if( ( x1 < 0 ) || ( x1 + BULLET_WIDTH > LEVEL_WIDTH ) )
    {
       alive = false;
    }
    if( ( y1 < 0 ) || ( y1 + BULLET_HEIGHT > LEVEL_HEIGHT ) )
    {
       alive = false;
    }
}

Thanks, i didn't know i needed to do that
Thank you so much fun2code the bullets actually working after me trying for weeks!
But i have another problem... The bullet doesn't spawn when the game starts so thats really good.
I just realised it does, i just can't see it
The problem i'm having is when i'm holding the bullet down i can see it travelling downwards slowly, i let go of space and the bullet disappears, the problem is that the bullet isn't killed it's still travelling downwards and i see it again if i hold down space so my question is: how do i kill the bullet when i let go of space infact i don't want to have to hold down space, can i make it the bullet is still there and i can see it until it hits the side of the screen and only then can i spawn another bullet.
And another problem...
The bullet doesn't get killed when it hits the end of the screen, well i can't see it.These problems i think are because when alive is set to false all that happens it Bullet::show1() is disabled so i can't see it. How can i make it when alive is false the bullet is 'killed'?
Thanks,
Last edited on
In your program you have one ship (a Square object) and one Bullet object. These are declared on lines 332 and 333
1
2
Square mySquare;
Bullet myBullet;

This seems like a fine start. It means that only one bullet exists, so only one at a time can be fired, but I say get this simple situation working first then add more bullets.

There are many ways to approach modeling a ship and a bullet. Inheriting Square by Bullet could be sensible since they share many data members (x,y,xVel,yVel) and methods (void handle_input(); void move(); void show();), but I would define them as distinct objects.

I would set it up this way:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// class to represent a Bullet
class Bullet
{
    public:
	int x, y;
	int xVel, yVel;

	bool alive;// bool is true/false
	Bullet();
	void handle_input(int xFire, int yFire );// pass where to fire bullet from
	void move();
	void show();
};

Bullet::Bullet()// set initial values for all data members
{
    alive = false;
    x = y = 0;
    xVel = 0;
    yVel = 3;// this will be the bullet speed
}

void handle_input(int xFire, int yFire )
{
    if( event.type == SDL_KEYDOWN && event.key.keysym.sym == DLK_SPACE )
    {
        x = xFire;
        y = yFire;
        alive = true;
    }
}

void Bullet::move()
{
    if( alive == false ) return;// do nothing and return
    x += xVel;
    y += yVel;
    if( ( x < 0 ) || ( x + BULLET_WIDTH > LEVEL_WIDTH ) )
    {
		alive = false;
    }
    if( ( y < 0 ) || ( y + BULLET_HEIGHT > LEVEL_HEIGHT ) )
    {
       alive = false;
    }
}

void Bullet::show()
{
	if(alive == true)
	{
            apply_surface( x, y, bullet, screen );
	}
}

Then leave the Square class and its functions like you have them, except remove lines 227 and 232 since you don't need to increment position twice.
This:
1
2
x += xVel;
y += yVel;

has already been done once.

Then your code in main() lines 346 to 361 could be more like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while( SDL_PollEvent( &event ) )
        {
            mySquare.handle_input();
	    myBullet.handle_input(mySquare.x, mySquare.y);// this is how bullet gets fired at ships position
            if( event.type == SDL_QUIT )
            {
                quit = true;
            }
        }
        mySquare.move();
        mySquare.set_camera();
	myBullet.move();
        apply_surface( 0, 0, background, screen, &camera );
        mySquare.show();
	myBullet.show();

I think that may get things working as you wish, or at least a lot closer.

EDIT: Forgot to address your question

And another problem...
The bullet doesn't get killed when it hits the end of the screen, well i can't see it.These problems i think are because when alive is set to false all that happens it Bullet::show1() is disabled so i can't see it. How can i make it when alive is false the bullet is 'killed'?

The bullet does become inactive (not moved or drawn) when alive = false. But this should be OK. What do you mean by 'killed'? It should no longer exist at all? You want it to remain in existence since the next shot fired is really just the 1st one being reused.
Last edited on
OmerGerd. Why are so many young people trying to use SDL & C++?

#hypocriteforever

Seriously though. I had my experiences with SDL. Since I'm too lazy to read that post above this one, I'm going to address the problem at hand.

First off, get change the handle input function to this. This version is more readable in my opinion than yours :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while(SDL_PollEvent event)
{
	switch(event.type
	{
		case SDL_KEYDOWN:
			switch(event.key.keysym.sym)
			{
				case SDLK_SPACE:
					x = xFire;
					y = yFire;
					alive = true;
				break;
			}
		break;
	}
}

(btw, you can just plug this into your program with no worries (hopefully))


(The screen-bullet-not-dying-thingy fix) Make some code like :
1
2
3
4
5
if(x >= LEVEL_WIDTH)
{
	alive = false;
}
// This is if the bullet travels horisontally. 

If the bullet has a collision box, set the sides to NULL to avoid ghostly collisions.

*Tip* If I were you, instead of making a huge map, I would make the map the size of the screen, and only move the background. The enemy ships would just spawn at 640 and move towards your character. Your ship would stay still. This would reduce CPU usage, and make things simpler.

Erm, I'm to lazy to read the rest of your post. Just PM me a **SHORT** message and I can help you with your code ;D
Last edited on
Thanks so much for your help everyone!
It works alot better now, the bullet doesn't spawn when the game starts and i can choose when and where it spawns (it spawns at my ship when i press space!) I've got another problem... when the bullet spawn it goes to the right, how can i make it go vertically?but.. I'm having more problems, firstly when i go out of a certain area (when the screens starts to scroll) the bullet spawning doesn't work at all, it spawns some distance to the right of my ship and whenever i go fast to try and catch the bullet up, it speeds up!? Why is the ship moving across the background (the level width is 1290 x 960) making the bullet go faster so i can't catch it up and the spawning co-ordinates not working (the bullet going to the right of my screen when spawning)? ALSO... i don't actually want to only have one bullet at a time and i don't want the last bullet to be 'killed' when i press space again. Will i need to make some kind of list for the bullets? Say i want to have three bullets on the screen at once and if i press space when all three bullets are on screen nothing would happen. Another problem i'm having is that when the ship collides with the bullet, or the other way around they go straight through each other, why?
Then leave the Square class and its functions like you have them, except remove lines 227 and 232 since you don't need to increment position twice.
This is lines 227 and 232:
1
2
3
4
void Square::show()
{ // <- line 227?
    apply_surface( x - camera.x, y - camera.y, square, screen );
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Square::set_camera()
{ // <- line 232?
    camera.x = ( x + SQUARE_WIDTH / 2 ) - SCREEN_WIDTH / 2;
    camera.y = ( y + SQUARE_HEIGHT / 2 ) - SCREEN_HEIGHT / 2;
    if( camera.x < 0 )
    {
        camera.x = 0;
    }
    if( camera.y < 0 )
    {
        camera.y = 0;
    }
    if( camera.x > LEVEL_WIDTH - camera.w )
    {
        camera.x = LEVEL_WIDTH - camera.w;
    }
    if( camera.y > LEVEL_HEIGHT - camera.h )
    {
        camera.y = LEVEL_HEIGHT - camera.h;
    }
}

If the bullet has a collision box, set the sides to NULL to avoid ghostly collisions.

How would i do that? I need a collision box because i'm going to eventually add enemy ships..
*Tip* If I were you, instead of making a huge map, I would make the map the size of the screen, and only move the background. The enemy ships would just spawn at 640 and move towards your character. Your ship would stay still. This would reduce CPU usage, and make things simpler.

I would make things alot simpler but i reckon the gameplay would be better if it was a huge map, just my opinion.
Thanks,
P.S I'll update the code in my first post now...
Last edited on
Dude, the gameplay would be the same if it was just a static map with the background that only moves.

Here's an example of what I mean:

1
2
3
4
Box.x  = NULL;
Box.y  = NULL;
Box.w  = NULL;
Box.h  = NULL;
Last edited on
If it was a static map was a moving background how would you run away?
Omergerd. If you could run away with the arrow keys. Just confine the ship to only move inside the screen and scroll the background.
I'll try to answer some questions. Some I can't help with because I don't use SDL (I use SFML). I hear SDL is great, I just don't know it. The "bullet speeding up" problem may be caused by your camera changing position. Maybe you should go with Fredbills advice and restrict everything to the screen size. He may be able to help you with the SDL related issues.

Sorry if I got the line #'s wrong. I meant that there shouldn't be x += xVel and y = yVel twice in this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void Square::move()
{
    x += xVel;
    if( ( x < 0 ) || ( x + SQUARE_WIDTH > LEVEL_WIDTH ) )
    {
        x -= xVel;
    }
    y += yVel;
    if( ( y < 0 ) || ( y + SQUARE_HEIGHT > LEVEL_HEIGHT ) )
    {
        y -= yVel;
    }
	x += xVel;// a second time?
    if( ( x < 0 ) || ( x + SQUARE_WIDTH > BULLET_WIDTH ) )
    {
        x -= xVel;
    }
    y += yVel;// a second time?
    if( ( y < 0 ) || ( y + SQUARE_HEIGHT > BULLET_HEIGHT ) )
    {
        y -= yVel;
    }
}


I think I can help with the following:
I've got another problem... when the bullet spawn it goes to the right, how can i make it go vertically?
.
I posted this for you:
1
2
3
4
5
6
7
Bullet::Bullet()// set initial values for all data members
{
    alive = false;
    x = y = 0;
    xVel = 0;
    yVel = 3;// this will be the bullet speed
}

The line yVel = 3; was to make it move vertically.
I see your code now looks like this:
1
2
3
4
5
6
7
Bullet::Bullet()// set initial values for all data members
{
    alive = false;
    x = y = 0;
    xVel = 2;
    yVel = 0;// this will be the bullet speed
}

Why did you change it? The line xVel = 2; is making the bullet go to the right.

Next issue:

ALSO... i don't actually want to only have one bullet at a time and i don't want the last bullet to be 'killed' when i press space again. Will i need to make some kind of list for the bullets?

For more bullets you will need a list or vector (some kind of container) of bullets. This will up the program complexity. Not a lot, but I again encourage you to get the "one bullet" version working perfectly before adding more bullets. I'll still help when you reach that point.

The bullet gets reset when you hit the spacebar again because you told it to here:
1
2
3
4
5
6
7
8
9
void handle_input(int xFire, int yFire )
{
    if( event.type == SDL_KEYDOWN && event.key.keysym.sym == DLK_SPACE )
    {
        x = xFire;
        y = yFire;
        alive = true;
    }
}

It assigns x = xFire and y = yFire even if alive is already true (if the bullet is still in flight). To make it so a space key press is ignored unless the bullet has finished its travel, add code so the position is reset only if alive = false.
1
2
3
4
5
6
7
8
9
10
11
void handle_input(int xFire, int yFire )
{
    if( alive == true ) return;// bullet still in flight so do nothing and return

    if( event.type == SDL_KEYDOWN && event.key.keysym.sym == DLK_SPACE )
    {
        x = xFire;
        y = yFire;
        alive = true;
    }
}


Last issue:
Another problem i'm having is that when the ship collides with the bullet, or the other way around they go straight through each other, why?

It's because there is no collision detection code. Is there any code you can point to which you think should be doing this and isn't?
Maybe you could add a second Square object as a target for the bullet. You would need to add a
hit( Bullet shot ) function to the Square class. The code in this function would check to see if the shot and the target rectangles overlap. If they do it's a hit and shot.alive = false should be assigned. The next step would be to give the Square class health, and decrease that when a hit is detected.
I think this is enough for now.
Last edited on
Thanks fun2code,
I meant that there shouldn't be x += xVel and y = yVel twice in this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void Square::move()
{
    x += xVel;
    if( ( x < 0 ) || ( x + SQUARE_WIDTH > LEVEL_WIDTH ) )
    {
        x -= xVel;
    }
    y += yVel;
    if( ( y < 0 ) || ( y + SQUARE_HEIGHT > LEVEL_HEIGHT ) )
    {
        y -= yVel;
    }
	x += xVel;// a second time?
    if( ( x < 0 ) || ( x + SQUARE_WIDTH > BULLET_WIDTH ) )
    {
        x -= xVel;
    }
    y += yVel;// a second time?
    if( ( y < 0 ) || ( y + SQUARE_HEIGHT > BULLET_HEIGHT ) )
    {
        y -= yVel;
    }
}

For whatever reason if i remove those line that you told me to the ship doesn't move?!
Thanks for the code for the keydown it only fires when there's no alive bullet!
The line yVel = 3; was to make it move vertically.

No it doesn't it makes the bullet go downwards?!
Do i need a combination of yVel and xVel?
It's because there is no collision detection code. Is there any code you can point to which you think should be doing this and isn't?

Yes this:
1
2
3
4
5
6
7
8
9
10
11
	x += xVel;
    if( ( x < 0 ) || ( x + SQUARE_WIDTH > BULLET_WIDTH ) )
    {
        x -= xVel;
    }
    y += yVel;
    if( ( y < 0 ) || ( y + SQUARE_HEIGHT > BULLET_HEIGHT ) )
    {
        y -= yVel;
    }
}

But now i think that i should've done this in bullet class and done something like this maybe:
1
2
3
4
5
6
7
8
9
10
11
	x1 += xVel1;
    if( ( x1 < 0 ) || ( x1 + SQUARE_WIDTH > BULLET_WIDTH ) )
    {
        alive = false;
    }
    y1 += yVel1;
    if( ( y1 < 0 ) || ( y1 + SQUARE_HEIGHT > BULLET_HEIGHT ) )
    {
        alive = false;
    }
}

Would that work?
I think i might need to get rid of the scrolling background cause i don't think i can fix this problem:
when i go out of a certain area (when the screens starts to scroll) the bullet spawning doesn't work at all, it spawns some distance to the right of my ship and whenever i go fast to try and catch the bullet up, it speeds up!? Why is the ship moving across the background (the level width is 1290 x 960) making the bullet go faster so i can't catch it up and the spawning co-ordinates not working (the bullet going to the right of my screen when spawning)?

Thanks
EDIT: I removed the scrolling background
Last edited on
Just make it seem as though the background is scrolling, just make it an animation.
To make the bullet go up assign yVel = -3; instead of yVel = 3;
Try:
1
2
3
4
5
6
7
Bullet::Bullet()// set initial values for all data members
{
    alive = false;
    x = y = 0;
    xVel = 0;
    yVel = -3;
}


I now see why x += xVel and y += yVel are needed twice.
This expression is always true: x + SQUARE_WIDTH > BULLET_WIDTH so the x -= xVel; always gets executed. Same for the y direction.
Suggest replace with:
1
2
3
4
5
6
7
8
9
10
11
12
13
void Square::move()
{
    x += xVel;
    if( ( x < 0 ) || ( x + SQUARE_WIDTH > LEVEL_WIDTH ) )
    {
        x -= xVel;
    }
    y += yVel;
    if( ( y < 0 ) || ( y + SQUARE_HEIGHT > LEVEL_HEIGHT ) )
    {
        y -= yVel;
    }	
}

This code just keeps the ship from leaving the screen area. It doesn't check for a Bullet-Square collision. To check for a collision the position and width/height of both objects must be in the code. Add another function to the Bullet class for collision testing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Bullet
{
    public:
	int x, y;
	int xVel, yVel;

	bool alive;
	Bullet();
	void handle_input(int xFire, int yFire );
	void move();
	void show();
        void hit( Square target );// new function is here
};

Bullet::hit( Square target )
{
    // check for misses
    if(  x + BULLET_WIDTH < target.x ) return;// bullet missed on left side of target
    if( target.x + SQUARE_WIDTH < x ) return;// bullet misses on right
    if( y > target.y + SQUARE_HEIGHT ) return;// bullet is below ship
    if( y + BULLET_HEIGHT < target.y ) return;// bullet is above ship

    // bullet hits ship
    alive = false;// stop drawing bullet and allow re-fire
}

Add code for a target in main():
1
2
3
4
5
6
7
8
9
10
11
12
int main( int argc, char* args[] )
{
    bool quit = false;
    Square mySquare;

    Square target;// new code for a target
    target.x = 300;// target near screen center
    target.y = 100;// near the top

    Bullet myBullet;
    Timer fps;
    // rest of main code... 

Then add the call to hit() lower in the main code...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ...
// move stuff
mySquare.move();
myBullet.move();

// collision test
myBullet.hit(target);// test if myBullet hits target

// draw stuff
apply_surface( 0, 0, background, screen );
target.show();// draw the target
mySquare.show();
myBullet.show();
// ... 

Did removing the camera code and the scrolling map make things work better? I'm not testing my code. Hopefully I haven't made errors and it works right.

The new code should cause the bullet to hit the target, but the target will never be destroyed. For this the target (Square objects) will need a new member for health, which would decrease on each hit until health <= 0 and then it's dead.
Last edited on
Thanks fun2code,
Using -3 instead of 3 makes the bullet go upwards!
The target code doesn't work though...
I was thinking of making a enemy thats like a target i've got this so far but it's not working, i don't know why...
1
2
3
4
5
6
7
const int ENEMY_HEIGHT = 4;
const int ENEMY_WIDTH = 8;
SDL_Surface *square = NULL;
SDL_Surface *background = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *bullet = NULL;
SDL_Surface *enemy = NULL;


1
2
3
4
5
6
7
8
9
10
class Enemy
{
public:
	bool shipalive;
	Enemy();
	void move();
    void show();
	int x, y;
	int yVel, xVel;
};


1
2
3
4
5
6
7
8
9
10
11
12
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;
}


1
2
3
4
5
6
7
8
void clean_up()
{
    SDL_FreeSurface( square );
    SDL_FreeSurface( background );
	SDL_FreeSurface( bullet );
	SDL_FreeSurface( enemy );
    SDL_Quit();
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Enemy::Enemy()
{
	shipalive = true;
	x = 20;
	y = 15;
	yVel = 0;
	xVel = 0;
}
void Enemy::show()
{
	if(shipalive == true)
	{
            apply_surface( x, y, enemy, screen );
	}
}

	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;
    }
}

And in main...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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;
            }
        }
	   myEnemy.move();
        mySquare.move();
	myBullet.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 don't know why but this code doesn't work, the enemy doesn't show...
It doesn't come up on the screen at all...
I want shipalive to be like bullets alive, when bullet hits the ufo (the enemy)
shipalive is set to false, why isn't this working?

Also removing the camera and the scrolling makes things work perfectly!

EDIT: I removed move() from Enemy and it works! But now i can't really hit it so it's useless...
Last edited on
I'm glad some parts are working better. Sorry the target code didn't. I can't troubleshoot that since I don't know what went wrong. The only problem description I see is "didn't work".

I can tell you why the Enemy wasn't being drawn. shipalive = false; is assigned the 1st time (and every time) Enemy::move() gets called. Can you see why?

Look at this code from your Enemy::move()
1
2
3
4
if( ( x < 0 ) || ( x + ENEMY_WIDTH > BULLET_WIDTH ) )
    {
		shipalive = false;
    }

shipalive = false; will be assigned if either x < 0 or
x + ENEMY_WIDTH > BULLET_WIDTH are true. if x < 0 the enemy won't be drawn.
If x >= 0 the enemy won't be drawn either. Since ENEMY_WIDTH = 8 and
BULLET_WIDTH = 5 the 2nd condition is:
x + 8 > 5 which is true for all x > -3.
shipalive = false; will be assigned every time Enemy::move() is called.
You're writing this code. You should be able to see how it works.
I think you need to study how the c++ language works more thoroughly before continuing with your game writing effort.

The Enemy::move() function can't be performing collision tests against a Bullet because no Bullets position data exists in the function code.

Please take the time to learn the language basics better. There are many good sources for c++ tutorials. This site is good. cprogramming.com has some good ones. Lots of video tutorial series exist online. I prefer studying from books myself (probably because I'm an old guy).
The problem is that you're writing code which you don't understand and can't troubleshoot on your own. Your code just isn't doing what you're thinking it should be doing.

Please post back if you get your code working better!
Yea, just as fun2code said, learn C++ itself better. If you need any help with anything, feel free to PM me with whatever problems you have.
Pages: 123