PlatformDemo

Smart Mobile Studio demonstration by Steven Binns: Y13 Age ~18

Introduction

This demonstration should help aspiring game programmers to make their own platform games. It demonstrates (using a circle for a player):
  • object-oriented code, with classes for platforms and players;
  • keyboard input for moving a player left and right and for jumping;
  • collision detection, preventing players from moving through platforms;
  • gravity;
  • friction, so that when left and right keys are not pressed a moving player slows to a standstill;
  • the use of real numbers that can be represented exactly in binary e.g. 0.25 and 0.125.

You will see from the code that the w, a and d keys control the blue player and the up, left and right arrows move the red player. Click on the graphics window before your first key press.

In order to familiarise yourself with the techniques you could, for example:

  • write code to detect collisions between players and respond to these collisions;
  • devise suitable positions and sizes for platforms;
  • introduce a scoring system.

If PlatformDemo does not run in your current browser, please try another (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.

Platform Demo

The Code

This is the code for versions of Smart Mobile Studio up to 2.0. See the code for Version 2.1 in the next section. We provide the code for a single player and platform in the final section to enable you to understand the collision code more easily.

unit PlatformDemo;
{
    Copyright (c) 2014 Steven Binns

    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
  W3System, W3Components, W3Application, W3Game, W3GameApp, W3Graphics;

type
  TApplication = class(TW3CustomGameApplication)
  protected
    procedure  ApplicationStarting; override;
    procedure  ApplicationClosing; override;
    procedure  PaintView(Canvas: TW3Canvas); override;
    procedure  KeyDownEvent(mCode: integer);
    procedure  KeyUpEvent(mCode: integer);
end;

type
  TPlayer = class(TObject)
  public
    radius: integer;
    x, y, xSpeed, ySpeed, grav: real;  //ySpeed is positive moving up.
    moveLeft, moveRight: boolean;
    leftKey, rightKey, jumpKey: integer;
    colour: string;
    constructor Create(newX, newY, newR, newLk, newRk, newJk: integer; newColour: String);
  end;

  TPlatform = class(TObject)
  public
    x, y, width, height: real;
    constructor create(newX, newY, newW, newH: real);
end;

var
  players : array[1..2] of TPlayer;
  platforms: array[0..3] of TPlatform;
  collision: boolean;
  gvWidth, gvHeight: integer;

function checkVCollision(k, subject: integer): boolean;
function checkHCollision(k, subject: integer): boolean;

implementation

{ TApplication}
procedure TApplication.KeyDownEvent(mCode: integer);
var
  i : integer;
begin
  for i := 1 to 2 do
    case mCode of
      players[i].leftKey: if players[i].moveRight = false then
                            players[i].moveLeft := true;
      players[i].rightKey: if players[i].moveLeft = false then
                             players[i].moveRight := true;
      players[i].jumpKey: if players[i].ySpeed = 0 then
                            players[i].ySpeed := 5;
    end;
end;

procedure TApplication.KeyUpEvent(mCode: integer);
var
  i : integer;
begin
  for i := 1 to 2 do
    case mCode of
      players[i].leftKey: players[i].moveLeft := false;
      players[i].rightKey: players[i].moveRight := false;
    end;
end;

procedure TApplication.ApplicationStarting;
begin
  inherited;
   players[1] := TPlayer.Create(150, 50, 5, 37, 39, 38, 'red'); //lKey:left rKey:right jKey:up
   players[2] := TPlayer.Create(50, 50, 5, 65, 68, 87, 'blue'); //lKey:a rKey:d jKey:w
  asm
    window.onkeydown = function(e)
    {
    TApplication.KeyDownEvent(Self,e.keyCode);
    }
    window.onkeyup = function(e)
    {
    TApplication.KeyUpEvent(Self,e.keyCode);
    }
  end;
  KeyDownEvent(0);
  KeyUpEvent(0);

  GameView.Width := 300;
  GameView.Height := 150;
  gvWidth := 300;
  gvHeight := 150;
  platforms[0] := TPlatform.create(50, GameView.Height - 30, 20, 5);
  platforms[1] := TPlatform.create(100, GameView.Height - 35, 15, 2);
  platforms[2] := TPlatform.create(150, GameView.Height - 45, 20, 10);
  platforms[3] := TPlatform.create(200, GameView.Height - 55, 5, 3);
  GameView.Delay := 10;
  GameView.StartSession(True);
end;

procedure TApplication.PaintView(Canvas: TW3Canvas);
var
  i, j, k : integer;
begin
  for j := 1 to 2 do  // Step players
    begin // Players will gain speed up to a max value while key pressed.
      if (players[j].moveRight = true) and (players[j].xSpeed < 3) then
        players[j].xSpeed += 0.25;
      if (players[j].moveLeft = true) and (players[j].xSpeed > -3) then
        players[j].xSpeed -= 0.25;
      //Check for horizontal collisions.
      collision := false;
      for k := 0 to 3 do
        begin
          if checkHCollision(k, j) = true then
            begin
              collision := true;
              break; //Collision platform's number = k
            end;
        end;

  if collision = false then  //Continue to move.
    players[j].x += players[j].xSpeed
  else //Horizontal collision
    begin  //Prevent player from entering platform and stop horizontal movement.
      if players[j].xSpeed > 0 then
        begin
          players[j].x := platforms[k].x - players[j].radius;
          players[j].xSpeed := 0;
        end;
      if players[j].xSpeed < 0 then
        begin
          players[j].x := platforms[k].x + platforms[k].width + players[j].radius;
          players[j].xSpeed := 0;
        end;
    end;
  //Keep players horizontally in the GameView.
  if players[j].x + players[j].radius > gvWidth then
    players[j].x := gvWidth - players[j].radius;
  if players[j].x - players[j].radius < 0 then
    players[j].x := players[j].radius;
  //Create horizontal friction by reducing the speed.
  if (players[j].moveLeft = false) and (players[j].moveRight = false) then
    begin
      if players[j].xSpeed < 0 then
        players[j].xSpeed += 0.125
      else if players[j].xSpeed > 0 then
        players[j].xSpeed -= 0.125;
    end;
  //Deal with jumping and gravity
  collision := false;
  for k := 0 to 3 do
    begin
      if checkVCollision(k, j) = true then
        begin
          collision := true;
          break; //k holds platform number of collision
        end;
    end;
  if collision = false then
    begin
      players[j].y -= players[j].ySpeed;
      if players[j].y + players[j].radius < gvHeight then
        begin
          players[j].ySpeed -= players[j].grav;
          players[j].grav += 0.1;
        end
      else
        begin //Player has reached the floor so position it exactly on the floor.
          players[j].y := gvHeight - players[j].radius;
          players[j].ySpeed := 0;
          players[j].grav := 0;
        end;
    end
  else //vertical collision
    begin
      if players[j].ySpeed > 0 then  //Hits bottom of platform k
        begin  //Position it just under platform.
          players[j].y := platforms[k].y + platforms[k].height + players[j].radius;
          players[j].ySpeed := 0;
          players[j].grav := 0;
        end;
      if players[j].yspeed < 0 then  //Hits top of platform k
        begin   //Place it on platform k.
          players[j].y := platforms[k].y - players[j].radius;
          players[j].ySpeed := 0;
          players[j].grav := 0;
        end;
    end;
  end; //Finished stepping players
  // Clear background with colour gray.
  Canvas.FillStyle := 'gray';
  Canvas.FillRect(0, 0, gvWidth, gvHeight);
  //Draw circles for players.
  for i := 1 to 2 do
    begin
      Canvas.FillStyle := players[i].colour;
      Canvas.BeginPath;
      Canvas.Ellipse(players[i].x - players[i].radius, players[i].y - players[i].radius,
                     players[i].x + players[i].radius, players[i].y + players[i].radius); //leftX, topY, rightX, bottomY
      Canvas.ClosePath;
      Canvas.Fill;
    end;
  {Draw platforms}
  for k := 0 to 3 do
    begin
      Canvas.FillStyle := 'black';
      Canvas.FillRect(round(platforms[k].x), round(platforms[k].y), round(platforms[k].width),
                      round(platforms[k].height));
    end;
end;

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

{TPlayer constructor}
constructor TPlayer.Create(newX, newY, newR, newLk, newRk, newJk: integer; newColour: string);
begin
  x := newX;
  y := newY;
  radius := newR;
  leftKey := newLk;
  rightKey := newRk;
  jumpKey := newJk;
  xSpeed := 0;
  ySpeed := 0;
  grav := 0;
  colour := newColour;
end;

{TPlatform constructor}
constructor TPlatform.create(newX, newY, newW, newH : real);
begin
  x := newX;
  y := newY;
  width := newW;
  height := newH;
end;

{General functions}
function checkVCollision(k, subject : integer): boolean;
  function hAlignment: boolean;  //Nested function
  //If player's right extremity to right of left edge of platform k and
  //player's left extremity to left of right edge of platform k then return true.
  begin
    result := false;
    if (players[subject].x + players[subject].radius > platforms[k].x) and
       (players[subject].x - players[subject].radius < platforms[k].x + platforms[k].width) then
      result := true;
  end;
begin
  result := false;
  //If ascending and horizontally aligned and
  //top of player under bottom of platform k and
  // next move would enter platform k then return true.
  if (players[subject].ySpeed > 0) and hAlignment and
     (players[subject].y - players[subject].radius > platforms[k].y + platforms[k].height) and
     (players[subject].y - players[subject].yspeed - players[subject].radius < platforms[k].y + platforms[k].height) then
     result := true;
  //If descending and horizontally aligned and
  // bottom of player under top of platform k and
  // next move would enter platform k then return true.
  if (players[subject].yspeed < 0) and hAlignment and
    (players[subject].y + players[subject].radius < platforms[k].y) and
    (players[subject].y + players[subject].radius - players[subject].ySpeed > platforms[k].y) then
    result := true;

  if (players[subject].yspeed = 0) and hAlignment and
     (players[subject].y + players[subject].radius = platforms[k].y) and
     (players[subject].y + players[subject].radius + 1 > platforms[k].y) then
    result := true;
end;

function checkHCollision(k, subject: integer): boolean;
  function vAlignment: boolean;  //Nested function
  begin
    result := false;
    if (players[subject].y - players[subject].radius < platforms[k].y + platforms[k].height) and
       (players[subject].y + players[subject].radius > platforms[k].y) then
      result := true;
  end;
begin
  result := false;
  //If moving right and vertically aligned and
  //player's right extremity to left or same as platform's left edge and
  //next move would enter platform then return true.
  if (players[subject].xspeed > 0) and vAlignment and
     (players[subject].x + players[subject].radius <= platforms[k].x) and
     (players[subject].x + players[subject].radius + players[subject].xSpeed > platforms[k].x) then
    result := true;
  //If moving left and vertically aligned and
  //player's left extremity to right or same as platform's right edge and
  //next move would enter platform then return true.
  if (players[subject].xspeed < 0 ) and vAlignment and
     (players[subject].x - players[subject].radius >= platforms[k].x + platforms[k].width) and
     (players[subject].x - players[subject].radius + players[subject].xspeed < platforms[k].x + platforms[k].width) then
    result := true;
end;

end.

Code for Version 2.1 of Smart Mobile Studio

We have moved global variables and functions so that they are now part of the TCanvasProject class.

unit Unit1;
{
    PlatformDemo  Copyright (c) 2014 Steven Binns

    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
  TPlayer = class(TObject)
  public
    radius: integer;
    x, y, xSpeed, ySpeed, grav: real;  //ySpeed is positive moving up.
    moveLeft, moveRight: boolean;
    leftKey, rightKey, jumpKey: integer;
    colour: string;
    constructor Create(newX, newY, newR, newLk, newRk, newJk: integer; newColour: String);
  end;

  TPlatform = class(TObject)
  public
    x, y, width, height: real;
    constructor create(newX, newY, newW, newH: real);
  end;

  TCanvasProject = class(TW3CustomGameApplication)
  private
     players : array[1..2] of TPlayer;
     platforms: array[0..3] of TPlatform;
     collision: boolean;
     gvWidth, gvHeight: integer;
     function checkVCollision(k, subject: integer): boolean;
     function checkHCollision(k, subject: integer): boolean;
  protected
    procedure  ApplicationStarting; override;
    procedure  ApplicationClosing; override;
    procedure  PaintView(Canvas: TW3Canvas); override;
    procedure  KeyDownEvent(mCode: integer);
    procedure  KeyUpEvent(mCode: integer);
end;

implementation

{ TCanvasProject}

{General functions}
function TCanvasProject.checkVCollision(k, subject : integer): boolean;
  function hAlignment: boolean;  //Nested function
  //If player's right extremity to right of left edge of platform k and
  //player's left extremity to left of right edge of platform k then return true.
  begin
    result := false;
    if (players[subject].x + players[subject].radius > platforms[k].x) and
       (players[subject].x - players[subject].radius < platforms[k].x + platforms[k].width) then
      result := true;
  end;
begin
  result := false;
  //If ascending and horizontally aligned and
  //top of player under bottom of platform k and
  // next move would enter platform k then return true.
  if (players[subject].ySpeed > 0) and hAlignment and
     (players[subject].y - players[subject].radius > platforms[k].y + platforms[k].height) and
     (players[subject].y - players[subject].yspeed - players[subject].radius < platforms[k].y + platforms[k].height) then
     result := true;
  //If descending and horizontally aligned and
  // bottom of player under top of platform k and
  // next move would enter platform k then return true.
  if (players[subject].yspeed < 0) and hAlignment and
    (players[subject].y + players[subject].radius < platforms[k].y) and
    (players[subject].y + players[subject].radius - players[subject].ySpeed > platforms[k].y) then
    result := true;

  if (players[subject].yspeed = 0) and hAlignment and
     (players[subject].y + players[subject].radius = platforms[k].y) and
     (players[subject].y + players[subject].radius + 1 > platforms[k].y) then
    result := true;
end;

function TCanvasProject.checkHCollision(k, subject: integer): boolean;
  function vAlignment: boolean;  //Nested function
  begin
    result := false;
    if (players[subject].y - players[subject].radius < platforms[k].y + platforms[k].height) and
       (players[subject].y + players[subject].radius > platforms[k].y) then
      result := true;
  end;
begin
  result := false;
  //If moving right and vertically aligned and
  //player's right extremity to left or same as platform's left edge and
  //next move would enter platform then return true.
  if (players[subject].xspeed > 0) and vAlignment and
     (players[subject].x + players[subject].radius <= platforms[k].x) and
     (players[subject].x + players[subject].radius + players[subject].xSpeed > platforms[k].x) then
    result := true;
  //If moving left and vertically aligned and
  //player's left extremity to right or same as platform's right edge and
  //next move would enter platform then return true.
  if (players[subject].xspeed < 0 ) and vAlignment and
     (players[subject].x - players[subject].radius >= platforms[k].x + platforms[k].width) and
     (players[subject].x - players[subject].radius + players[subject].xspeed < platforms[k].x + platforms[k].width) then
    result := true;
end;
procedure TCanvasProject.KeyDownEvent(mCode: integer);
var
  i : integer;
begin
  for i := 1 to 2 do
    case mCode of
      players[i].leftKey: if players[i].moveRight = false then
                            players[i].moveLeft := true;
      players[i].rightKey: if players[i].moveLeft = false then
                             players[i].moveRight := true;
      players[i].jumpKey: if players[i].ySpeed = 0 then
                            players[i].ySpeed := 5;
    end;
end;

procedure TCanvasProject.KeyUpEvent(mCode: integer);
var
  i : integer;
begin
  for i := 1 to 2 do
    case mCode of
      players[i].leftKey: players[i].moveLeft := false;
      players[i].rightKey: players[i].moveRight := false;
    end;
end;

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
   players[1] := TPlayer.Create(150, 50, 5, 37, 39, 38, 'red'); //lKey:left rKey:right jKey:up
   players[2] := TPlayer.Create(50, 50, 5, 65, 68, 87, 'blue'); //lKey:a rKey:d jKey:w
  asm
    window.onkeydown = function(e)
    {
    TCanvasProject.KeyDownEvent(Self,e.keyCode);
    }
    window.onkeyup = function(e)
    {
    TCanvasProject.KeyUpEvent(Self,e.keyCode);
    }
  end;
  KeyDownEvent(0);
  KeyUpEvent(0);

  GameView.Width := 300;
  GameView.Height := 150;
  gvWidth := 300;
  gvHeight := 150;
  platforms[0] := TPlatform.create(50, GameView.Height - 30, 20, 5);
  platforms[1] := TPlatform.create(100, GameView.Height - 35, 15, 2);
  platforms[2] := TPlatform.create(150, GameView.Height - 45, 20, 10);
  platforms[3] := TPlatform.create(200, GameView.Height - 55, 5, 3);
  GameView.Delay := 10;
  GameView.StartSession(True);
end;

procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
var
  i, j, k : integer;
begin
  for j := 1 to 2 do  // Step players
    begin // Players will gain speed up to a max value while key pressed.
      if (players[j].moveRight = true) and (players[j].xSpeed < 3) then
        players[j].xSpeed += 0.25;
      if (players[j].moveLeft = true) and (players[j].xSpeed > -3) then
        players[j].xSpeed -= 0.25;
      //Check for horizontal collisions.
      collision := false;
      for k := 0 to 3 do
        begin
          if checkHCollision(k, j) = true then
            begin
              collision := true;
              break; //Collision platform's number = k
            end;
        end;

  if collision = false then  //Continue to move.
    players[j].x += players[j].xSpeed
  else //Horizontal collision
    begin  //Prevent player from entering platform and stop horizontal movement.
      if players[j].xSpeed > 0 then
        begin
          players[j].x := platforms[k].x - players[j].radius;
          players[j].xSpeed := 0;
        end;
      if players[j].xSpeed < 0 then
        begin
          players[j].x := platforms[k].x + platforms[k].width + players[j].radius;
          players[j].xSpeed := 0;
        end;
    end;
  //Keep players horizontally in the GameView.
  if players[j].x + players[j].radius > gvWidth then
    players[j].x := gvWidth - players[j].radius;
  if players[j].x - players[j].radius < 0 then
    players[j].x := players[j].radius;
  //Create horizontal friction by reducing the speed.
  if (players[j].moveLeft = false) and (players[j].moveRight = false) then
    begin
      if players[j].xSpeed < 0 then
        players[j].xSpeed += 0.125
      else if players[j].xSpeed > 0 then
        players[j].xSpeed -= 0.125;
    end;
  //Deal with jumping and gravity
  collision := false;
  for k := 0 to 3 do
    begin
      if checkVCollision(k, j) = true then
        begin
          collision := true;
          break; //k holds platform number of collision
        end;
    end;
  if collision = false then
    begin
      players[j].y -= players[j].ySpeed;
      if players[j].y + players[j].radius < gvHeight then
        begin
          players[j].ySpeed -= players[j].grav;
          players[j].grav += 0.1;
        end
      else
        begin //Player has reached the floor so position it exactly on the floor.
          players[j].y := gvHeight - players[j].radius;
          players[j].ySpeed := 0;
          players[j].grav := 0;
        end;
    end
  else //vertical collision
    begin
      if players[j].ySpeed > 0 then  //Hits bottom of platform k
        begin  //Position it just under platform.
          players[j].y := platforms[k].y + platforms[k].height + players[j].radius;
          players[j].ySpeed := 0;
          players[j].grav := 0;
        end;
      if players[j].yspeed < 0 then  //Hits top of platform k
        begin   //Place it on platform k.
          players[j].y := platforms[k].y - players[j].radius;
          players[j].ySpeed := 0;
          players[j].grav := 0;
        end;
    end;
  end; //Finished stepping players
  // Clear background with colour gray.
  Canvas.FillStyle := 'gray';
  Canvas.FillRect(0, 0, gvWidth, gvHeight);
  //Draw circles for players.
  for i := 1 to 2 do
    begin
      Canvas.FillStyle := players[i].colour;
      Canvas.BeginPath;
      Canvas.Ellipse(players[i].x - players[i].radius, players[i].y - players[i].radius,
                     players[i].x + players[i].radius, players[i].y + players[i].radius); //leftX, topY, rightX, bottomY
      Canvas.Fill;
    end;
  {Draw platforms}
  for k := 0 to 3 do
    begin
      Canvas.FillStyle := 'black';
      Canvas.FillRect(round(platforms[k].x), round(platforms[k].y), round(platforms[k].width),
                      round(platforms[k].height));
    end;
end;

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

{TPlayer constructor}
constructor TPlayer.Create(newX, newY, newR, newLk, newRk, newJk: integer; newColour: string);
begin
  x := newX;
  y := newY;
  radius := newR;
  leftKey := newLk;
  rightKey := newRk;
  jumpKey := newJk;
  xSpeed := 0;
  ySpeed := 0;
  grav := 0;
  colour := newColour;
end;

{TPlatform constructor}
constructor TPlatform.create(newX, newY, newW, newH : real);
begin
  x := newX;
  y := newY;
  width := newW;
  height := newH;
end;

end.

Single Platform Demo

unit Unit1;
{
    Single platform and player version of
    PlatformDemo  Copyright (c) 2014 Steven Binns

    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
  TPlayer = class(TObject)
  public
    radius: integer;
    x, y, xSpeed, ySpeed, grav: real;  //ySpeed is positive moving up.
    moveLeft, moveRight: boolean;
    leftKey, rightKey, jumpKey: integer;
    colour: string;
    constructor Create(newX, newY, newR, newLk, newRk, newJk: integer; newColour: String);
  end;

  TPlatform = class(TObject)
  public
    x, y, width, height: real;
    constructor create(newX, newY, newW, newH: real);
  end;

  TCanvasProject = class(TW3CustomGameApplication)
  private
     player: TPlayer;
     platform: TPlatform;
     collision: boolean;
     gvWidth, gvHeight: integer;
     function checkVCollision: boolean;
     function checkHCollision: boolean;
  protected
    procedure  ApplicationStarting; override;
    procedure  ApplicationClosing; override;
    procedure  PaintView(Canvas: TW3Canvas); override;
    procedure  KeyDownEvent(mCode: integer);
    procedure  KeyUpEvent(mCode: integer);
end;

implementation

{ TCanvasProject}

{General functions}
function TCanvasProject.checkVCollision: boolean;
  function hAlignment: boolean;  //Nested function
  // If player's right extremity to right of left edge of platform and player's
  // left extremity to left of right edge of platform then return true.
  begin
    result := false;
    if (player.x + player.radius > platform.x) and
       (player.x - player.radius < platform.x + platform.width) then
      result := true;
  end;
begin
  result := false;
  //If ascending and horizontally aligned and top of player under bottom of
  // platform and next move would enter platform then return true.
  if (player.ySpeed > 0) and hAlignment and
     (player.y - player.radius > platform.y + platform.height) and
     (player.y - player.yspeed - player.radius < platform.y + platform.height) then
     result := true;
  // If descending and horizontally aligned and bottom of player under top of
  // platform and next move would enter platform then return true.
  if (player.yspeed < 0) and hAlignment and
    (player.y + player.radius < platform.y) and
    (player.y + player.radius - player.ySpeed > platform.y) then
    result := true;
  if (player.yspeed = 0) and hAlignment and
     (player.y + player.radius = platform.y) and
     (player.y + player.radius + 1 > platform.y) then
    result := true;
end;

function TCanvasProject.checkHCollision: boolean;
  function vAlignment: boolean;  //Nested function
  begin
    result := false;
    if (player.y - player.radius < platform.y + platform.height) and
       (player.y + player.radius > platform.y) then
      result := true;
  end;
begin
  result := false;
  //If moving right and vertically aligned and
  //player's right extremity to left or same as platform's left edge and
  //next move would enter platform then return true.
  if (player.xspeed > 0) and vAlignment and
     (player.x + player.radius <= platform.x) and
     (player.x + player.radius + player.xSpeed > platform.x) then
    result := true;
  //If moving left and vertically aligned and
  //player's left extremity to right or same as platform's right edge and
  //next move would enter platform then return true.
  if (player.xspeed < 0 ) and vAlignment and
     (player.x - player.radius >= platform.x + platform.width) and
     (player.x - player.radius + player.xspeed < platform.x + platform.width) then
    result := true;
end;
procedure TCanvasProject.KeyDownEvent(mCode: integer);
begin
  case mCode of
    player.leftKey: if player.moveRight = false then
                      player.moveLeft := true;
    player.rightKey: if player.moveLeft = false then
                      player.moveRight := true;
    player.jumpKey: if player.ySpeed = 0 then
                      player.ySpeed := 5;
    end;
end;

procedure TCanvasProject.KeyUpEvent(mCode: integer);
begin
  case mCode of
    player.leftKey: player.moveLeft := false;
    player.rightKey: player.moveRight := false;
  end;
end;

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
   player := TPlayer.Create(150, 50, 5, 37, 39, 38, 'red'); //lKey:left rKey:right jKey:up
  asm
    window.onkeydown = function(e)
    {
    TCanvasProject.KeyDownEvent(Self,e.keyCode);
    }
    window.onkeyup = function(e)
    {
    TCanvasProject.KeyUpEvent(Self,e.keyCode);
    }
  end;
  KeyDownEvent(0);
  KeyUpEvent(0);

  GameView.Width := 300;
  GameView.Height := 150;
  gvWidth := 300;
  gvHeight := 150;
  platform := TPlatform.create(50, GameView.Height - 30, 20, 5);
  GameView.Delay := 10;
  GameView.StartSession(True);
end;

procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
begin
  // Player will gain speed up to a max value while key pressed.
  if (player.moveRight = true) and (player.xSpeed < 3) then
    player.xSpeed += 0.25;
  if (player.moveLeft = true) and (player.xSpeed > -3) then
    player.xSpeed -= 0.25;
  //Check for horizontal collision.
  collision := false;
  if checkHCollision = true then
    collision := true;

  if collision = false then  //Continue to move.
    player.x += player.xSpeed
  else //Horizontal collision
    begin  //Prevent player from entering platform and stop horizontal movement.
      if player.xSpeed > 0 then
        begin
          player.x := platform.x - player.radius;
          player.xSpeed := 0;
        end;
      if player.xSpeed < 0 then
        begin
          player.x := platform.x + platform.width + player.radius;
          player.xSpeed := 0;
        end;
    end;
  //Keep player horizontally in the GameView.
  if player.x + player.radius > gvWidth then
    player.x := gvWidth - player.radius;
  if player.x - player.radius < 0 then
    player.x := player.radius;
  //Create horizontal friction by reducing the speed.
  if (player.moveLeft = false) and (player.moveRight = false) then
    begin
      if player.xSpeed < 0 then
        player.xSpeed += 0.125
      else if player.xSpeed > 0 then
        player.xSpeed -= 0.125;
    end;
  //Deal with jumping and gravity
  collision := false;
  if checkVCollision = true then
    collision := true;

  if collision = false then
    begin
      player.y -= player.ySpeed;
      if player.y + player.radius < gvHeight then
        begin
          player.ySpeed -= player.grav;
          player.grav += 0.1;
        end
      else
        begin //Player has reached the floor so position it exactly on the floor.
          player.y := gvHeight - player.radius;
          player.ySpeed := 0;
          player.grav := 0;
        end;
    end
  else //vertical collision
    begin
      if player.ySpeed > 0 then  //Hits bottom of platform
        begin  //Position it just under platform.
          player.y := platform.y + platform.height + player.radius;
          player.ySpeed := 0;
          player.grav := 0;
        end;
      if player.yspeed < 0 then  //Hits top of platform
        begin   //Place it on platform.
          player.y := platform.y - player.radius;
          player.ySpeed := 0;
          player.grav := 0;
        end;
  end;
  // Clear background with colour gray.
  Canvas.FillStyle := 'gray';
  Canvas.FillRect(0, 0, gvWidth, gvHeight);
  //Draw circle for player.
  Canvas.FillStyle := player.colour;
  Canvas.BeginPath;
  Canvas.Ellipse(player.x - player.radius, player.y - player.radius,
                 player.x + player.radius, player.y + player.radius); //leftX, topY, rightX, bottomY
  Canvas.Fill;
  // Draw platform
  Canvas.FillStyle := 'black';
  Canvas.FillRect(round(platform.x), round(platform.y), round(platform.width),
                  round(platform.height));
end;

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

{TPlayer constructor}
constructor TPlayer.Create(newX, newY, newR, newLk, newRk, newJk: integer; newColour: string);
begin
  x := newX;
  y := newY;
  radius := newR;
  leftKey := newLk;
  rightKey := newRk;
  jumpKey := newJk;
  xSpeed := 0;
  ySpeed := 0;
  grav := 0;
  colour := newColour;
end;

{TPlatform constructor}
constructor TPlatform.create(newX, newY, newW, newH : real);
begin
  x := newX;
  y := newY;
  width := newW;
  height := newH;
end;

end.
Programming - a skill for life!

Seven programs including ALifeSim, Invader, My_first_sdl2 and PlatformDemo by Steven Binns