Tutorial 3
In the last tutorial we created a single-player ping-pong game.
The check for collisions (ball with borders and player) was done manually,
where the ball after moving was being checked against all relevant objects.
One problem was, that just the target position of the moving ball was checked for collisions.
Theoretically the ball could have been jumped over an object, so that no collision was detected.
Another problem was, that the check had been done against all objects independend of being near or far.
With collision functions these problems can be circumvented.
Using quadtrees just a few number of objects near to the moving object is being checked to save time.
With VG3_coll_q_find() or VG3_coll_check() every step of the moving object is being checked.
We refer to the previous tutorial and modify the ping-pong game to use collision functions.
- Step 1
At first we need a collision-quadtree, we declare it globally as all other variables.
Then in the main() function we create and destroy this collision-quadtree.#include <vgagames3.h> /* for simplicity declare variables globally, which are shared with functions */ static struct vg3_window *wstruct; /* window struct */ static int winw, winh; /* size of window */ static struct object obj_hborder_up, obj_hborder_down, obj_vborder, obj_ball, obj_player; /* objects */ static int xball, yball; /* moving direction of the ball */ static int audio_hit, audio_gameover; /* audio descriptors */ static struct vg3_quadtree *qdtr; /* collision-quadtree */ int main(int argc, char **argv) { int coldef; struct vg3_rect rect; /* open window */ wstruct = VG3_window_new(argv[0], VGAG3_VGAVERSION_LOW, VGAG3_WINSCALE_BESTSCALE); if (wstruct == NULL) { fprintf(stderr, "%s\n", VG3_error()); exit(1); } /* get the size of the window */ VG3_window_getsize(wstruct, &winw, &winh); /* create collision-quadtree for the game-field (i.e. the window) */ rect.x = rect.y = 0; rect.w = winw; rect.h = winh; qdtr = VG3_coll_q_new(&rect, 0, 0); if (qdtr == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } [...] /* clean up */ VG3_image_unload(wstruct, NULL); VG3_audio_unload(wstruct, VG3_audio_group(VGAG3_AUDIO_VOLUME_ALL)); VG3_coll_q_free(qdtr); /* destroy quadtree */ endgame: /* close window and exit */ VG3_window_free(wstruct); exit(0); }
- Step 2
Now we have to modify the relevant functions. - Step 2.1
The function create_objects() has to be modified:
Each object is inserted after creation into the collision-quadtree.
We decide to use the rectangle of the object-image for the collision-rectangle./* create objects and load audio files */ static int create_objects(void) { struct vg3_coll coll; /* +++ create upper horizontal border object +++ */ /* load image */ obj_hborder_up.img = VG3_image_load(wstruct, "hborder.bmp", 0); if (obj_hborder_up.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position */ VG3_image_getsize(wstruct, obj_hborder_up.img, NULL, &obj_hborder_up.rect.w, &obj_hborder_up.rect.h); obj_hborder_up.rect.x = obj_hborder_up.rect.y = 0; /* insert into collision-quadtree */ coll.rect = obj_hborder_up.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "hborder"); /* object-ID = horizontal border */ coll.optr = &obj_hborder_up; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ create lower horizontal border object +++ */ /* load image */ obj_hborder_down.img = VG3_image_load(wstruct, "hborder.bmp", 0); if (obj_hborder_down.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position */ VG3_image_getsize(wstruct, obj_hborder_down.img, NULL, &obj_hborder_down.rect.w, &obj_hborder_down.rect.h); obj_hborder_down.rect.x = 0; obj_hborder_down.rect.y = winh - obj_hborder_down.rect.h; /* insert into collision-quadtree */ coll.rect = obj_hborder_down.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "hborder"); /* object-ID = horizontal border */ coll.optr = &obj_hborder_down; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ create right vertical border object +++ */ /* load image */ obj_vborder.img = VG3_image_load(wstruct, "vborder.bmp", 0); if (obj_vborder.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position */ VG3_image_getsize(wstruct, obj_vborder.img, NULL, &obj_vborder.rect.w, &obj_vborder.rect.h); obj_vborder.rect.x = winw - obj_vborder.rect.w; obj_vborder.rect.y = 0; /* insert into collision-quadtree */ coll.rect = obj_vborder.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "vborder"); /* object-ID = vertical border */ coll.optr = &obj_vborder; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ create ball object +++ */ /* load image */ obj_ball.img = VG3_image_load(wstruct, "ball.bmp", 0); if (obj_ball.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position */ VG3_image_getsize(wstruct, obj_ball.img, NULL, &obj_ball.rect.w, &obj_ball.rect.h); obj_ball.rect.x = (winw - obj_ball.rect.w) / 2; /* x-position centered */ obj_ball.rect.y = VG3_nw_get_random(0, winh - obj_ball.rect.h); /* random y-position */ /* set initial random moving-direction of the ball */ xball = VG3_nw_get_random(1, 2); if (xball == 2) { xball = -1; } yball = VG3_nw_get_random(1, 2); if (yball == 2) { yball = -1; } /* insert into collision-quadtree */ coll.rect = obj_ball.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "ball"); /* object-ID = ball */ coll.optr = &obj_ball; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ create player object +++ */ /* load image */ obj_player.img = VG3_image_load(wstruct, "player.bmp", 0); if (obj_player.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position centered at the left */ VG3_image_getsize(wstruct, obj_player.img, NULL, &obj_player.rect.w, &obj_player.rect.h); obj_player.rect.x = 0; obj_player.rect.y = (winh - obj_player.rect.h) / 2; /* insert into collision-quadtree */ coll.rect = obj_player.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "player"); /* object-ID = player */ coll.optr = &obj_player; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ load audio files +++ */ /* audio: hit border and player */ audio_hit = VG3_audio_load(wstruct, "hit.wav", 100, VGAG3_AUDIO_VOLUME_SOUND); if (audio_hit == 0) { fprintf(stderr, "loading hit.wav: %s\n", VG3_error()); return 0; } /* audio: game over */ audio_gameover = VG3_audio_load(wstruct, "gameover.wav", 100, VGAG3_AUDIO_VOLUME_SOUND); if (audio_gameover == 0) { fprintf(stderr, "loading gameover.wav: %s\n", VG3_error()); return 0; } return 1; /* ok */ }
- Step 2.2
The function move_objects() has to be completely replaced:
At first each object is removed from the collision-quadtree, for not being checked against itself and to be inserted later at the new position.
Then the moving way is being checked step by step with VG3_coll_q_find(), returning an array of collisions./* move objects and check for interaction with other objects (touching) */ static int move_objects(void) { struct vg3_rect nrect; struct vg3_coll *pcoll, coll; int icoll; /* +++ borders don't move +++ */ /* +++ move ball +++ */ /* first remove from collision-quadtree */ VG3_coll_q_remove(qdtr, &obj_ball); /* set new position after possible moving */ nrect = obj_ball.rect; nrect.x += (xball * 3); nrect.y += (yball * 3); /* check on the way from actual position to new position for collisions */ icoll = VG3_coll_q_find(qdtr, &obj_ball.rect, &nrect, &pcoll); if (icoll < 0) { fprintf(stderr, "%s\n", VG3_error()); return 0; } if (icoll > 0) { /* collisions found, we just take the first one */ /* there are two possibilies: * - check for pcoll[0].oid: * "hborder" = revert yball * "vborder" = revert xball * - check for pcoll[0].ret.side: * VGAG3_COLLSIDE_RIGHT | VGAG3_COLLSIDE_LEFT = revert xball * VGAG3_COLLSIDE_TOP | VGAG3_COLLSIDE_BOTTOM = revert yball */ if (pcoll[0].ret.side & (VGAG3_COLLSIDE_RIGHT | VGAG3_COLLSIDE_LEFT)) { xball = -xball; } if (pcoll[0].ret.side & (VGAG3_COLLSIDE_TOP | VGAG3_COLLSIDE_BOTTOM)) { yball = -yball; } nrect = pcoll[0].ret.pos_nocoll; /* set to new position (before collision) */ free(pcoll); VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } obj_ball.rect = nrect; /* set to new position */ /* check ball missing player: game over */ if (obj_ball.rect.x + obj_ball.rect.w - 1 <= 0) { return 0; } /* insert into collision-quadtree again */ coll.rect = obj_ball.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "ball"); /* object-ID = ball */ coll.optr = &obj_ball; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ move player +++ */ /* first remove from collision-quadtree */ VG3_coll_q_remove(qdtr, &obj_player); /* check for key-strokes and set new possible position */ nrect = obj_player.rect; if (VG3_key_ispressed(wstruct, VGAG3_KEY_UCURS, VGAG3_IS_PRESSED)) { nrect.y -= 3; /* move 3 pixels up */ } if (VG3_key_ispressed(wstruct, VGAG3_KEY_DCURS, VGAG3_IS_PRESSED)) { nrect.y += 3; /* move 3 pixels down */ } /* check on the way from actual position to new position for collisions */ icoll = VG3_coll_q_find(qdtr, &obj_player.rect, &nrect, &pcoll); if (icoll < 0) { fprintf(stderr, "%s\n", VG3_error()); return 0; } if (icoll > 0) { /* collisions found, we check all collisions until being stopped */ int inr, steps = 0; for (inr = 0; inr < icoll; inr++) { if (steps > 0 && pcoll[inr].ret.steps > steps) { break; } /* collision beyond the stopping one */ if (strcmp(pcoll[inr].oid, "hborder") == 0) { /* horizontal border stops moving */ nrect = pcoll[inr].ret.pos_nocoll; /* set to new position (before collision) */ steps = pcoll[inr].ret.steps; } else if (strcmp(pcoll[inr].oid, "ball") == 0) { /* push ball back */ xball = -xball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } free(pcoll); } obj_player.rect = nrect; /* set to new position */ /* insert into collision-quadtree again */ coll.rect = obj_player.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "player"); /* object-ID = player */ coll.optr = &obj_player; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); return 1; }
- Putting together
Finally we got the following file (main.c):#include <vgagames3.h> /* structure for objects: borders, ball, player */ struct object { struct vg3_image *img; /* loaded image */ struct vg3_rect rect; /* position and size */ }; /* for simplicity declare variables globally, which are shared with functions */ static struct vg3_window *wstruct; /* window struct */ static int winw, winh; /* size of window */ static struct object obj_hborder_up, obj_hborder_down, obj_vborder, obj_ball, obj_player; /* objects */ static int xball, yball; /* moving direction of the ball */ static int audio_hit, audio_gameover; /* audio descriptors */ static struct vg3_quadtree *qdtr; /* collision-quadtree */ /* static functions */ static int create_objects(void); static int move_objects(void); static void draw_objects(void); int main(int argc, char **argv) { int coldef; struct vg3_rect rect; /* open window */ wstruct = VG3_window_new(argv[0], VGAG3_VGAVERSION_LOW, VGAG3_WINSCALE_BESTSCALE); if (wstruct == NULL) { fprintf(stderr, "%s\n", VG3_error()); exit(1); } /* get the size of the window */ VG3_window_getsize(wstruct, &winw, &winh); /* create collision-quadtree for the game-field (i.e. the window) */ rect.x = rect.y = 0; rect.w = winw; rect.h = winh; qdtr = VG3_coll_q_new(&rect, 0, 0); if (qdtr == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* create objects */ if (!create_objects()) { goto endgame; } /* set initial color definition for window */ coldef = VGAG3_COLORDEF_DEFAULT; /* print infos */ printf("\n"); printf("Keys:\n"); printf("- cursor up, cursor down: move\n"); printf("- spacebar: color change\n"); printf("- ALT+Q: exit\n"); /* +++ game-loop +++ */ VG3_discard_input(wstruct); for (;;) { if (VG3_inputevent_update(wstruct) > 0) { break; } /* ALT+Q: exit */ if (VG3_key_ispressed(wstruct, VGAG3_KEY_Q, VGAG3_IS_NEW_PRESSED) && VG3_key_ispressed(wstruct, VGAG3_KEY_LALT, VGAG3_IS_PRESSED)) { break; } /* change color to monochrome and vice versa? */ if (VG3_key_ispressed(wstruct, VGAG3_KEY_SPACE, VGAG3_IS_NEW_PRESSED)) { if (coldef == VGAG3_COLORDEF_DEFAULT) { coldef = VGAG3_COLORDEF_GREY; } else { coldef = VGAG3_COLORDEF_DEFAULT; } VG3_window_attributes(wstruct, NULL, NULL, coldef, NULL); } /* clear window with halfdark blue background */ VG3_draw_clear(wstruct, NULL, VG3_color_brightness(VGAG3_COLOR_BLUE, 50)); /* move objects and check for interaction with other objects (touching) */ if (!move_objects()) { break; } /* draw objects onto the window */ draw_objects(); /* draw out window-contents */ VG3_window_update(wstruct, 0, 0); /* sleep until 30 msec are gone for this game-loop */ VG3_wait_time(30); } VG3_discard_input(wstruct); /* +++ game over +++ */ /* play gameover.wav and wait for finishing */ VG3_audio_play(wstruct, audio_gameover, 0, 0); while (VG3_audio_isplaying(wstruct, audio_gameover)) { VG3_wait_time(100); } /* clean up */ VG3_image_unload(wstruct, NULL); VG3_audio_unload(wstruct, VG3_audio_group(VGAG3_AUDIO_VOLUME_ALL)); VG3_coll_q_free(qdtr); /* destroy quadtree */ endgame: /* close window and exit */ VG3_window_free(wstruct); exit(0); } /* create objects and load audio files */ static int create_objects(void) { struct vg3_coll coll; /* +++ create upper horizontal border object +++ */ /* load image */ obj_hborder_up.img = VG3_image_load(wstruct, "hborder.bmp", 0); if (obj_hborder_up.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position */ VG3_image_getsize(wstruct, obj_hborder_up.img, NULL, &obj_hborder_up.rect.w, &obj_hborder_up.rect.h); obj_hborder_up.rect.x = obj_hborder_up.rect.y = 0; /* insert into collision-quadtree */ coll.rect = obj_hborder_up.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "hborder"); /* object-ID = horizontal border */ coll.optr = &obj_hborder_up; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ create lower horizontal border object +++ */ /* load image */ obj_hborder_down.img = VG3_image_load(wstruct, "hborder.bmp", 0); if (obj_hborder_down.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position */ VG3_image_getsize(wstruct, obj_hborder_down.img, NULL, &obj_hborder_down.rect.w, &obj_hborder_down.rect.h); obj_hborder_down.rect.x = 0; obj_hborder_down.rect.y = winh - obj_hborder_down.rect.h; /* insert into collision-quadtree */ coll.rect = obj_hborder_down.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "hborder"); /* object-ID = horizontal border */ coll.optr = &obj_hborder_down; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ create right vertical border object +++ */ /* load image */ obj_vborder.img = VG3_image_load(wstruct, "vborder.bmp", 0); if (obj_vborder.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position */ VG3_image_getsize(wstruct, obj_vborder.img, NULL, &obj_vborder.rect.w, &obj_vborder.rect.h); obj_vborder.rect.x = winw - obj_vborder.rect.w; obj_vborder.rect.y = 0; /* insert into collision-quadtree */ coll.rect = obj_vborder.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "vborder"); /* object-ID = vertical border */ coll.optr = &obj_vborder; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ create ball object +++ */ /* load image */ obj_ball.img = VG3_image_load(wstruct, "ball.bmp", 0); if (obj_ball.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position */ VG3_image_getsize(wstruct, obj_ball.img, NULL, &obj_ball.rect.w, &obj_ball.rect.h); obj_ball.rect.x = (winw - obj_ball.rect.w) / 2; /* x-position centered */ obj_ball.rect.y = VG3_nw_get_random(0, winh - obj_ball.rect.h); /* random y-position */ /* set initial random moving-direction of the ball */ xball = VG3_nw_get_random(1, 2); if (xball == 2) { xball = -1; } yball = VG3_nw_get_random(1, 2); if (yball == 2) { yball = -1; } /* insert into collision-quadtree */ coll.rect = obj_ball.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "ball"); /* object-ID = ball */ coll.optr = &obj_ball; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ create player object +++ */ /* load image */ obj_player.img = VG3_image_load(wstruct, "player.bmp", 0); if (obj_player.img == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* get width and height of image and set position centered at the left */ VG3_image_getsize(wstruct, obj_player.img, NULL, &obj_player.rect.w, &obj_player.rect.h); obj_player.rect.x = 0; obj_player.rect.y = (winh - obj_player.rect.h) / 2; /* insert into collision-quadtree */ coll.rect = obj_player.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "player"); /* object-ID = player */ coll.optr = &obj_player; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ load audio files +++ */ /* audio: hit border and player */ audio_hit = VG3_audio_load(wstruct, "hit.wav", 100, VGAG3_AUDIO_VOLUME_SOUND); if (audio_hit == 0) { fprintf(stderr, "loading hit.wav: %s\n", VG3_error()); return 0; } /* audio: game over */ audio_gameover = VG3_audio_load(wstruct, "gameover.wav", 100, VGAG3_AUDIO_VOLUME_SOUND); if (audio_gameover == 0) { fprintf(stderr, "loading gameover.wav: %s\n", VG3_error()); return 0; } return 1; /* ok */ } /* move objects and check for interaction with other objects (touching) */ static int move_objects(void) { struct vg3_rect nrect; struct vg3_coll *pcoll, coll; int icoll; /* +++ borders don't move +++ */ /* +++ move ball +++ */ /* first remove from collision-quadtree */ VG3_coll_q_remove(qdtr, &obj_ball); /* set new position after possible moving */ nrect = obj_ball.rect; nrect.x += (xball * 3); nrect.y += (yball * 3); /* check on the way from actual position to new position for collisions */ icoll = VG3_coll_q_find(qdtr, &obj_ball.rect, &nrect, &pcoll); if (icoll < 0) { fprintf(stderr, "%s\n", VG3_error()); return 0; } if (icoll > 0) { /* collisions found, we just take the first one */ /* there are two possibilies: * - check for pcoll[0].oid: * "hborder" = revert yball * "vborder" = revert xball * - check for pcoll[0].ret.side: * VGAG3_COLLSIDE_RIGHT | VGAG3_COLLSIDE_LEFT = revert xball * VGAG3_COLLSIDE_TOP | VGAG3_COLLSIDE_BOTTOM = revert yball */ if (pcoll[0].ret.side & (VGAG3_COLLSIDE_RIGHT | VGAG3_COLLSIDE_LEFT)) { xball = -xball; } if (pcoll[0].ret.side & (VGAG3_COLLSIDE_TOP | VGAG3_COLLSIDE_BOTTOM)) { yball = -yball; } nrect = pcoll[0].ret.pos_nocoll; /* set to new position (before collision) */ free(pcoll); VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } obj_ball.rect = nrect; /* set to new position */ /* check ball missing player: game over */ if (obj_ball.rect.x + obj_ball.rect.w - 1 <= 0) { return 0; } /* insert into collision-quadtree again */ coll.rect = obj_ball.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "ball"); /* object-ID = ball */ coll.optr = &obj_ball; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); /* +++ move player +++ */ /* first remove from collision-quadtree */ VG3_coll_q_remove(qdtr, &obj_player); /* check for key-strokes and set new possible position */ nrect = obj_player.rect; if (VG3_key_ispressed(wstruct, VGAG3_KEY_UCURS, VGAG3_IS_PRESSED)) { nrect.y -= 3; /* move 3 pixels up */ } if (VG3_key_ispressed(wstruct, VGAG3_KEY_DCURS, VGAG3_IS_PRESSED)) { nrect.y += 3; /* move 3 pixels down */ } /* check on the way from actual position to new position for collisions */ icoll = VG3_coll_q_find(qdtr, &obj_player.rect, &nrect, &pcoll); if (icoll < 0) { fprintf(stderr, "%s\n", VG3_error()); return 0; } if (icoll > 0) { /* collisions found, we check all collisions until being stopped */ int inr, steps = 0; for (inr = 0; inr < icoll; inr++) { if (steps > 0 && pcoll[inr].ret.steps > steps) { break; } /* collision beyond the stopping one */ if (strcmp(pcoll[inr].oid, "hborder") == 0) { /* horizontal border stops moving */ nrect = pcoll[inr].ret.pos_nocoll; /* set to new position (before collision) */ steps = pcoll[inr].ret.steps; } else if (strcmp(pcoll[inr].oid, "ball") == 0) { /* push ball back */ xball = -xball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } free(pcoll); } obj_player.rect = nrect; /* set to new position */ /* insert into collision-quadtree again */ coll.rect = obj_player.rect; /* collision rectangle equals to image rectangle */ snprintf(coll.oid, sizeof(coll.oid), "player"); /* object-ID = player */ coll.optr = &obj_player; /* pointer to object */ VG3_coll_q_insert(qdtr, &coll); return 1; } /* draw objects onto the window */ static void draw_objects(void) { /* +++ draw borders +++ */ /* upper horizontal border */ VG3_image_copy(wstruct, NULL, obj_hborder_up.img, obj_hborder_up.rect.x + obj_hborder_up.rect.w / 2, obj_hborder_up.rect.y + obj_hborder_up.rect.h / 2, NULL, 0); /* lower horizontal border */ VG3_image_copy(wstruct, NULL, obj_hborder_down.img, obj_hborder_down.rect.x + obj_hborder_down.rect.w / 2, obj_hborder_down.rect.y + obj_hborder_down.rect.h / 2, NULL, 0); /* right vertical border */ VG3_image_copy(wstruct, NULL, obj_vborder.img, obj_vborder.rect.x + obj_vborder.rect.w / 2, obj_vborder.rect.y + obj_vborder.rect.h / 2, NULL, 0); /* +++ draw ball +++ */ VG3_image_copy(wstruct, NULL, obj_ball.img, obj_ball.rect.x + obj_ball.rect.w / 2, obj_ball.rect.y + obj_ball.rect.h / 2, NULL, 0); /* +++ draw player +++ */ VG3_image_copy(wstruct, NULL, obj_player.img, obj_player.rect.x + obj_player.rect.w / 2, obj_player.rect.y + obj_player.rect.h / 2, NULL, 0); }
- Running
Running the game
<<Prev | Top | Next>> |