Delphi Tutorial 4: Reaction Timer
In this tutorial you will create a simple game that measures reaction time. The interface makes extensive use of panel objects as status indicators. It also uses a timer control, an image control and two SpeedButtons. This exercise requires great attention to detail in setting the properties of the objects on the screen.
Operation
The program shows and hides an image at random locations within an invisible grid on the screen. While the image is visible it can be “hit” by clicking on it with the mouse button. If the mouse button is clicked on any other part of the screen a “miss” is recorded. If no attempt to click the mouse button is made while the image is visible a “timeout” is recorded. A timer event is used control the rate at which the image moves. “Hits”, “misses” and “timeouts” are recorded in status boxes at the bottom of the screen. After a set number of images are shown the program stops and the average “hit time” is shown.
Program Interface
The main form for the program is shown in the screenshot . There are seven panel components on the form although it is possible to see only six of them. Panel1 is used as a container for panels 2-6 and it is essential, when creating the form, to place these panels “inside” panel 1.
Create the form as follows
Left = 241
Top = 156
BorderStyle = bsDialog
Caption = 'Decide this for yourself‘
ClientHeight = 188
ClientWidth = 404
Left = 0
Top = 152
Width = 404
Height = 36
Align = alBottom
Caption = 'Panel1'
| Panel2 |
Panel3 |
Panel4 |
Panel5 |
Panel6 |
|
| Left |
1 |
66 |
121 |
181 |
251 |
| Top |
1 |
1 |
1 |
1 |
1 |
| Width |
65 |
55 |
60 |
70 |
152 |
| Height |
34 |
34 |
34 |
34 |
34 |
| Align |
alLeft |
alLeft |
alLeft |
alLeft |
alClient |
| Alignment |
taLeftJustify |
taLeftJustify |
taLeftJustify |
taLeftJustify |
taLeftJustify |
| BevelInner |
bvLowered |
bvLowered |
bvLowered |
bvLowered |
bvLowered |
| Caption |
'Panel2' |
'Panel3' |
'Panel4' |
'Panel5' |
'Panel6' |
Note the Align property of each panel - Panel6 is aligned to the client (Panel1) - this is to say it fills the space between the right margin of Panel1 (which is effectively the right margin of the form) and the right margin of Panel5.
Left = 0 Top = 0 Width = 39 Height = 152 Align = alLeft
Panel7 is the container for the SpeedButtons and the Edit Box. These controls must not be added to the form itself.
Edit1: Left = 5 Top = 67 Width = 25 Height = 21 Hint = 'Delay = value x 100 (ms)'
Text = '20'
SpeedButton1: Left = 5 Top = 6 Width = 25 Height = 25 Hint = 'Start Game'
NumGlyphs = 2 ParentShowHint = True ShowHint = True OnClick = StartClick
SpeedButton2: Left = 5 Top = 36 Width = 25 Height = 25 Hint = 'Stop Game'
NumGlyphs = 2 ParentShowHint = True ShowHint = True OnClick = StopClick
Program Code
procedure TForm1.MoveBall;
var
NewSlot: Integer;
begin
repeat
NewSlot := Random (144);
until NewSlot <> slot;
slot := NewSlot;
Ball.Left := (slot mod 12) * dx + Panel7.Width;
Ball.Top := (slot div 12) * dy;
Ball.Show;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
dx := Ball.Height;
dy := Ball.Width;
total_hit:=0;total_miss:=0;
ClientWidth := dx * 12 + Panel7.Width;
ClientHeight := dy * 12 + Panel1.Height;
Randomize;
Panel2.Caption:='';
Panel3.Caption:='';
Panel4.Caption:='';
Panel5.Caption:='';
Panel6.Caption:='';
Ball.Hide;
end;
procedure TForm1.BallClick(Sender: TObject);
var this_time:longint;
begin
if running then
begin
this_time:=GettickCount-tstart;
if sender is TForm1 then
begin
Inc(Misses);
total_miss:=total_miss+this_time;
Panel4.Caption:=' Misses:'+InttoStr(Misses);
Panel6.Caption:=' Miss in '+InttoStr(this_time)+' ms';
end
else if Sender is TImage then
begin
Inc(Hits);
total_hit:=total_hit+this_time;
Panel3.Caption:=' Hits:'+InttoStr(Hits);
Panel6.Caption:=' Hit in '+InttoStr(this_time)+' ms';
Messagebeep(0);
end;
running:=false;
Ball.Hide;
end;
end;
procedure TForm1.StartClick(Sender: TObject);
begin
Balls:=10;
hits:=0;
misses:=0;
total_hit:=0;
total_miss:=0;
timeouts:=0;
Panel2.Caption:=' Targets:'+InttoStr(Balls);
Panel3.Caption:=' Hits:'+InttoStr(Hits);
Panel4.Caption:=' Misses:'+InttoStr(Misses);
Panel5.Caption:=' Timeouts:'+InttoStr(Timeouts);
Panel6.Caption:=' ';
Timer1.Interval:=Trunc(StrtoFloat(Edit1.Text)*100);
Moveball;
Timer1.Enabled:=true;
running:=true;
tstart:=GetTickCount;
end;
procedure TForm1.StopClick(Sender: TObject);
begin
Timer1.Enabled:=False;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var average:real;
begin
if running then
begin
Inc(Timeouts);
Panel5.Caption:=' Timeouts:'+InttoStr(Timeouts);
running:=false;
end;
Dec(Balls);
Panel2.Caption:=' Targets:'+InttoStr(Balls);
if Balls > 0
then
begin
Moveball;
tstart:=GetTickCount;
running:=true;
end
else
begin
timer1.Enabled:=false;
running:=false;
Panel2.Caption:=' Finished';
if hits>0 then
begin
average:=total_hit/hits;
Panel6.Caption:=' Average Hit Time = '
+FloattoStrF(average,fffixed,8,2)+ ' ms';
end;
Ball.Hide;
end;
end;
end.