components used: TImage TButton TTimer

the first method in this tutorial will work with borland kylix

download source code

In the last tutorial you were introduced to Artificial Intelligence. In this tutorial, we're going to make a program that imitates a fly buzzing around a flower. We're going to let the fly 'decide' whether to try and go for the flower, escape the flower or go in any random direction. Why put decide in inverted commas? Well, because technically all it does is select a random number and assign it to a particular action, it's not actually making any decisions itself, but then most games you'll create will need a certain degree of random-ness about them, when a decision can not be made by the program. Read on.

The Random function
Before we go any further, let me introduce you to the random number. The function below should be entered before anything else in your program, just after the implementation and {...} line.

function RandomNumber(const n: integer): integer;
begin
 Result := Trunc(Random*n);
end;

Short, but sweet. This sets up the procedure, which can be used later. If you were to use the line RandomNumber(6) then this would randomly select a number between zero and six, but we'll be coming back to this later. You also need to call the following one line in your FormCreate procedure so that the random number is properly fed a random number.

Randomize;

More than one way to skin a cat...
There are two ways of going about this. Both have their own advantages of which I'll lay out after each example, it's up to you to decide which you would want to use in your own game, but I'd recommend trying them both.

The First Way
To make this program you'll need three components, the TTimer component and two TImage components. The two TImage components will represent the fly and the flower respectively, while the TTimer component will be used to check for different things on regular timings. So here's what to do with them.

Assuming you've entered the random procedure lines, double-click on the TTimer component on your form and enter the lines as shown below. Note, that I've named the TTimer component, LoopTimer, just so that you know.

procedure TForm1.LoopTimerTimer(Sender: TObject);
var
 RandNum: integer;
begin
 RandNum := RandomNumber(5)+ 1;

 // Run Away
 if RandNum = 1 then
  if
Fly.Top = Flower.Top then Fly.Top := Fly.Top + 8
  else
 if Fly.Left = Flower.Left then Fly.Left:=Fly.Left+8;

 // Go for the flower
 if RandNum = 2 then
  if
Fly.Top < Flower.Top then Fly.Top := Fly.Top + 8 
 else
  if
Fly.Left < Flower.Left then Fly.Left := Fly.Left+8 
 else

  if Fly.Top > Flower.Top then Fly.Top := Fly.Top - 8 
 else
  if
Fly.Left > Flower.Left then Fly.Left :=Fly.Left-8;

 // Go Down
 if RandNum = 3 then
  Fly.Top := Fly.Top + 8;

 // Go Up
 if RandNum = 4 then
  Fly.Top := Fly.Top - 8;

 // Go Right
 if RandNum = 5 then
  Fly.Left := Fly.Left + 8;

 // Go Left
 if RandNum = 6 then
  Fly.Left := Fly.Left - 8;

end;

So what does it all do? Well firstly, we've set up a variable in which to place the result of our random number, called RandNum. The line, RandNum := RandomNumber(5)+ 1; places a random number between zero and five and then adds one onto it, so that we effectively end up with any number between one and six. So if the random number picked was four, one would be added onto that to give a result of five. Each of these six numbers represent a different action which the fly can take. The first two represent the fly 'running away' from the flower and going towards the flower, while the final four represent the fly going up, down, left and right. You can see that in the first two cases I've set it so that the fly will effectively go diagonally towards or away from the flower, whereas in the last four cases the fly will go horizontally or vertically. That really is all there is to it and it can create a pretty realistic action of a fly. Feel free to fiddle around with it to get the results you want.

So what are the advantages of this method? Well, the program is potentially free of any bugs, as you can quit out whenever you want, unlike below when you'll have to click on a button to achieve this. What makes this method not so good is the restrictions of the TTimer component in letting your fly, fly further away from the flower, as you can not get one direction to be repeated several times, to allow the fly to fly smoothly away from the flower. The example below gets over that.

The Second Way (Not for Kylix users)
For the second program, you'll need two TImage components and a TButton component, no TTimer component. A lot of the code used in the moving of the fly is similar to the last method, so we'll not go over that again. The same applies for the random procedure, which still needs to be placed at the beginning of your program. You will also need to add the Delay procedure we created in tutorial 6, as shown below, before you continue, as we'll be using that as well.

