Roller

by Neil: Y9 Age ~14

Introduction

This game is an excellent first use of WinGraph by Neil. It is straightforward to play and has a nice feel to it so that you want to try for higher scores. The first screenshot shows the ball beginning its jump to clear the oncoming barb and the second illustrates the output of the final score together with a tempting invitation to play again. Other young programmers should learn from and try to emulate this impressive, well-commented code.

See the following page for a version that keeps track of the highest score.

Game in progress

Game in progress

Final score

Final score

In order to compile program Roller, you will need to have downloaded Stefan Berinde`s wingraph.zip file as described in our Graphics tutorial. You should copy the unzipped wincrt.pas, wingraph.pas and winmouse.pas (from the src folder) into your program folder. (The compiled units are included in the zip file but you might as well have the source code available for reference). You should find these three files useful for your own motion graphics programs. Note that you need to compile programs that use WinGraph on a 32-bit compiler.

The next page shows the code for the game updated to save your highest score.

The Code

program Roller;
{
    Copyright (c) 2013 Neil

    Licensed under the Apache License, Version 2.0 (the "License"); you may not
    use this file except in compliance with the License, as described at
    http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/ }

{$APPTYPE GUI}

uses
  WinCrt, WinGraph, Math, SysUtils;

const
  FrameDelay = 15;
  JumpHeight = 6;
  playerX = 100;
  barbXradius = 15;
  barbYradius = 24;

var
  gd, gm : smallint;
  playerY : integer;
  KeyboardInput, DeathScreenController : char;
  ender : integer;
  barbX, barbY : integer;
  BarbSpeed : integer;
  BarbController : integer;
  BallMovement, KeyChecker : string;
  score : integer;
  DistanceSquared : integer;
  PlayedOnce : boolean;

  //Player Procedures:
  procedure DrawBondiBlueCircle;
  begin
    setcolor(Azure); //sets all colour to Azure because I can't find a way to specifically change the outline to Azure.
    SetFillStyle(SolidFill, BondiBlue); //sets the fill to be solid and white
    FillEllipse(playerX, playerY, 13, 13); //draws the circle
  end;

  procedure HideBondiBlueCircle;
  begin
    setcolor(Azure);
    SetFillStyle(SolidFill, Azure);
    FillEllipse(playerX, playerY, 13, 13);
  end;

  procedure GoUp;
  begin
    HideBondiBlueCircle;
    playerY := playerY - JumpHeight;
    DrawBondiBlueCircle;
  end;

  procedure GoDown;
  begin
    HideBondiBlueCircle;
    playerY := playerY + JumpHeight;
    DrawBondiBlueCircle;
  end;

//game management procedures
procedure InitGame;
  begin
    SetFillStyle(SolidFill, azure);
    Bar(0, 0, GetMaxX, GetMaxY);
    SetFillStyle(SolidFill, PaleGreen);
    Bar(0, 226, GetMaxX, GetMaxY);
    setcolor(BondiBlue);
    SetFillStyle(SolidFill, BondiBlue);
    playerY := 212;
    FillEllipse(playerX, playerY, 13, 13);
    SetColor(scarlet);

    if PlayedOnce = false then
      begin
        SetTextStyle(MSSansSerifFont, HorizDir, 60);
        OutTextXY(250, 100, 'Roller');
        SetTextStyle(MSSansSerifFont, HorizDir, 35);
        OutTextXY(180, 163, 'Press space to play.');
      end;

    if PlayedOnce = true then
      begin
        SetTextStyle(MSSansSerifFont, HorizDir, 45);
        OutTextXY(205, 117, 'Your score was: ' + inttostr(score));
        SetTextStyle(MSSansSerifFont, HorizDir, 35);
        OutTextXY(205, 163, 'Press space to play again.');
      end;

    DeathScreenController := #1;
    repeat
      if (keypressed = True) then
        DeathScreenController := readkey;
    until (DeathScreenController = #32) or (CloseGraphRequest) or (DeathScreenController = #27);

    SetFillStyle(SolidFill, azure);
    Bar(0, 0, GetMaxX, GetMaxY);
    SetFillStyle(SolidFill, PaleGreen);
    Bar(0, 226, GetMaxX, GetMaxY);
    SetColor(InternationalKleinBlue);
    SetTextStyle(CourierNewFont, HorizDir, 27);
    OutTextXY(6, 4, 'Score:');

    BarbController := 1;
    BallMovement := 'stationary';
    score := 0;
    BarbSpeed := 9;
    ender := 1;
    barbX := 615;
    barbY := RandomRange(150, 205); //sets barbY to a random integer between 150 and 205
  end;

begin
  gd := d8bit; //graphics driver settings
  gm := mCustom; //graphics mode. The window size is set to a custom size
  SetWindowSize(640, 360); //sets custom window size to 640 pixels wide and 360 high
  InitGraph(gd, gm, 'Roller'); //initiates a graphics window that uses gd and gm and has "Roller" as the title
  PlayedOnce := false;
  InitGame;

  repeat
    SetColor(InternationalKleinBlue);
    SetTextStyle(CourierNewFont, HorizDir, 27);  //writes out the score at 90, 4
    OutTextXY(90, 4, IntToStr(score)); //IntToStr converts the integer "score" into a string

    if score = 1 then
      OutTextXY(30, 270, 'Press the up arrow to jump.');
    if score = 400 then
      begin
        SetFillStyle(SolidFill, PaleGreen);
        Bar(0, 226, GetMaxX, GetMaxY);
      end;

    //speed increasing code
    if score = 1000 then
      begin
        BarbSpeed := 12;
        SetTextStyle(MSSansSerifFont, HorizDir, 45);
        SetColor(scarlet);
        OutTextXY(30, 270, 'Speed up!');
      end;
    if score = 1100 then
      begin
        SetFillStyle(SolidFill, PaleGreen);
        Bar(0, 226, GetMaxX, GetMaxY);
      end;

    if (KeyPressed = true) then
      begin
        KeyboardInput := readkey;
        case KeyboardInput of
          #72 : if BallMovement = 'stationary' then
                  BallMovement := 'up';  //up key to jump
          #27 : CloseGraph; //closes when esc is pressed
        end;
      end;

    if playerY = 122 then
      BallMovement := 'down'; //122 is above 212 rather than below because the Y axis is inverted.
                                                  //If it wasn't inverted I would use 302
    if BallMovement = 'up' then
      GoUp;
    if BallMovement = 'down' then
      GoDown;

    if playerY = 212 then
      BallMovement := 'stationary';

    if BarbController = 1 then
      begin
        barbX := 615;
        barbY := RandomRange(168, 205); //sets barbY to a random integer between 150 and 205
      end;

    DistanceSquared := (playerX - barbX) * (playerX - barbX) + (playerY - barbY) * (playerY - barbY); //Pythagoras collision detection stuff
    if 666 > DistanceSquared then //1300 was roughly the original number
      begin
        SetColor(DarkGray);
        SetTextStyle(ArialFont, HorizDir, 90);
        OutTextXY(barbX - barbXradius, barbY - barbYradius, '*'); //outputs '*' at barbX, barbY
        delay(1011);
        InitGame;
      end;

    if barbX > 0 then
      begin
        BarbController := 2;
        SetColor(DarkGray);
        SetTextStyle(ArialFont, HorizDir, 90);
        OutTextXY(barbX - barbXradius, barbY - barbYradius, '*'); //outputs '*' at barbX, barbY
        delay(FrameDelay);
        SetFillStyle(SolidFill, azure);
        bar(barbX - 10, barbY - 37, barbX + 35, barbY + 20); //covers up the area that the barb is
        barbX := barbX - BarbSpeed;
        SetColor(DarkGray);
        SetTextStyle(ArialFont, HorizDir, 90);
        OutTextXY(barbX - barbXradius, barbY - barbYradius, '*'); //outputs '*' at barbX, barbY
      end;

    SetColor(BondiBlue); //this is here so that parts of the ball doesn't seem to be cut off by the barb
    SetFillStyle(SolidFill, BondiBlue);
    FillEllipse(playerX, playerY, 13, 13);

    if barbX = 3 then     //I use 3 instead of 0 because the distance between 615 and 3 is 612, which has lots of factors that I can use for BarbSpeed
      begin
        SetColor(DarkGray);
        SetTextStyle(ArialFont, HorizDir, 90);
        OutTextXY(0, barbY - barbYradius, '*');
        delay(FrameDelay);
        barbX := 0;
        SetColor(Azure);
        SetFillStyle(SolidFill, Azure);
        Bar(0, 105, 45, 225); //puts a bar over the entire area that the barb could be
        OutTextXY(0, barbY - barbYradius, '*');
        BarbController := 1;
      end;

    SetColor(BondiBlue);//so that parts of the ball don't seem to be cut off by the barb
    SetFillStyle(SolidFill, BondiBlue);
    FillEllipse(playerX, playerY, 13, 13);

    SetColor(Azure);
    SetFillStyle(SolidFill, Azure);//covers up the previously written score so that it can be written again
    bar(90, 4, 200, 60);

    Inc(score); //increments the score by one

    if PlayedOnce = false then
      PlayedOnce := true;

  until (ender = 2) or (CloseGraphRequest) or (DeathScreenController = #27); //waits until the user clicks the X on the window

  CloseGraph; //closes the program

end.
Programming - a skill for life!

Student programs to inspire you!