Mega Code Archive

 
Categories / Delphi / Examples
 

Threads balloon experiment

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.