procedure Delay(Num: longint);
var
 tc: longint;
begin
 tc :=GetTickCount;
 repeat
  Application.ProcessMessages;
 until ((GetTickCount-tc) >= Num);
end;

We're now going to use the Form's event, OnActivate to put the rest of our program in. This basically means that when our program is run, the following will begin.

procedure TForm1.FormActivate(Sender: TObject);
var
 RandNum: integer;
 Counter: integer;
begin
 counter :=0;
 repeat
  Delay(400);
  RandNum := RandomNumber(6)+ 1;

  // Run Away
  repeat
   counter := counter + 1;
   if RandNum = 1 then
    if Fly.Top = Flower.Top then Fly.Top :=Fly.Top + 8
   else

    if Fly.Left=Flower.Left then Fly.Left:=Fly.Left+8;
  until counter = 10;
  counter := 0;

  // Go for the flower
  repeat
   counter := counter + 1;
   if RandNum = 2 then
    if Fly.Top < Flower.Top then Fly.Top :=Fly.Top + 8
   else
    if Fly.Left <Flower.Left then Fly.Left:=Fly.Left+8
   else
    if Fly.Top > Flower.Top then Fly.Top := Fly.Top-8
   else
    if Fly.Left>Flower.Left then Fly.Left:=Fly.Left-8;
  until counter = 10;
  counter := 0;

  // Go Down
  repeat
   counter := counter + 1;
   if RandNum = 3 then
    Fly.Top := Fly.Top + 8;
  until counter = 10;
  counter := 0;

  // Go Up
  repeat
   counter := counter + 1;
   if RandNum = 4 then
    Fly.Top := Fly.Top - 8;
  until counter = 10;
  counter := 0;

  // Go Right
  repeat
   counter := counter + 1;
   if RandNum = 5 then
    Fly.Left := Fly.Left + 8;
  until counter = 10;
  counter := 0;

  // Go Left
  repeat
   counter := counter + 1;
   if RandNum = 6 then
    Fly.Left := Fly.Left - 8;
  until counter = 10;
  counter := 0;

end;

The first thing you'll notice is the introduction of another variable called Counter. This variable is used in our repeat...until lines to decide how many times the loop should be run. You'll notice a number of counter := 0; lines scattered across the program. These are there purely to ensure that after each loop the counter value resets to zero, so as not to cause any errors. So, the main difference between this method and the last is that we now have, as briefly mentioned before, a repeat...until command around each of our six directions. This effectively just repeats the lines it encloses, each time adding one more to the counter variable, and when the number in the counter variable reaches 10, the loop is ended and the program continues. Where the delay procedure comes in is at the beginning of our main loop, which makes sure that when we get to the end of the procedure we start at the top again. The loop causes a delay in between each main loop so that the fly does not move into warp speed and zip about the screen, never to be seen again. Try it without this and you'll see what I mean. The scroll bars will go crazy! A WARNING though, before you do run this program, you should make use of the button we added earlier and add the following line before end; in the above procedure:

until ButtonClick = True;

As well, as this double-click on your button component on your form and enter this line inside that bit of code:

ButtonClick := True;

Finally, add the next line in your var section right at the start of your overall program (underneath the line Form1: TForm1; if your form is called Form1):

ButtonClick: Boolean;

What does all this do? Well, it stops you getting stuck in this program, because when you click on the button the main loop in our FormActivate procedure is broken and only then, can you quit without resorting to either Control, Alt and Delete actions! It should be said that if ever you do find your self in a situation such as this, if you can click back to Delphi, while your running program is going, pushing Ctrl and F2 will reset the program. You can now run the program without having too many problems and you should find that the fly is now free to roam about wherever it pleases. This now brings another problem. Occasionally, the fly will head off the screen and depending on what random actions it takes, may be away a while. This is solved using similar methods to those described in tutorial three, so I'll let you work that out for yourself. If you have real problems let me know.

So that concludes this rather lengthy tutorial. I hope you've found it useful and if you have any problems or if you spot a mistake in this tutorial, let me know by e-mail or the message board.

download source code

Subscribe to the feed delphigamedev.com NEWS Feed
archived news | mailing list

[ all tutorials found at delphigamedev.com are Copyright © 2008 Ben Watt ]