Tutorial 2
We create a simple ping-pong game for one player.
Using the keys cursor-up and cursor-down we move the racket.
With space-key we change the color to monochrome and back.
With ALT+Q we exit the game.
When the ball hits a border or the racket a short sound is played.
On exit a game-over sound is played.
- Step 1
We create a skeleton main function.
Object related actions will be put to functions and later explained.
For simplicity with passing parameters to the functions we declare all related variables globally. - Step 1.1
At first do initialisations.#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 int audio_hit, audio_gameover; /* audio descriptors */ int main(int argc, char **argv) { int coldef; /* 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 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");
- Step 1.2
We create the game-loop./* +++ 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);
- Step 1.3
After the game-loop we do the cleaning up./* +++ 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)); endgame: /* close window and exit */ VG3_window_free(wstruct); exit(0); }
- Step 2
Now we have to create the missing functions. - Step 2.1
At first we declare the rest of the global variables.
The structure struct object will hold the informations we need for every object:
- the element img contains the loaded image.
- the element rect contains the position and size of the object, represented by the image.
- the sub-elements of rect:
- - x and y contain the coordinate of the left upper corner
- - w and h contain the width and height
The objects are:
- obj_hborder_up: upper horizontal border
- obj_hborder_down: lower horizontal border
- obj_vborder: right vertical border
- obj_ball: moving ball
- obj_player: player's racket on the left
The variables xball and yball will hold the direction of the moving ball./* 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 functions */ static int create_objects(void); static int move_objects(void); static void draw_objects(void);
- Step 2.2
The function create_objects().
For each object the image is loaded and the initial position is set./* create objects and load audio files */ static int create_objects(void) { /* +++ 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; /* +++ 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; /* +++ 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; /* +++ 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; } /* +++ 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; /* +++ 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.3
The function move_objects().
Each object is moved and checked against other relevant objects:
- borders don't move, no check needed
- the ball moves according to xball and yball and is checked against all objects
- the player moves according to key-strokes and is checked against both horizontal borders
/* move objects and check for interaction with other objects (touching) */ static int move_objects(void) { /* +++ borders don't move +++ */ /* +++ move ball +++ */ /* move 3 pixels */ obj_ball.rect.x += (xball * 3); obj_ball.rect.y += (yball * 3); /* check ball with upper horizontal border */ if (obj_ball.rect.x + obj_ball.rect.w > obj_hborder_up.rect.x && obj_ball.rect.x < obj_hborder_up.rect.x + obj_hborder_up.rect.w) { if (obj_ball.rect.y < obj_hborder_up.rect.y + obj_hborder_up.rect.h) { obj_ball.rect.y = obj_hborder_up.rect.y + obj_hborder_up.rect.h + 1; yball = -yball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } /* check ball with lower horizontal border */ if (obj_ball.rect.x + obj_ball.rect.w > obj_hborder_down.rect.x && obj_ball.rect.x < obj_hborder_down.rect.x + obj_hborder_down.rect.w) { if (obj_ball.rect.y + obj_ball.rect.h > obj_hborder_down.rect.y) { obj_ball.rect.y = obj_hborder_down.rect.y - obj_ball.rect.h; yball = -yball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } /* check ball with right vertical border */ if (obj_ball.rect.y + obj_ball.rect.h > obj_vborder.rect.y && obj_ball.rect.y < obj_vborder.rect.y + obj_vborder.rect.h) { if (obj_ball.rect.x + obj_ball.rect.w > obj_vborder.rect.x) { obj_ball.rect.x = obj_vborder.rect.x - obj_ball.rect.w; xball = -xball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } /* check ball with player */ if (obj_ball.rect.y + obj_ball.rect.h > obj_player.rect.y && obj_ball.rect.y < obj_player.rect.y + obj_player.rect.h) { if (obj_ball.rect.x < obj_player.rect.x + obj_player.rect.w) { obj_ball.rect.x = obj_player.rect.x + obj_player.rect.w; xball = -xball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } /* check ball missing player: game over */ if (obj_ball.rect.x + obj_ball.rect.w - 1 <= 0) { return 0; } /* +++ move player +++ */ if (VG3_key_ispressed(wstruct, VGAG3_KEY_UCURS, VGAG3_IS_PRESSED)) { obj_player.rect.y -= 3; /* move 3 pixels up */ /* check player with upper horizontal border */ if (obj_player.rect.y < obj_hborder_up.rect.y + obj_hborder_up.rect.h) { obj_player.rect.y = obj_hborder_up.rect.y + obj_hborder_up.rect.h; } } if (VG3_key_ispressed(wstruct, VGAG3_KEY_DCURS, VGAG3_IS_PRESSED)) { obj_player.rect.y += 3; /* move 3 pixels down */ /* check player with lower horizontal border */ if (obj_player.rect.y + obj_player.rect.h > obj_hborder_down.rect.y) { obj_player.rect.y = obj_hborder_down.rect.y - obj_player.rect.h; } } return 1; }
- Step 2.4
The function draw_objects().
Each object is drawn onto the window at its new position./* 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); }
- 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 functions */ static int create_objects(void); static int move_objects(void); static void draw_objects(void); int main(int argc, char **argv) { int coldef; /* 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 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)); endgame: /* close window and exit */ VG3_window_free(wstruct); exit(0); } /* create objects and load audio files */ static int create_objects(void) { /* +++ 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; /* +++ 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; /* +++ 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; /* +++ 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; } /* +++ 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; /* +++ 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) { /* +++ borders don't move +++ */ /* +++ move ball +++ */ /* move 3 pixels */ obj_ball.rect.x += (xball * 3); obj_ball.rect.y += (yball * 3); /* check ball with upper horizontal border */ if (obj_ball.rect.x + obj_ball.rect.w > obj_hborder_up.rect.x && obj_ball.rect.x < obj_hborder_up.rect.x + obj_hborder_up.rect.w) { if (obj_ball.rect.y < obj_hborder_up.rect.y + obj_hborder_up.rect.h) { obj_ball.rect.y = obj_hborder_up.rect.y + obj_hborder_up.rect.h + 1; yball = -yball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } /* check ball with lower horizontal border */ if (obj_ball.rect.x + obj_ball.rect.w > obj_hborder_down.rect.x && obj_ball.rect.x < obj_hborder_down.rect.x + obj_hborder_down.rect.w) { if (obj_ball.rect.y + obj_ball.rect.h > obj_hborder_down.rect.y) { obj_ball.rect.y = obj_hborder_down.rect.y - obj_ball.rect.h; yball = -yball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } /* check ball with right vertical border */ if (obj_ball.rect.y + obj_ball.rect.h > obj_vborder.rect.y && obj_ball.rect.y < obj_vborder.rect.y + obj_vborder.rect.h) { if (obj_ball.rect.x + obj_ball.rect.w > obj_vborder.rect.x) { obj_ball.rect.x = obj_vborder.rect.x - obj_ball.rect.w; xball = -xball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } /* check ball with player */ if (obj_ball.rect.y + obj_ball.rect.h > obj_player.rect.y && obj_ball.rect.y < obj_player.rect.y + obj_player.rect.h) { if (obj_ball.rect.x < obj_player.rect.x + obj_player.rect.w) { obj_ball.rect.x = obj_player.rect.x + obj_player.rect.w; xball = -xball; VG3_audio_play(wstruct, audio_hit, 0, 0); /* play hit.wav */ } } /* check ball missing player: game over */ if (obj_ball.rect.x + obj_ball.rect.w - 1 <= 0) { return 0; } /* +++ move player +++ */ if (VG3_key_ispressed(wstruct, VGAG3_KEY_UCURS, VGAG3_IS_PRESSED)) { obj_player.rect.y -= 3; /* move 3 pixels up */ /* check player with upper horizontal border */ if (obj_player.rect.y < obj_hborder_up.rect.y + obj_hborder_up.rect.h) { obj_player.rect.y = obj_hborder_up.rect.y + obj_hborder_up.rect.h; } } if (VG3_key_ispressed(wstruct, VGAG3_KEY_DCURS, VGAG3_IS_PRESSED)) { obj_player.rect.y += 3; /* move 3 pixels down */ /* check player with lower horizontal border */ if (obj_player.rect.y + obj_player.rect.h > obj_hborder_down.rect.y) { obj_player.rect.y = obj_hborder_down.rect.y - obj_player.rect.h; } } 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>> |