Cool

by Dom: Y7 Age ~12

Introduction

Young Dom developed this Smart Pascal program as he was learning the language. He had plenty of ideas and asked for help when necessary, for example with collision detection. See the technical features and code on this page below the program in action. Use the arrow or wasd keys to move and the space bar or q to shoot in the direction of your last move. (The wasd and q keys will not cause unwanted page shifts by the browser).

Cool.html

If the program does not work after clicking on the display, try another browser such as Chrome. If you see no display at school, the security system might have blocked it. You can try instead this direct link to the program running on its own page. Note the colour changes of the enemy as its health deteriorates and of the text as you progress through the ranks. Three bullet hits or one collision will kill the enemy and cause another to appear in a random position.

Technical Features

The program benefits from:

  • own procedures;
  • use of inbuilt routines such as Abs, Intersect, RandomInt, Ellipse, FillRect, FillText and BeginPath;
  • use of case statements;
  • handling of keyboard input.

The Code

unit OneBall;
{
    Copyright (c) 2015 Dom

    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/
}

interface

uses
  System.Types, SmartCL.System, SmartCL.Components, SmartCL.Application,
  SmartCL.Game, SmartCL.GameApp, SmartCL.Graphics;

type
  TCanvasProject = class(TW3CustomGameApplication)
  public
    procedure ApplicationStarting; override;
    procedure ApplicationClosing; override;
    procedure KeyDownEvent(mCode: integer);
    procedure Shoot;
    procedure PaintView(Canvas: TW3Canvas); override;
    procedure NewEnemy;
  end;

implementation

const
  MOB_WIDTH = 20;
  MOB_HEIGHT = 20;

var
  MobX: integer; //Distance of mobile from left of GameView
  GoingRight, GoingDown: Boolean;
  MobY, EnemyX, EnemyY, BulletX, BulletY, Score, Health: integer;
  Rank: string = 'Beginner';
  Colour: string = 'red';
  IntersectRect: TRect;
  direction: string = 'ShootUp';
  EnemyColour: string = 'blue';
  DirectionFacing: string = 'Up';

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
  randomize;
  GoingRight := True;
  GoingDown := True;
  MobX := 50;
  MobY := 100;
  EnemyX := RandomInt(GameView.Width);
  EnemyY := RandomInt(GameView.Height);
  BulletX := -60;
  Health := 3;
  asm
    window.onkeydown=function(e)
    {
    TCanvasProject.KeyDownEvent(Self,e.keyCode);
    }
  end;
  KeyDownEvent(0);
  GameView.Delay := 1;
  GameView.StartSession(True);
end;

procedure TCanvasProject.KeyDownEvent(mCode : integer);
begin
  case mCode of
    27: Application.Terminate;
    37, 65: begin
              dec(MobX, 20);
              DirectionFacing := 'Left'
            end;
    38, 87: begin
              dec(MobY, 20);
              DirectionFacing := 'Up'
            end;
    39, 68: begin
              inc(MobX, 20);
              DirectionFacing := 'Right'
            end;
    40, 83: begin
              inc(MobY, 20);
              DirectionFacing := 'Down'
            end;
    32, 81: begin
              if DirectionFacing = 'Up' then
                direction := 'ShootUp';
              if DirectionFacing = 'Right' then
                direction := 'ShootRight';
              if DirectionFacing = 'Down' then
                direction := 'ShootDown';
              if DirectionFacing = 'Left' then
                direction := 'ShootLeft';
              Shoot;
            end;
  end;
end;

procedure TCanvasProject.NewEnemy;
begin
  EnemyX := RandomInt(GameView.Width);
  EnemyY := RandomInt(GameView.Height);
  Score += 1;
  case Score of
    0..9: Rank := 'Beginner';
    10..19: Rank := 'Apprentice';
    20..29: Rank := 'Soldier';
    30..39: Rank := 'Master';
    40..max_Int: Rank := 'God';
  end;
end;

procedure TCanvasProject.Shoot;
begin
  BulletX := MobX + 10;
  BulletY := MobY + 10;
end;

procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
begin
  if Rank = 'Beginner' then
    Colour := 'Red';
  if Rank = 'Apprentice' then
    Colour := 'Blue';
  if Rank = 'Soldier' then
    Colour := 'Green';
  if Rank = 'Master' then
    Colour := 'Yellow';
  if Rank = 'God' then
    Colour := 'Gold';
  // Clear background with colour teal.
  Canvas.FillStyle := 'teal';
  Canvas.FillRect(0, 0, GameView.Width, GameView.Height);
  Canvas.Font := '10pt verdana';
  Canvas.FillStyle := 'black';
  Canvas.FillText('Score: ' + inttostr(Score) + '   Rank: ', 10, 20);
  Canvas.FillStyle := Colour;
  Canvas.FillText(Rank, 140, 20);
  //Draw circle.
  Canvas.FillStyle := Colour;
  Canvas.BeginPath;
  Canvas.Ellipse(MobX, MobY, MobX + MOB_WIDTH, MobY + MOB_HEIGHT);
  Canvas.Fill;
  Canvas.FillRect(BulletX, BulletY, 5, 5);
  if BulletX > 0 then
    case direction of
      "ShootRight": inc(BulletX, 20);
      "ShootLeft": inc(BulletX, -20);
      "ShootUp": inc(BulletY, -20);
      "ShootDown": inc(BulletY, 20);
    end;

  var BulletRect := TRect.Create(BulletX, BulletY, BulletX + 10, BulletY + 5);
  var EnemyRect := TRect.Create(EnemyX, EnemyY, EnemyX + 20, EnemyY + 20);
  if BulletRect.Intersect(EnemyRect, IntersectRect) then
    begin
      Health -= 1;
      BulletX := -100000; // Safely off the screen!
    end;
  if Health = 0 then
    begin
      Health := 3;
      NewEnemy;
    end;

  if Health = 2 then
    EnemyColour := 'black';
  if Health = 1 then
    EnemyColour := 'red';
  if Health = 3 then
    EnemyColour := 'blue';
  //Test for collision between mobile and enemy of the same size (20)
  if not ((abs(MobX - EnemyX) <= 20) and (abs(MobY - EnemyY) <= 20)) then
    begin
      Canvas.FillStyle := EnemyColour;
      Canvas.FillRect(EnemyX, EnemyY, 20, 20);
    end
  else
    NewEnemy;
end;

procedure TCanvasProject.ApplicationClosing;
begin
  GameView.EndSession;
  inherited;
end;

end.

Remarks

Can you develop this program further so that, for example, it is more difficult to continue to hit the enemy with bullets? You could also provide a measure of the scoring rate (since hitting a stationary target is rather easy given sufficient time).

Programming - a skill for life!

Student programs to inspire you!