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.
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.
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.