Pipes, Collision & Game Over
Observations:
- They are scrolling from left to right.
- There is a max. of 6 pipes at the same time.
- The distance between each set of pipes will be constant.
- The height of the opening varies, but the size of the opening does not.
- Move the column vertically to a random Y-Position when we reset its X-Position.
Those are the pipes from the sprite atlas, afaik those red pipes weren't used.
Since I started with day/night selection of the background, I will implement the night pipes also.
We just need one of each color and just flip it while drawing.
One also needs some properties from the observations: I started with some rules of thumb while implementing. So the numbers of the properties came by testing the code.
Actually after playing around I would have gotten away with 4 pipes, but in case you want to experiment with those, there you go.
First I will set the game state back to playing game_state = GS_PLAYING;
,
since the pipes are shown there. Then the preprocessor variables will be defined.
After that the globals.
In this section I will set the source and destination rectangle for the pipes.
#define PIPES 6
#define PIPE_START 600
#define PIPE_OPEN 100
#define PIPE_MIN_Y 210 // constrains the minimum top of a pipe
#define PIPE_MAX_Y 370 // constrains the maximum top of a pipe
#define PIPE_X_OFFSET 180 // offset between pipes
#define PIPE_STEP 2
// globals
SDL_Rect pipe_src; // need one source rectangle for the pipes
SDL_Rect pipe_dst[PIPES]; // but as many dest-rests as max. shown
// playing_set
// select day or night pipe by background
if (bg_src.x==0){
pipe_src.w=52;
pipe_src.h=320;
pipe_src.x=168;
pipe_src.y=646;
} else {
pipe_src.w=52;
pipe_src.h=320;
pipe_src.x=56;
pipe_src.y=646;
}
// init pipe dest rects
int i;
for (i = 0; i < PIPES; i++)
{
pipe_dst[i].w = pipe_src.w;
pipe_dst[i].h = pipe_src.h;
}
playing set
pipe_dst[0].x = PIPE_START;
pipe_dst[0].y = rand() % (PIPE_MAX_Y + 1 - PIPE_MIN_Y) + PIPE_MIN_Y;
pipe_dst[1].x = PIPE_START;
pipe_dst[1].y = pipe_dst[0].y - (pipe_src.h + PIPE_OPEN);
pipe_hit_dst[0].x = pipe_dst[0].x + pipe_src.w / 2;
pipe_hit_dst[0].y = pipe_dst[0].y - PIPE_OPEN;
pipe_dst[2].x = PIPE_START + PIPE_X_OFFSET;
pipe_dst[2].y = rand() % (PIPE_MAX_Y + 1 - PIPE_MIN_Y) + PIPE_MIN_Y;
pipe_dst[3].x = PIPE_START + PIPE_X_OFFSET;
pipe_dst[3].y = pipe_dst[2].y - (pipe_src.h + PIPE_OPEN);
pipe_hit_dst[1].x = pipe_dst[2].x + pipe_src.w / 2;
pipe_hit_dst[1].y = pipe_dst[2].y - PIPE_OPEN;
pipe_dst[4].x = PIPE_START + PIPE_X_OFFSET * 2;
pipe_dst[4].y = rand() % (PIPE_MAX_Y + 1 - PIPE_MIN_Y) + PIPE_MIN_Y;
pipe_dst[5].x = PIPE_START + PIPE_X_OFFSET * 2;
pipe_dst[5].y = pipe_dst[4].y - (pipe_src.h + PIPE_OPEN);
pipe_hit_dst[2].x = pipe_dst[4].x + pipe_src.w / 2;
pipe_hit_dst[2].y = pipe_dst[4].y - PIPE_OPEN;
//END SET pipe
Finally one needs to call playing_set(); in the region init, else all rectangles will be uninitialized und therefore won't be rendered.
//BEGIN UPDATE pipes
for (i = 0; i < PIPES; i += 2)
{
pipe_dst[i].x -= 1 + PIPE_STEP;
pipe_dst[i + 1].x -= 1 + PIPE_STEP;
if (pipe_dst[i].x + pipe_dst[i].w < 0)
{
if (i == 0)
{
pipe_dst[0].x = pipe_dst[4].x + PIPE_X_OFFSET;
pipe_dst[0].y = rand() % (PIPE_MAX_Y + 1 - PIPE_MIN_Y) + PIPE_MIN_Y;
pipe_dst[1].x = pipe_dst[i].x;
pipe_dst[1].y = pipe_dst[i].y - (pipe_src.h + PIPE_OPEN);
}
if (i == 2)
{
pipe_dst[2].x = pipe_dst[0].x + PIPE_X_OFFSET;
pipe_dst[2].y = rand() % (PIPE_MAX_Y + 1 - PIPE_MIN_Y) + PIPE_MIN_Y;
pipe_dst[3].x = pipe_dst[i].x;
pipe_dst[3].y = pipe_dst[i].y - (pipe_src.h + PIPE_OPEN);
}
if (i == 4)
{
pipe_dst[4].x = pipe_dst[2].x + PIPE_X_OFFSET;
pipe_dst[4].y = rand() % (PIPE_MAX_Y + 1 - PIPE_MIN_Y) + PIPE_MIN_Y;
pipe_dst[5].x = pipe_dst[i].x;
pipe_dst[5].y = pipe_dst[i].y - (pipe_src.h + PIPE_OPEN);
}
}
}
//END UPDATE pipes
In playing draw
int i;
for (i = 0; i < PIPES; i++)
{
if (i % 2)
SDL_RenderCopyEx(Renderer, Texture, &pipe_src, &pipe_dst[i], 0, NULL, SDL_FLIP_VERTICAL);
else
SDL_RenderCopy(Renderer, Texture, &pipe_src, &pipe_dst[i]);
}
./3
I am using the dest rects of the pipes to check for collision.
SDL_HasIntersection
does that for me, just throw 2 addresses in.
// Check collision of Pipes and bird
for (i = 0; i < PIPES; i++)
{
if (SDL_HasIntersection(&bird_dst, &pipe_dst[i]))
{
game_over_set();
game_state = GS_OVER;
}
}
Now that we have a lost/end of game trigger, we can switch over to the game over state.
global:
SDL_Rect over_src; // Game Over Word
SDL_Rect over_dst; // Game Over Word
Call game_over_set();
in the init region.
game_over_set
void game_over_set(void)
{
// Game over literals
over_src.w = 197;
over_src.h = 52;
over_src.x = 790;
over_src.y = 118;
over_dst.w = over_src.w;
over_dst.h = over_src.h;
over_dst.x = (ww / 2) - (over_dst.w / 2);
over_dst.y = 130;
}
game_over_update
update_bird();
game_over_Draw();
game_over_draw
void game_over_Draw(void)
{
playing_draw();
SDL_RenderCopy(Renderer, atlas, &over_src, &over_dst);
SDL_RenderCopy(Renderer, atlas, &play_src, &play_dst);
SDL_RenderCopy(Renderer, atlas, &lb_src, &lb_dst);
SDL_RenderCopy(Renderer, atlas, &rate_src, &rate_dst);
}
./3a
That is it. We have a prototypal implementation of all states. The bird is falling through pipes when it died between the pipes.
I will refactor set states to split it in init & set, it is not necessary to init most of the source rects which each set. I will initialize static source rects in assets_in
. But that is a really tiny issue, more like optimization.
Then I will implement the score and the highest score. The score will be rendered with pixel mapping to the atlas.