Using the Intersect method of TRect to detect Collisions
Introduction
A TRect (defined first in the W3System unit and now in System.Types.Graphics) is a record with useful methods such as Create, CenterPoint, ContainsPoint, Intersect and MoveBy. In the first demo, use the mouse to move the yellow rectangle and find the hidden rectangle. The area of any overlap between the yellow and hidden rectangle will be green.
If IntersectDemo does not run in your current browser after clicking on the display, 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.
The Code
See in the following code that the Intersect method of a TRect takes as parameters (1) the other TRect being tested for collision and (2) a var parameter for the TRect of the intersect region. The method returns true if there is any overlap. When using Version 3 of Smart Mobile Studio, add System.Types.Graphics to the uses clause.
unit Unit1; interface uses System.Types, SmartCL.System, SmartCL.Components, SmartCL.Application, SmartCL.Game, SmartCL.GameApp, SmartCL.Graphics; type TCanvasProject = class(TW3CustomGameApplication) private MobX, MobY : integer; StatRect : TRect; const MOB_WIDTH = 30; MOB_HEIGHT = 20; public procedure ApplicationStarting; override; procedure ApplicationClosing; override; procedure PaintView(Canvas: TW3Canvas); override; end; implementation procedure TCanvasProject.ApplicationStarting; begin inherited; GameView.Width := 200; GameView.Height := 300; MobX := MOB_WIDTH div 2; MobY := MOB_HEIGHT div 2; StatRect := TRect.Create(100, 200, 130, 230); GameView.OnMouseMove := procedure(o : TObject; ss : TShiftState; x, y : integer) begin MobX := x; MobY := y; end; GameView.Delay := 1; GameView.StartSession(True); end; procedure TCanvasProject.PaintView(Canvas: TW3Canvas); var MobRect, intersectRect: TRect; begin // Clear background with colour teal. Canvas.FillStyle := 'gray'; Canvas.FillRect(0, 0, GameView.Width, GameView.Height); //Draw yellow rect Mobrect := TRect.Create(MobX - MOB_WIDTH div 2, MobY - MOB_HEIGHT div 2, MobX + MOB_WIDTH div 2, MobY + MOB_HEIGHT div 2); Canvas.FillStyle := 'yellow'; Canvas.FillRectF(MobRect); //leftX, topY, rightX, bottomY if mobRect.Intersect(StatRect, intersectRect) then begin Canvas.FillStyle := 'green'; Canvas.FillRectF(intersectRect); //leftX, topY, rightX, bottomY end; end; procedure TCanvasProject.ApplicationClosing; begin GameView.EndSession; inherited; end; end.
Using an array of TRect
This demonstration is the start of a maze type of game in which the player is prevented from overlapping with a wall. Use the wasd or arrow keys to navigate through the spaces between the thin rectangles. The concise code checks for a collision with each rectangle - even those with which the player has no chance of intersecting. If the response became sluggish you could choose which wall(s) to check.
If IntersectDemo2 does not run in your current browser after clicking on the display, 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.
Project File for Version 3.0 of Smart Mobile Studio.
Not in this trimmed-down project file that code packing and obfuscation are disabled; enabling either has the unwanted effect of disabling the key presses.
<SMART> <Project version="3" subversion="0"> <Name>IntersectDemo</Name> <Options> <Compiler /> <Codegen> <Obfuscation>0</Obfuscation> <InstanceChecking>0</InstanceChecking> <MainBody>1</MainBody> <CodePacking>0</CodePacking> <SmartLinking>1</SmartLinking> </Codegen> <ConditionalDefines /> <Linker> <EmbedJavaScript>1</EmbedJavaScript> </Linker> <Output> <HtmlFileName>IntersectDemo.html</HtmlFileName> <OutputFilePath>www\</OutputFilePath> </Output> <Import /> <Execute /> </Options> <Files> <File type="main"> <Name>IntersectDemo</Name> <Source> <![CDATA[uses Unit1; var Application: TCanvasProject; Application := TCanvasProject.Create; Application.RunApp; ]]> </Source> </File> <File type="unit"> <Name>Unit1</Name> <Source> <![CDATA[
unit Unit1; interface uses System.Types.Graphics, SmartCL.System, SmartCL.Components, SmartCL.Application, SmartCL.Game, SmartCL.GameApp, SmartCL.Graphics; type TDirection = (Up, Down, Left, Right); TCanvasProject = class(TW3CustomGameApplication) private const PLAYER_SIZE = 4; const WALL_WIDTH = 4; Rects: array[1..9] of TRect; PlayerRect, TestRect, OverlapRect: TRect; MoveAttempt: Boolean; Direction: TDirection; MoveDist = 4; Gap, Moves: integer; protected procedure ApplicationStarting; override; procedure ApplicationClosing; override; procedure PaintView(Canvas: TW3Canvas); override; procedure KeyDownEvent(mCode: integer); end; implementation procedure TCanvasProject.KeyDownEvent(mCode: integer); begin MoveAttempt := True; case mCode of 27: Application.Terminate; 37, 65: Direction := Left; 38, 87: Direction := Up; 39, 68: Direction := Right; 40, 83: Direction := Down; 0: MoveAttempt := False; //Initialization end; end; procedure TCanvasProject.ApplicationStarting; begin inherited; asm window.onkeydown=function(e) { TCanvasProject.KeyDownEvent(Self,e.keyCode); } end; KeyDownEvent(0); Gap := 3 * PLAYER_SIZE; PlayerRect := TRect.Create(0, WALL_WIDTH,PLAYER_SIZE, WALL_WIDTH + PLAYER_SIZE); // Top, bottom, left and right boundaries Rects[1] := TRect.Create(0, 0, GameView.Width, WALL_WIDTH); Rects[2] := TRect.Create(0, GameView.Height - WALL_WIDTH, GameView.Width - WALL_WIDTH - 3 * PLAYER_SIZE, GameView.Height); Rects[3] := TRect.Create(0, WALL_WIDTH + 3 * PLAYER_SIZE, WALL_WIDTH, GameView.Height - WALL_WIDTH); Rects[4] := TRect.Create(GameView.Width - WALL_WIDTH, WALL_WIDTH, GameView.Width, GameView.Height - WALL_WIDTH); //2 Horizontal walls with two gaps Rects[5] := TRect.Create(WALL_WIDTH, WALL_WIDTH + Gap, GameView.Width div 3, 2 * WALL_WIDTH + Gap); Rects[6] := TRect.Create(GameView.Width div 3 + Gap, WALL_WIDTH + Gap, GameView.Width - Gap - WALL_WIDTH, 2 * WALL_WIDTH + Gap); Rects[7] := TRect.Create(WALL_WIDTH + Gap, 2 * (WALL_WIDTH + Gap), GameView.Width div 2, 3 * WALL_WIDTH + 2 * Gap); Rects[8] := TRect.Create(GameView.Width div 2 + Gap, 2 * (WALL_WIDTH + Gap), GameView.Width - WALL_WIDTH, 3 * WALL_WIDTH + 2 * Gap); // Vertical wall Rects[9] := TRect.Create(GameView.Width div 2 - WALL_WIDTH, 2 * WALL_WIDTH + Gap, GameView.Width div 2, GameView.Height - WALL_WIDTH - Gap); GameView.Delay := 20; GameView.StartSession(False); end; procedure TCanvasProject.ApplicationClosing; begin GameView.EndSession; inherited; end; procedure TCanvasProject.PaintView(Canvas: TW3Canvas); var Overlap: Boolean; begin // Clear background Canvas.FillStyle := 'lightgray'; Canvas.FillRectF(0, 0, GameView.Width, GameView.Height); Canvas.FillStyle := 'rgb(255, 110, 110)'; for var r in Rects do Canvas.FillRectF(r); if MoveAttempt = True then //Key pressed begin inc(Moves); TestRect := PlayerRect; case Direction of Up: TestRect.MoveBy(0, -MoveDist); Left: TestRect.MoveBy(-MoveDist, 0); Right: TestRect.MoveBy(MoveDist, 0); Down: TestRect.MoveBy(0, MoveDist); end; //end of case Overlap := False; for var r in Rects do if TestRect.Intersect(r, OverlapRect) then Overlap := True; if not Overlap then PlayerRect := TestRect; //Otherwise PlayerRect is unchanged by attempted move end; //end of if MoveAttempt Canvas.FillStyle := 'blue'; Canvas.FillRectF(PlayerRect); if PlayerRect.Top > GameView.Height - WALL_WIDTH then begin ShowMessage('Moves: ' + IntToStr(Moves)); Application.Terminate; end; MoveAttempt := False; end; end.
]]> </Source> </File> </Files> <Target>Browser</Target> <Generator>Canvas Project</Generator> </Project> </SMART>