Using Classes and Objects in Smart Pascal

Like a record, a class can have variables and routines. A variable of a class is known as a field, so many programmers begin its name with an F. A routine of a class is known as a method. Usefully, one (derived) class can inherit from another (base) class, as demonstrated on the next page. The derived class can use fields and methods in the base class plus additional fields and methods of its own. In order to use a class, you usually make at least one object (instance) of that class. (Class methods and fields can be used without an instance). The class provides the template for the structure of the object, which takes its own values of fields.

It is useful to have private fields (which you cannot access directly from another unit) or protected fields (which you can access directly only from the same unit or from derived classes). Usually, you must access private data using public methods. In this way, you hide (encapsulate) data and can ensure its validity. You should be able to change the implementation of methods within the 'black box' of the class without rewriting code that uses the class.

We have already shown how to add a procedure and fields to the existing TCanvasProject class. This first example of a new class is for a mobile and is named TMob. (The prefix T denotes a type). This mobile simply moves diagonally for simplicity, but you can experiment with code in the Move procedure. Joel Sutcliffe's RoamingBlockBuster has a most effective procedure for making a target difficult to hit.
unit Unit1;

interface

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

type
  TMob = class
  private
    const WIDTH = 20;
    const HEIGHT = 10;
    X, Y: integer;
    Colour: string;
    procedure Move;
    procedure Draw(Canv: TW3Canvas);
  end;

type
  TCanvasProject = class(TW3CustomGameApplication)
  private
    Mob: TMob;
  protected
    procedure ApplicationStarting; override;
    procedure ApplicationClosing; override;
    procedure PaintView(Canvas: TW3Canvas); override;
  end;

implementation

procedure TMob.Move;
begin
  inc(X);
  inc(Y);
end;

procedure TMob.Draw(Canv: TW3Canvas);
begin
  Canv.BeginPath;
  Canv.FillStyle := Colour;
  Canv.Ellipse(X, Y, X + WIDTH, Y + HEIGHT);
  Canv.Fill;
end;

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
  Mob := new TMob;
  Mob.Colour := 'red';
  GameView.Delay := 20;
  GameView.StartSession(True);
end;

procedure TCanvasProject.ApplicationClosing;
begin
  GameView.EndSession;
  inherited;
end;
 
procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
begin
  // Clear background
  Canvas.FillStyle := 'rgb(0, 0, 99)';
  Canvas.FillRectF(0, 0, GameView.Width, GameView.Height);
  // Draw mobile on the screen
  Mob.Draw(Canvas);
  // Move mobile ready for next frame
  Mob.Move;
end;

end.

Using a Constructor

In the first example we created the object with Mob := new TMob; but could have instead used Mob := TMob.Create;. We then assigned a value to Mob's colour field. More neatly, we can code a constructor with parameters to initialise fields. The constructor may also perform any tasks that the object may require at creation time. The next example uses a constructor to initialise the starting coordinates and colour. It also demonstrates the use of ShowMessage to produce a JavaScript alert.

unit Unit1;

interface

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

type
  TMob = class
  private
    const WIDTH = 20;
    const HEIGHT = 10;
    X, Y: integer;
    Colour: string;
    procedure Move;
    procedure Draw(Canv: TW3Canvas);
    constructor Create(StartX, StartY: integer; StartColour: string);
  end;

type
  TCanvasProject = class(TW3CustomGameApplication)
  private
    Mob: TMob;
  protected
    procedure ApplicationStarting; override;
    procedure ApplicationClosing; override;
    procedure PaintView(Canvas: TW3Canvas); override;
  end;

implementation

constructor TMob.Create(StartX, StartY: integer; StartColour: string);
begin
  X := StartX;
  Y := StartY;
  Colour := StartColour;
  ShowMessage('Creating mobile');
end;

procedure TMob.Move;
begin
  inc(X);
  inc(Y);
end;

procedure TMob.Draw(Canv: TW3Canvas);
begin
  Canv.BeginPath;
  Canv.FillStyle := Colour;
  Canv.Ellipse(X, Y, X + WIDTH, Y + HEIGHT);
  Canv.Fill;
end;

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
  Mob := TMob.Create(50, -10, 'red');
  GameView.Delay := 20;
  GameView.StartSession(True);
end;

procedure TCanvasProject.ApplicationClosing;
begin
  GameView.EndSession;
  inherited;
end;
 
procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
begin
  // Clear background
  Canvas.FillStyle := 'rgb(0, 0, 99)';
  Canvas.FillRectF(0, 0, GameView.Width, GameView.Height);
  // Draw mobile on the screen
  Mob.Draw(Canvas);
  // Move mobile ready for next frame
  Mob.Move;
end;

end.
In this tutorial we show how you can use classes and objects in canvas projects.

Using a TRect Within a Class

The following example shows how you can use the most useful TRect record within a class. Here we use the ContainsRow and ContainsCol functions to detect the GameView borders and on the next page we will use a TRect's Intersects routine. The code follows the application in action. If the application does not work, please use 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.

TRectInClassDemo
unit Unit1;

interface

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

type
  TMob = class
  private
    Rect: TRect;
    Colour: string;
    DeltaX, DeltaY: integer; // Changes in X and Y each move
    constructor Create(StartRect: TRect; StartColour: string; dX, dY: integer);
    procedure Move;
    procedure Negate(Horizontal: Boolean);
    procedure Draw(Canv: TW3Canvas);
  end;

type
  TCanvasProject = class(TW3CustomGameApplication)
  private
    Mob: TMob;
  protected
    procedure ApplicationStarting; override;
    procedure ApplicationClosing; override;
    procedure PaintView(Canvas: TW3Canvas); override;
  end;

implementation

constructor TMob.Create(StartRect: TRect; StartColour: string; dX, dY: integer);
begin
  Rect:= StartRect;
  Colour := StartColour;
  DeltaX := dX;
  DeltaY := dY;
end;

procedure TMob.Move;
begin
  Rect.MoveBy(DeltaX, DeltaY);
end;

procedure TMob.Negate(Horizontal: Boolean);
begin
  if Horizontal then
    DeltaX := -DeltaX
  else
    DeltaY := -DeltaY;
end;

procedure TMob.Draw(Canv: TW3Canvas);
begin
  Canv.FillStyle := Colour;
  Canv.FillRect(Rect);
end;

procedure TCanvasProject.ApplicationStarting;
begin
  inherited;
  var StartRect := TRect.Create(1, 1, 21, 11);
  Mob := new TMob(StartRect, 'red', 2, 2);
  GameView.Delay := 20;
  GameView.StartSession(False);
end;

procedure TCanvasProject.ApplicationClosing;
begin
  GameView.EndSession;
  inherited;
end;
 
procedure TCanvasProject.PaintView(Canvas: TW3Canvas);
begin
  // Clear background
  Canvas.FillStyle := 'rgb(0, 0, 99)';
  Canvas.FillRect(0, 0, GameView.Width, GameView.Height);
  // Draw mobile on the screen
  Mob.Draw(Canvas);
  // Move mobile ready for next frame
  if (Mob.Rect.ContainsCol(0)) or (Mob.Rect.ContainsCol(GameView.Width)) then
    Mob.Negate(true);  // Reverse component of direction along x axis
  if (Mob.Rect.ContainsRow(0)) or (Mob.Rect.ContainsRow(GameView.Height)) then
    Mob.Negate(false); // Reverse component of direction along y axis
  Mob.Move;
end;

end.
Programming - a skill for life!

How to learn the Pascal language the fun way by making games in Smart Mobile Studio