Tutorial 5
Now we want to modify our ping-pong game using VgaGames3-objects from tutorial 4 to play with two players over network connection.
On the left we will find our well-known green player-racket.
On the right the vertical border is cancelled and replaced by a new lila player-racket.
As playing over network just sends key-strokes and otherwise every client operates autarkically we don't have really much to change.
It is important to open the network connection before generating random numbers using VG3_nw_get_random().
So we will finish the connection before creating the object-instances.
One player is the master-client, all others are normal clients.
The master-client normally starts the network-server and is only distinguished from the others when connecting, after that there will be no difference any more.
- Step 1
The most has to be done in the main-program.
- Step 1.1
The individual game structure will get more elements:
- two integers containing the keyboard keys for moving the racket up and down - a pointer to the network structure
Differences green-highlighted in main.h:/* individual game structure */ struct game { struct vg3_window *wstruct; /* window structure */ int winw, winh; /* size of window */ struct vg3_ofunc *ofstruct; /* common VgaGames3-object structure */ struct vg3_quadtree *qdtr; /* collision-quadtree */ int key_up, key_down; /* keyboard keys for moving the player-racket */ struct vg3_nwclient *nwclnt; /* network structure */ int endgame; /* flag to end the game */ };
- Step 1.2
In the main-program we
- use a programm parameter to distinguish between master-client and client
- insert a code-block for (starting and) connecting to the network-server
- create two object-instances for the player instead of one as before
- remove the creation of a vertical border instance
- replace the function VG3_inputevent_update() with VG3_nw_update()
- call the function VG3_wait_time() on condition
- close the network connection when cleaning up
Differences green-highlighted in main.c:
Use a programm parameter to distinguish between master-client and client
int main(int argc, char **argv) { struct game game; const struct vg3_ofunc_objfunc *ofc; struct vg3_rect rect; int master, dowait; /* program parameter: * -m: master-client * -s: client */ if (argc > 1 && strcmp(argv[1], "-m") == 0) { master = 1; } else if (argc > 1 && strcmp(argv[1], "-s") == 0) { master = 0; } else { fprintf(stderr, "Usage:\n"); fprintf(stderr, " %s -m: master-client\n", argv[0]); fprintf(stderr, " %s -s: client\n", argv[0]); exit(1); } /* +++ set game structure +++ */
Insert a code-block for (starting and) connecting to the network-server
Create two object-instances for the player instead of one as before
Remove the creation of a vertical border instance
/* create collision-quadtree with size of window */ rect.x = rect.y = 0; rect.w = game.winw; rect.h = game.winh; game.qdtr = VG3_coll_q_new(&rect, 0, 0); if (game.qdtr == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* define keyboard keys */ game.key_up = VGAG3_KEY_UCURS; game.key_down = VGAG3_KEY_DCURS; game.nwclnt = NULL; game.endgame = 0; /* +++ (start and) connect to network-server +++ */ { int clnr; char *nwserverip, nwservername[64]; struct vg3_rect rect; struct vg3_text stxt; /* clear window and draw a centered text */ VG3_draw_clear(game.wstruct, NULL, VGAG3_COLOR_BLACK); VGAG3_TEXT_ATTRIBUTES_SET(&stxt, NULL, '\n', 0, "Connecting to server ..."); rect.x = 0; rect.w = game.winw; rect.y = 0; rect.h = game.winh; rect = VG3_draw_text(game.wstruct, NULL, &rect, ' ', &stxt, VGAG3_COLOR_YELLOW, VGAG3_COLOR_BLUE, 1); rect.x = (game.winw - rect.w) / 2; rect.y = (game.winh - rect.h) / 2; VG3_draw_text(game.wstruct, NULL, &rect, ' ', &stxt, VGAG3_COLOR_YELLOW, VGAG3_COLOR_BLUE, 0); VG3_window_update(game.wstruct, 0, 0); /* if master-client, start multicast-/broadcast-server and network-server */ if (master) { VG3_nw_mbcast_start(); VG3_nw_server(NULL); } /* get ip-address of network-server via multicast/broadcast */ nwserverip = VG3_nw_mbcast_getip(nwservername, sizeof(nwservername)); if (nwserverip == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } printf("\n"); printf("Network-server: name=%s, ip-address=%s\n", nwservername, nwserverip); /* open network */ game.nwclnt = VG3_nw_open(game.wstruct); /* define network-keys */ VG3_nw_addkey(game.nwclnt, 0, game.key_up); VG3_nw_addkey(game.nwclnt, 0, game.key_down); /* connect to network-server with default port and name = hostname * and return own client-number, * we only want two players to connect */ clnr = VG3_nw_connect(game.nwclnt, nwserverip, NULL, NULL, 2, &master); if (clnr < 0) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* error */ if (clnr == 0) { goto endgame; } /* exit-request: close window */ /* if master-client, stop multicast-/broadcast-server */ if (master) { VG3_nw_mbcast_stop(); } /* clean up */ free(nwserverip); } /* +++ create VgaGames3-objects +++ */ /* create players: for each network player create a player-instance, pass its client-number */ { int clmax, clnr; ofc = VG3_ofunc_get_objfunc(game.ofstruct, OID_PLAYER); if (ofc == NULL) { fprintf(stderr, "Player-object not found\n"); goto endgame; } clmax = VG3_nw_numberofclients(game.nwclnt); /* get highest client-number */ for (clnr = 1; clnr <= clmax; clnr++) { if (ofc->f_new(&game, 0, clnr) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } } } /* create borders */ ofc = VG3_ofunc_get_objfunc(game.ofstruct, OID_BORDER); if (ofc == NULL) { fprintf(stderr, "Border-object not found\n"); goto endgame; } /* upper horizontal border */ if (ofc->f_new(&game, 0, 1) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* lower horizontal border */ if (ofc->f_new(&game, 0, 2) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* vertical border */ if (ofc->f_new(&game, 0, 3) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* create ball */ ofc = VG3_ofunc_get_objfunc(game.ofstruct, OID_BALL);
Replace the function VG3_inputevent_update() with VG3_nw_update()
/* +++ game-loop +++ */ VG3_discard_input(game.wstruct); for (;;) { if (VG3_nw_update(game.nwclnt, &dowait)) { break; } /* ALT+Q: exit */
Call the function VG3_wait_time() on condition
/* draw out window-contents */ VG3_window_update(game.wstruct, 0, 0); /* sleep until 30 msec are gone for this game-loop */ if (dowait) { VG3_wait_time(30); } /* if end game is requested, quit */
Close the network connection when cleaning up
/* free main-struct for object-functions */ VG3_ofunc_free(game.ofstruct); /* close network connection */ VG3_nw_close(game.nwclnt); endgame: /* close window and exit */
- Step 2
We modify the player-object, because we now pass the network-client-number.
- Step 2.1
We insert the client-number into the private structure for the player object.
Differences green-highlighted in obj-player.h:/* private structure for player object */ struct o_player { struct vg3_image *img; /* loaded image */ struct vg3_rect rect; /* position and size */ int xdelta, ydelta; /* for moving: 1/100 pixels steps for x- and y-direction */ int xremainder, yremainder; /* for moving: remainders for x- and y-moving */ int clnr; /* network-client number */ };
- Step 2.2
In the player-object we
- modify the function f_new() to distinguish between two players
- modify the function f_run() to check whether the player-instance is still connected
- modify the function f_run() to replace VG3_key_ispressed() with VG3_nw_key_ispressed()
Differences green-highlighted in obj-player.c:
Modify the function f_new() to distinguish between two players
/* +++ set private structure +++ */ /* with the variadic parameters we pass the network-client number */ va_start(ap, parent_objid); gobj->clnr = va_arg(ap, int); va_end(ap); /* load image read-only (dependend of player: player 1 = green racket, player 2 = lila racket) */ if (gobj->clnr == 1) { gobj->img = VG3_image_load(game->wstruct, "player-green.bmp", 1); } else { gobj->img = VG3_image_load(game->wstruct, "player-lila.bmp", 1); } if (gobj->img == NULL) { return NULL; } /* get width and height of image and set position centered dependend of player */ VG3_image_getsize(game->wstruct, gobj->img, NULL, &gobj->rect.w, &gobj->rect.h); if (gobj->clnr == 1) { gobj->rect.x = 0; /* at the left */ } else { gobj->rect.x = game->winw - gobj->rect.w; /* at the right */ } gobj->rect.y = (game->winh - gobj->rect.h) / 2; /* initalize moving direction */
Modify the function f_run() to check whether the player-instance is still connected
Modify the function f_run() to replace VG3_key_ispressed() with VG3_nw_key_ispressed()
/* get private structure */ gobj = (struct o_player *)objp->ostruct; /* check whether client is still connected; * if not destroy it because it isn't needed any more */ if (VG3_nw_getclientinfo(game->nwclnt, gobj->clnr, NULL) == 0) { f_free(vgame, objp); return; } /* remove player object-instance from collision-quadtree */ VG3_coll_q_remove(game->qdtr, objp); /* +++ check for key-strokes and set moving direction +++ */ gobj->ydelta = 0; if (VG3_nw_key_ispressed(game->nwclnt, gobj->clnr, 0, game->key_up, VGAG3_IS_PRESSED)) { gobj->ydelta = -100; /* set moving upwards: full y-direction, no x-direction */ } if (VG3_nw_key_ispressed(game->nwclnt, gobj->clnr, 0, game->key_down, VGAG3_IS_PRESSED)) { gobj->ydelta = 100; /* set moving downwards: full y-direction, no x-direction */ } /* +++ move player according to moving direction and check for collisions +++ */
- Step 3
We modify the ball-object, just checking for ending game also if the right player misses the ball.
Differences green-highlighted in obj-ball.c:/* +++ check for leaving the window => end game +++ */ if (gobj->rect.x + gobj->rect.w - 1 <= 0 || gobj->rect.x >= game->winw) { /* one of the players missed ball: end game */ game->endgame = 1; return; } /* +++ insert into collision-quadtree again +++ */
- Putting together
See the files in example-games/tutorial5/ - Running
Running the game
<<Prev | Top | Next>> |