VgaGames4 - tutorial

Tutorial 3 

screenshot.gif

In this tutorial-part we just add the moving ball and check for collision of it with the player-racket and the borders.


At first an overview about modifications in the file pingpong.c.

pingpong.c: Overview about modifications

/* global declarations */
[...]

/* show help-text */
[...]

/* check for collisions */
[CODEBOX]

/* main function */
int main(int argc, char **argv) {
  /* variable declaration */
  [CODEBOX]

  /* initializing */
  [CODEBOX]  /* opening */
  [...]      /* set keys */
  [...]      /* 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 */
}

An overview about the used variables in function main(). We add two audio descriptors and the ball 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;

/* helper variables */
struct VG_Rect rect;
struct VG_RectCent *rectcp;
int rectc_nr, ipos;
struct VG_Position posi;

We open the window, deactivate mouse grabbing, open the audio system and load the audio-files, adding one for ball's collision and one for game-over.

[To Position]
pingpong.c: function main()

/* initialize and open window */
if (!VG_init("VgaGames4 tutorial 3")) { exit(1); }
if (!vg4->window->open(VG_WINDOW_SIZE_LOW, VG_WINDOW_SCALE_BEST)) { VG_dest(); exit(1); }
vg4->window->getsize(&winw, &winh);  /* get window-size */

/* set mouse grabbing to off */
vg4->input->mouse_grabbing(VG_FALSE);

/* open audio system */
if (!vg4->audio->open(VG_AUDIO_FREQ_MEDIUM, VG_FALSE)) { VG_dest(); exit(1); }

/* load audio files */
audc_hit = vg4->audio->load("files/hit.wav", 100, VG_AUDIO_VOLUME_SOUND);
audc_gameover = vg4->audio->load("files/gameover.wav", 100, VG_AUDIO_VOLUME_SOUND);
audc_bgmusic = vg4->audio->load("files/bgmusic.wav", 50, VG_AUDIO_VOLUME_MUSIC);

We initialize the ball, using the structure-variable ball. The ball-image is loaded and the rectangle of the ball is set to the center of the window with a randomized moving angle. The velocity of the ball is set to 3.5 pixels per moving-step.

[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;

Now we are in the game-loop. When moving the player-racket we also check for collision with the ball.

[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;
    if (player.rect.y < border_size) { player.rect.y = border_size; }
    /* check for collision with ball */
    if (check_for_collision(&ball.rectc.rect, &ball.angle, &player.rect)) {
      player.rect.y = y;
      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;
    if (player.rect.y > winh - border_size - player.rect.h) { player.rect.y = winh - border_size - player.rect.h; }
    /* check for collision with ball */
    if (check_for_collision(&ball.rectc.rect, &ball.angle, &player.rect)) {
      player.rect.y = y;
      vg4->audio->play(audc_hit, VG_FALSE, VG_FALSE);
    }
  }

We also have to move the ball. The function vg4->misc->moving_one_step() is used to get the moving positions. Then we iterate over these positions, checking for collisions with borders and the player-racket. If the player missed the ball (moving outside the window), the game is over.

[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 right border */
    if (rectcp[ipos].rect.x > winw - border_size - ball.rectc.rect.w) {
      /* change moving direction */
      ball.angle = 360 - ball.angle;
      ball.angle %= 360; ball.angle += 360; ball.angle %= 360;
      vg4->audio->play(audc_hit, VG_FALSE, VG_FALSE);
      break;
    }

    /* check top border */
    if (rectcp[ipos].rect.y < border_size) {
      /* change moving direction */
      ball.angle = 180 - ball.angle;
      ball.angle %= 360; ball.angle += 360; ball.angle %= 360;
      vg4->audio->play(audc_hit, VG_FALSE, VG_FALSE);
      break;
    }

    /* check bottom border */
    if (rectcp[ipos].rect.y > winh - border_size - ball.rectc.rect.h) {
      /* change moving direction */
      ball.angle = 180 - ball.angle;
      ball.angle %= 360; ball.angle += 360; ball.angle %= 360;
      vg4->audio->play(audc_hit, VG_FALSE, VG_FALSE);
      break;
    }

    /* check for collision with player */
    if (check_for_collision(&rectcp[ipos].rect, &ball.angle, &player.rect)) {
      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, where we just add the drawing of the ball.

[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 */
  rect.x = rect.y = 0; rect.w = winw; rect.h = border_size;
  vg4->window->draw_rect(&rect, VG_COLOR_RGB(0xb1, 0, 0), VG_TRUE);

  /* bottom border */
  rect.x = 0; rect.y = winh - border_size; rect.w = winw; rect.h = border_size;
  vg4->window->draw_rect(&rect, VG_COLOR_RGB(0xb1, 0, 0), VG_TRUE);

  /* right border */
  rect.x = winw - border_size; rect.y = 0; rect.w = border_size; rect.h = winh;
  vg4->window->draw_rect(&rect, VG_COLOR_RGB(0xb1, 0, 0), VG_TRUE);

  /* 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 add the function check_for_collision(), which checks whether the ball collides with the player-racket.

[To Position]
pingpong.c: function check_for_collision()

/* check whether ball collides with player
 * @param ball_rect    rectangle of the ball
 * @param ball_angle   angle of the ball, will be updated
 * @param player_rect  rectangle of the player-racket
 * @return  VG_TRUE = collision or VG_FALSE = no collision
 */
static VG_BOOL
check_for_collision(const struct VG_Rect *ball_rect, int *ball_angle, const struct VG_Rect *player_rect)
{
  VG_BOOL retw;

  if (ball_rect == NULL || ball_angle == NULL || player_rect == NULL) { return VG_FALSE; }

  /* check if ball touches player */
  if (ball_rect->x + ball_rect->w - 1 < player_rect->x
      || ball_rect->x > player_rect->x + player_rect->w - 1) { return VG_FALSE; }
  if (ball_rect->y + ball_rect->h - 1 < player_rect->y
      || ball_rect->y > player_rect->y + player_rect->h - 1) { return VG_FALSE; }

  /* modify ball-angle according to collision side */

  retw = VG_FALSE;

  if (ball_rect->x <= player_rect->x + player_rect->w - 1
      && ball_rect->x + ball_rect->w - 1 >= player_rect->x + player_rect->w - 1) {
    /* ball collides with player on the right */
    *ball_angle = 360 - (*ball_angle);
    *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360;
    retw = VG_TRUE;
  }

  if (ball_rect->y + ball_rect->h - 1 >= player_rect->y
      && ball_rect->y <= player_rect->y) {
    /* ball collides with player on the top */
    *ball_angle = 180 - (*ball_angle);
    *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360;
    retw = VG_TRUE;
  }

  if (ball_rect->y <= player_rect->y + player_rect->h - 1
      && ball_rect->y + ball_rect->h - 1 >= player_rect->y + player_rect->h - 1) {
    /* ball collides with player on the bottom */
    *ball_angle = 180 - (*ball_angle);
    *ball_angle %= 360; *ball_angle += 360; *ball_angle %= 360;
    retw = VG_TRUE;
  }

  return retw;
}



<<Previous Download Next>>