Tutorial 4 

In the last tutorial-part we added the ball and checked for collisions by iterating over all objects: checking ball with each border and with player-racket. Now we want to use the accelerated checking for collisions via a quadtree, which means that only adjacent objects are checked. To do this, the borders are now created as objects just as player-racket and ball. For our little game this method is certainly oversized, but we want to show the functionality.
At first an overview about modifications in the file pingpong.c.
pingpong.c: Overview about modifications /* global declarations */ [CODEBOX] /* show help-text */ [...] /* check for collisions */ [CODEBOX] /* main function */ int main(int argc, char **argv) { /* variable declaration */ [CODEBOX] /* initializing */ [...] /* opening */ [...] /* set keys */ [CODEBOX] /* create collision tag */ [CODEBOX] /* initialize borders */ [CODEBOX] /* initialize player-racket */ [CODEBOX] /* initialize ball */ [...] /* show help, start background music */ /* game loop */ [...] /* receive input-events and check the local key-strokes */ [CODEBOX] /* check key-strokes for player-racket */ [CODEBOX] /* move ball */ [CODEBOX] /* draw out */ [...] /* flush and wait */ /* end game */ [...] /* destroy and exit */ }
At the beginning of the file pingpong.c we add declarations
for static unique instance-IDs (needed by the collision-functions)
and the collision-tag.
[To Position]
pingpong.c: global declarations /* keys */ struct { int k_quit_lalt; /* part of quit: Left-ALT */ int k_quit_q; /* part of quit: Q */ int k_pause; /* pause */ int k_up; /* move up */ int k_down; /* move down */ } kref; /* instance-IDs for each object-instance */ enum { INSTANCE_PLAYER = 1, INSTANCE_BALL, INSTANCE_BORDER_TOP, INSTANCE_BORDER_BOTTOM, INSTANCE_BORDER_RIGHT }; /* collision-tag */ unsigned int coll_tag;
An overview about the used variables in function main().
We add the border structure.
[To Position]
pingpong.c: the variables in function main() const int border_size = 10; /* size of borders in pixels */ int winw, winh; /* window's width and height */ int audc_hit, audc_gameover, audc_bgmusic; /* audio descriptors */ struct { /* player's structure */ struct VG_Image *imgp; /* image */ struct VG_Rect rect; /* rectangle position */ } player; struct { /* ball's structure */ struct VG_Image *imgp; /* image */ struct VG_RectCent rectc; /* rectangle position */ int angle; /* moving direction angle */ int factor; /* moving velocity in 1/1000 pixels */ } ball; struct { /* border's structure */ struct VG_Image *imgp; /* image */ struct VG_Rect rect; /* rectangle position */ } border[3]; /* helper variables */ struct VG_RectCent *rectcp; int rectc_nr, ipos; struct VG_Position posi;
To use accelerated collisions we need a collision-tag,
which is created here.
[To Position]
pingpong.c: function main() /* create collision-tag */ coll_tag = vg4->collision->create(NULL, 0, 0);
We initialize the three borders, using the structure-array border.
The border-images are created and their rectangles are set according to their position.
Then we insert them into the collision-tag.
[To Position]
pingpong.c: function main() /* initialize borders */ /* top border */ border[0].rect.x = border[0].rect.y = 0; border[0].rect.w = winw; border[0].rect.h = border_size; border[0].imgp = vg4->image->create(border[0].rect.w, border[0].rect.h); vg4->image->fill(border[0].imgp, VG_COLOR_RGB(0xb1, 0, 0)); vg4->collision->insert(coll_tag, INSTANCE_BORDER_TOP, &border[0].rect, 100); /* insert into collision-tag */ /* bottom border */ border[1].rect.x = 0; border[1].rect.y = winh - border_size; border[1].rect.w = winw; border[1].rect.h = border_size; border[1].imgp = vg4->image->create(border[1].rect.w, border[1].rect.h); vg4->image->fill(border[1].imgp, VG_COLOR_RGB(0xb1, 0, 0)); vg4->collision->insert(coll_tag, INSTANCE_BORDER_BOTTOM, &border[1].rect, 100); /* insert into collision-tag */ /* right border */ border[2].rect.x = winw - border_size; border[2].rect.y = 0; border[2].rect.w = border_size; border[2].rect.h = winh; border[2].imgp = vg4->image->create(border[2].rect.w, border[2].rect.h); vg4->image->fill(border[2].imgp, VG_COLOR_RGB(0xb1, 0, 0)); vg4->collision->insert(coll_tag, INSTANCE_BORDER_RIGHT, &border[2].rect, 100); /* insert into collision-tag */
We initialize the player-racket.
Then we insert it into the collision-tag.
[To Position]
pingpong.c: function main() /* initialize player-racket, put it at the left */ /* load image */ player.imgp = vg4->image->load("files/player.bmp"); if (player.imgp == NULL) { VG_dest(); exit(1); } /* set rectangle */ vg4->image->getsize(player.imgp, NULL, &player.rect.w, &player.rect.h); player.rect.x = 0; player.rect.y = (winh - player.rect.h) / 2; /* insert player-racket with 90% of size into collision-tag */ vg4->collision->insert(coll_tag, INSTANCE_PLAYER, &player.rect, 90);
We initialize the ball.
Then we insert it into the collision-tag.
[To Position]
pingpong.c: function main() /* initialize ball, put it at the center of the window */ /* load image */ ball.imgp = vg4->image->load("files/ball.bmp"); if (ball.imgp == NULL) { VG_dest(); exit(1); } /* set rectangle */ vg4->image->getsize(ball.imgp, NULL, &ball.rectc.rect.w, &ball.rectc.rect.h); ball.rectc.rect.x = (winw - ball.rectc.rect.w) / 2; ball.rectc.rect.y = (winh - ball.rectc.rect.h) / 2; ball.rectc.centx = ball.rectc.centy = 0; /* set moving angle and factor */ ball.angle = 45 + 90 * vg4->random->get("ball-angle", 0, 3); /* randomized moving direction */ ball.factor = 3500; /* insert ball with 90% of size into collision-tag */ vg4->collision->insert(coll_tag, INSTANCE_BALL, &ball.rectc.rect, 90);
Now we are in the game-loop.
When moving the player-racket we also check for collisions.
The check for touching a border is removed.
[To Position]
pingpong.c: in the game-loop of the function main() /* +++ player: check for key-strokes +++ */ /* moving up */ if (vg4->input->key_pressed(kref.k_up)) { int y = player.rect.y; player.rect.y -= 2; /* check for collision with ball */ if (check_for_collisions(INSTANCE_PLAYER, &player.rect, &ball.angle)) { /* set back to previous position */ player.rect.y = y; vg4->collision->setpos(coll_tag, INSTANCE_PLAYER, &player.rect, NULL); vg4->audio->play(audc_hit, VG_FALSE, VG_FALSE); } } /* moving down */ if (vg4->input->key_pressed(kref.k_down)) { int y = player.rect.y; player.rect.y += 2; /* check for collision with ball */ if (check_for_collisions(INSTANCE_PLAYER, &player.rect, &ball.angle)) { /* set back to previous position */ player.rect.y = y; vg4->collision->setpos(coll_tag, INSTANCE_PLAYER, &player.rect, NULL); vg4->audio->play(audc_hit, VG_FALSE, VG_FALSE); } }
We also have to move the ball.
We remove the manually checking for collisions with borders
and call check_for_collisions() for checking collisions with all objects.
[To Position]
pingpong.c: in the game-loop of the function main() /* +++ move ball +++ */ /* increase ball-factor for 1/1000 pixel */ ball.factor++; /* get moving steps */ rectc_nr = vg4->misc->moving_one_step(&ball.rectc, ball.angle, ball.factor / 100, &rectcp); /* iterate moving steps and check for collisions */ for (ipos = 0; ipos < rectc_nr; ipos++) { /* player missed the ball? */ if (rectcp[ipos].rect.x + ball.rectc.rect.w <= 0) { /* play audio and wait for its end */ vg4->audio->stop(audc_bgmusic, VG_FALSE); vg4->audio->play(audc_gameover, VG_FALSE, VG_FALSE); while (vg4->audio->is_playing(audc_gameover, NULL)) { vg4->window->flush(); vg4->misc->wait_time(40); } goto endgame; } /* check for collisions */ if (check_for_collisions(INSTANCE_BALL, &rectcp[ipos].rect, &ball.angle)) { /* set back to previous position */ vg4->collision->setpos(coll_tag, INSTANCE_BALL, &ball.rectc.rect, NULL); vg4->audio->play(audc_hit, VG_FALSE, VG_FALSE); break; } /* actualize rectangle position */ ball.rectc = rectcp[ipos]; }
We have all moving done, now we draw out all to the window.
As borders are now objects we replace the vg4->window->draw_rect()
with vg4->window->copy().
[To Position]
pingpong.c: in the game-loop of the function main() /* +++ draw out +++ */ /* clear window */ vg4->window->clear(); /* draw background and borders */ vg4->window->fill(vg4->misc->colorbrightness(VG_COLOR_BLUE, 50)); /* top border */ vg4->window->copy(border[0].imgp, vg4->misc->rect2position(&posi, &border[0].rect), NULL); /* bottom border */ vg4->window->copy(border[1].imgp, vg4->misc->rect2position(&posi, &border[1].rect), NULL); /* right border */ vg4->window->copy(border[2].imgp, vg4->misc->rect2position(&posi, &border[2].rect), NULL); /* draw player */ vg4->window->copy(player.imgp, vg4->misc->rect2position(&posi, &player.rect), NULL); /* draw ball */ vg4->window->copy(ball.imgp, vg4->misc->rect2position(&posi, &ball.rectc.rect), NULL);
At last we replace the function check_for_collision() with another function check_for_collisions(),
which checks the moving object (ball or player-racket) for collisions with any object,
which is contained in the collision-tag.
The angle of the ball will be changed when the ball collides.
[To Position]
pingpong.c: function check_for_collisions() /* check for collisions * @param instanceid instance-ID of moving object * @param moving_rect actual rectangle of moving object * @param ball_angle angle of the ball, will be updated * @return VG_TRUE = collision or VG_FALSE = no collision */ static VG_BOOL check_for_collisions(unsigned int instanceid, const struct VG_Rect *moving_rect, int *ball_angle) { struct VG_Coll *collp; int ipos, canz; VG_BOOL retw; if (instanceid == 0 || moving_rect == NULL || ball_angle == NULL) { return VG_FALSE; } retw = VG_FALSE; /* get collisions */ canz = vg4->collision->setpos(coll_tag, instanceid, moving_rect, &collp); /* iterate collisions */ for (ipos = 0; ipos < canz; ipos++) { if (collp[ipos].type == VG_COLL_TYPE_ENTRY) { /* entering into collision */ /* act according the the moving object */ if (instanceid == INSTANCE_PLAYER) { /* player-racket moves */ if (collp[ipos].instanceid == INSTANCE_BALL) { /* player collides with ball */ /* check collision sides of ball */ if (collp[ipos].side & VG_COLL_SIDE_LEFT) { *ball_angle = 360 - (*ball_angle); *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360; retw = VG_TRUE; } if (collp[ipos].side & (VG_COLL_SIDE_TOP | VG_COLL_SIDE_BOTTOM)) { *ball_angle = 180 - (*ball_angle); *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360; retw = VG_TRUE; } } else if (collp[ipos].instanceid == INSTANCE_BORDER_TOP || collp[ipos].instanceid == INSTANCE_BORDER_BOTTOM) { /* player collides with border */ retw = VG_TRUE; } } else if (instanceid == INSTANCE_BALL) { /* ball moves */ if (collp[ipos].instanceid == INSTANCE_PLAYER) { /* ball collides with player */ /* check collision sides of player */ if (collp[ipos].side & VG_COLL_SIDE_RIGHT) { *ball_angle = 360 - (*ball_angle); *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360; retw = VG_TRUE; } if (collp[ipos].side & (VG_COLL_SIDE_TOP | VG_COLL_SIDE_BOTTOM)) { *ball_angle = 180 - (*ball_angle); *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360; retw = VG_TRUE; } } else if (collp[ipos].instanceid == INSTANCE_BORDER_TOP || collp[ipos].instanceid == INSTANCE_BORDER_BOTTOM) { /* ball collides with top- or bottom-border */ *ball_angle = 180 - (*ball_angle); *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360; retw = VG_TRUE; } else if (collp[ipos].instanceid == INSTANCE_BORDER_RIGHT) { /* ball collides with right border */ *ball_angle = 360 - (*ball_angle); *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360; retw = VG_TRUE; } } } } /* free collision struct */ if (collp != NULL) { free(collp); } return retw; }
<<Previous |
Download ![]() |
Next>> |