What is threads good for? If you wonder, maybe this example will give you a clue. It would have been difficult to accomplish something like this without using threads.
The example will create a balloon each time the button is pressed, and it will start to slowly descend towards ground. A random side movement is applied during descending, to give the balloon a "wacky" feeling. Each balloon will have it's own "life", within his thread. For each new balloon-position the program will check that it doesn't collide with others. In that case another position will be calculated until a free spot is found. After a while the balloon will land and the thread will be terminated.
All thread-activity that results in changes on the form canvas, must be timed with other window activities in the main thread. The Synchronize command takes care of that part, and we don't need to bother to much about it. All we have to do is make sure that all things that goes to the window is put in separate procedures called by Synchronize. Note: You can't pass any variables to a procedure being called with Synchronize. You have to declare these as private within the thread.
The program is using three bitmaps, one for the balloon, one "splash"-picture and one with just the background color.
If you are of the destructive type, you can aim at a balloon and shoot it down by pressing the left mouse-key.
If you want the project files, press here:
{-----------------------------------------------------------------------------
Unit Name: Unit1
Author: Mats Asplund
Purpose: Threading example
History:
-----------------------------------------------------------------------------}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TBalloonThread = class(TThread)
private
fBalloon, fNothing, fSplash: TBitmap;
fBalloonNo, fWidth, fHeight, x, y, xold, yold, xtmp, ytmp: Integer;
fOK, fHit: Boolean;
procedure Draw;
procedure DrawSplash;
procedure EraseOldPos;
procedure CheckPos;
protected
procedure Execute; override;
constructor Create(BalloonNo, Width, Height: Integer;
BitmapFile1, BitmapFile2, BitMapFile3: TFileName);
end;
TForm1 = class(TForm)
Button1: TButton;
Shape1: TShape;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
BWidth, BHeight, BalloonNo: Integer;
BallonPositions: array[0..99] of array[0..1] of Integer;
Hit: array[0..99] of boolean;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TBallonThread }
procedure TForm1.FormCreate(Sender: TObject);
var
Bitmap: TBitmap;
n: Integer;
begin
// Initialize
BalloonNo:= 0;
for n:= Low(Hit) to High(Hit) do
begin
BallonPositions[n, 0]:= 0;
BallonPositions[n, 1]:= 0;
Hit[n]:= false;
end;
// Check bitmap width and height.
// All three bitmaps should be of same size.
Bitmap:= TBitmap.Create;
Bitmap.LoadFromFile('balloon.bmp');
BWidth:= Bitmap.Width;
BHeight:= Bitmap.Height;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// Create one Balloon-instance.
TBalloonThread.Create(BalloonNo, BWidth, BHeight,
'balloon.bmp', 'nothing.bmp', 'splash.bmp');
Inc(BalloonNo);
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
n: integer;
begin
// Check every balloonposition to see if there's a hit.
for n:= 0 to BalloonNo -1 do
begin
if (abs(BallonPositions[n, 0]- X) < BWidth + 1) and
(abs(BallonPositions[n, 1]- Y) < BHeight + 1) and
((BallonPositions[n, 0]- X) <= 0) and
((BallonPositions[n, 1]- Y) <= 0) then
Hit[n]:= true;
end;
end;
constructor TBalloonThread.Create(BalloonNo, Width, Height: Integer;
BitmapFile1, BitmapFile2, BitmapFile3: TFileName);
begin
inherited Create(true);
fBalloonNo:= BalloonNo;
fWidth:= Width;
fHeight:= Height;
fHit:= false;
// Create bitmaps needed
fBalloon:= TBitmap.Create;
fBalloon.LoadFromFile(BitmapFile1);
fBalloon.TransParentColor:= fBalloon.Canvas.Pixels[0, 0];
fBalloon.Transparent:= true;
fNothing:= TBitmap.Create;
fNothing.LoadFromFile(BitmapFile2);
fSplash:= TBitmap.Create;
fSplash.LoadFromFile(BitmapFile3);
fSplash.TransParentColor:= fSplash.Canvas.Pixels[0, 0];
fSplash.Transparent:= true;
FreeOnTerminate:= true;
Resume;
end;
procedure TBalloonThread.Execute;
begin
fOK:= false;
// First position on screen
while not fOK do
begin
xtmp:= Random(600) + 50;
ytmp:= 0;
Synchronize(CheckPos);
end;
x:= xtmp;
y:= ytmp;
// Main loop
while not Terminated do
begin
// The balloon has landed. Terminate.
if y > 313 then
begin
y:= 313;
Terminate;
end;
// Erase old position - draw new
Synchronize(EraseOldPos);
Synchronize(Draw);
Sleep(400);
xold:= x;
yold:= y;
fOK:= false;
while not fOK do
begin
// Calculate new position
if Random(2) = 0 then
xtmp:= x + Random(3)
else
xtmp:= x - Random(3);
if Random(7) < 6 then
ytmp:= y + Random(3)
else
ytmp:= y - Random(3);
Synchronize(CheckPos);
// Balloon is hit. Draw splash, then terminate.
if fHit then
begin
Synchronize(EraseOldPos);
Synchronize(DrawSplash);
Sleep(200);
Synchronize(EraseOldPos);
Terminate;
end;
end;
x:= xtmp;
y:= ytmp;
end;
end;
procedure TBalloonThread.CheckPos;
var
n: Integer;
begin
// Check that new position(xtmp, ytmp) doesn't interfere with
// any other balloons position.
for n:= 0 to Form1.BalloonNo - 1 do
begin
if n <> fBalloonNo then
begin
if (abs(xtmp - Form1.BallonPositions[n, 0]) < fWidth) and
(abs(ytmp - Form1.BallonPositions[n, 1]) < fHeight) then
begin
fOK:= false;
Exit;
end;
end;
end;
// Check if hit
if Form1.Hit[fBalloonNo] then
fHit:= true;
// ...It didn't interfere. Set new position.
Form1.BallonPositions[fBalloonNo, 0]:= xtmp;
Form1.BallonPositions[fBalloonNo, 1]:= ytmp;
fOK:= true;
end;
procedure TBalloonThread.Draw;
begin
// Draw balloon in new position
Form1.Canvas.Draw(x, y, fBalloon);
end;
procedure TBalloonThread.EraseOldPos;
begin
// Draw "nothing" at previous position
Form1.Canvas.Draw(xold, yold, fNothing);
end;
procedure TBalloonThread.DrawSplash;
begin
// Draw splashpicture. Set position outside screen.
Form1.Canvas.Draw(x, y, fSplash);
Form1.BallonPositions[fBalloonNo, 0]:= -fWidth;
Form1.BallonPositions[fBalloonNo, 1]:= -fHeight;
end;
end.