[Tutorial] "Perfect" Following 
	 
	
		| Author | 
		Message | 
	 
		 
		The_Bean
 
  
 
    
		 | 
		
		
			
				  Posted: Tue Mar 25, 2008 9:12 pm    Post subject: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				After watching classmates make countless game where you are being chased by something and have to shoot at it, and my recent Geometry Wars game, I have decided to show you how to make those enemies follow perfectly. 
 
 
Warning: Math Ahead
 
It is advised to have Grade 11 academic math before attempting to understand this, but if you think you can do it, go ahead.
 
 
Part: 1 - What to get away from!
 
 
We all know how to make a ball bounce off the wall. Now people often apply the same idea in trying to get an enemy to follow you or your mouse around the screen.  This is really primitive, but in tile based games works just fine.  But we're not here to talk about those.
 
 
I find alot of people using the simple if my x position is greater then my enemies then my enemies += their speed same thing with the y and in the opposite direction. But doing this makes your enemy only able to travel: N NE E SE S SW W NW.  
 
 
This is a simple program of that:
 
 
	  | Turing: | 	 		   
View.Set ("graphics:max;max,title:Prefect Following,offscreenonly,nobuttonbar")
var xm, ym, bm  : int % Mouse variables
var eTaleLength  : int := 5 % length of the enemys tale
var eX, eY  : array 1 .. eTaleLength  of int % enemy variables
var eSpeed  : int := 5 % enemy speed
var eAngle  : real %enemy angle of travel
eX  (eTaleLength ) := Rand.Int  (eTaleLength,  maxx - eTaleLength ) % making the enemy appear randomly
eY  (eTaleLength ) := Rand.Int  (eTaleLength,  maxy - eTaleLength )
for i  : 1 .. eTaleLength -  1 % preseting the the tale to keep from getting errors
    eX  (i ) := eX  (eTaleLength ) % makes all start at the same spot
    eY  (i ) := eY  (eTaleLength ) %because hes not moving yet :)
end for
proc youLose
     put "He got you"
end youLose
 loop
    cls
    Mouse.Where (xm, ym, bm )
    for i  : 1 .. eTaleLength -  1 % making the tale move by making it = to the one infront of it
        eX  (i ) := eX  (i +  1)
        eY  (i ) := eY  (i +  1)
    end for
    
    if xm > eX  (eTaleLength ) then
        eX  (eTaleLength ) + = eSpeed
     elsif xm < eX  (eTaleLength ) then
        eX  (eTaleLength ) - = eSpeed
     end if
    if ym > eY  (eTaleLength ) then
        eY  (eTaleLength ) + = eSpeed
     elsif ym < eY  (eTaleLength ) then
        eY  (eTaleLength ) - = eSpeed
     end if
    
    for i  : 1 .. eTaleLength  % draws the tale and enemy
        Draw.FillOval (eX  (i ), eY  (i ), i, i,  7)
    end for
    View.Update
    delay (25)
    exit when hasch % prematurely exits when keyboard is hit (nice for testing)
    if Math.Distance (xm, ym, eX  (eTaleLength ), eY  (eTaleLength )) < eTaleLength  then % checks to see if he ketches you (ads a game flare to it)
        youLose  %(Jordan YOU LOSE)
        exit
    end if
end loop
  | 	  
 
 
Now that you know what you use to be doing and what were getting away from lets get to "Perfect" Following
 
 
Part: 2 - The Math
 
 
In grade 11 math you go deeper into trig and how to use it. That is exactly what we are going to do here.
 
 
(I'm going to * as the degree symbol to make it easier and shorter for me to write)
 
Firstly if you noticed before the N NE E... are at 0*, 45*, 90* ... so when trying to get something to follow you perfectly it needs to travel in more degrees than just those.
 
 
We need to find the degree from your enemy to you
 
 
Since were working on the x,y plane we know that  tan Angle =y/x gives you the degree, but the problem is that the origin is in the bottom left and we need the origin to be over the enemy so he will (0,0) and your guy or mouse will be (x,y) so to do this we subtract the enemies x and y from your x and y so your coordinates compared to your enemy are 
 
(mouseX-enemyX,mouseY-enemyY).
 
so now we can use tan=y/x
 
now to put this in turing we need the angle on one side an everything on the other.
 
Angle := tan inverse y/x
 
and in turing 
 
Angle := arctand (y/x)
 
now putting this in a function 
 
 
	  | Turing: | 	 		   
function setAngle  (x, y  : real) : real
    result arctand(y/x )
end setAngle
 
enemyAngle  := setAngle  (mouseX-enemyX,mouseY-enemyY )
  | 	  
 
 
But we can't use this just yet!
 
 
First there are a couple other things that we need to take care of.
 
When x=0 meaning that you and the enemy are on the same x coordinate 
 
you will have y/0 and we know that you can't divide by 0 so when need to take catch that in the function.
 
 
	  | Turing: | 	 		   
function setAngle  (x, y  : real) : real
   if x= 0 and y> 0 then
      result 90
   elsif x= 0 and y< 0 then
      result - 90
   else
      result arctand(y/x )
   end if
end setAngle
 
enemyAngle  := setAngle  (mouseX-enemyX,mouseY-enemyY )
  | 	  
 
 
But we still aren't done!
 
For some reason when y=0 turing can't decide what to do and sometimes he travels away form you and sometimes hes goes towards you.  So I put the case where y=0 in also.
 
 
	  | Turing: | 	 		   
function setAngle  (x, y  : real) : real
   if x= 0 and y> 0 then
      result 90
   elsif x= 0 and y< 0 then
      result - 90
   if x> 0 and y= 0 then
      result 0
   elsif x< 0 and y= 0 then
      result 180
   else
      result arctand(y/x )
   end if
end setAngle
 
enemyAngle  := setAngle  (mouseX-enemyX,mouseY-enemyY )
  | 	  
 
 
And once again still not done.  Doesn't this seam alot easier in class.  The power of the Brain WOW!
 
We also have to consider that tan only gives us something between 90 and -90 so we need to see whta quadrent it is in and then make the necessary change to accomadate.
 
 
	  | Turing: | 	 		   
function setAngle  (x, y  : real) : real % the function that gets the angle from the enemy to your mouse
    if x  = 0 and y  = 0 then
        result 0
    elsif x  = 0 and y >  0 then
        result 90
    elsif x  = 0 and y <  0 then
        result 270
    elsif y  = 0 and x >  0 then
        result 0
    elsif y  = 0 and x <  0 then
        result 180
    elsif x >  0 and y >  0 then
        result arctand (y / x )
    elsif x <  0 and y >  0 then
        result 180 +  arctand (y / x )
    elsif x >  0 and y <  0 then
        result 360 +  arctand (y / x )
    elsif x <  0 and y <  0 then
        result 180 +  arctand (y / x )
    else
        result 0
    end if
end setAngle
 
enemyAngle  := setAngle  (mouseX-enemyX,mouseY-enemyY )
  | 	  
 
 
YAY 
 
 
Your function for getting the angle to which you are from your enemy is now done.  Now to use that angle.
 
 
If you remember form class.
 
You derived from that nice little triangle that to get your (x,y) from a given angle and radius you used:
 
x= r * cos Angle 
 
y= r * sin Angle
 
 
Now we will use r (radius, the hypotinues of the triangle) as the speed of the enemy (how fast he travels around the screen in pixels) and the Angle is what we did before. the x and y you get are the amount that the enemy travels in that given direction.
 
So you add that to their current (x,y) and get their new (x,y) r distance away at the Angle.
 
 
Now to put those in turing your x and y need to be integers remember that.
 
enemyX+= round(cosd(Angle)*enemySpeed)
 
enemyY+= round(sind(Angle)*enemySpeed)
 
 
Now put that into a nice little program add a cool tail a simple game aspect to it and you get something like this:
 
 
	  | Turing: | 	 		   
View.Set ("graphics:max;max,title:Prefect Following,offscreenonly,nobuttonbar")
var xm, ym, bm  : int % Mouse variables
var eTaleLenght  : int := 5 % length of the enemys tale
var eX, eY  : array 1 .. eTaleLenght  of int % enemy variables
var eSpeed  : int := 5 % enemy speed
var eAngle  : real %enemy angle of travel
eX  (eTaleLenght ) := Rand.Int  (eTaleLenght,  maxx - eTaleLenght ) % making the enemy appear randomly
eY  (eTaleLenght ) := Rand.Int  (eTaleLenght,  maxy - eTaleLenght )
for i  : 1 .. eTaleLenght -  1 % preseting the the tale to keep from getting errors
    eX  (i ) := eX  (eTaleLenght ) % makes all start at the same spot
    eY  (i ) := eY  (eTaleLenght ) %because hes not moving yet :)
end for
function setAngle  (x, y  : real) : real % the function that gets the angle from the enemy to your mouse
    if x  = 0 and y  = 0 then
        result 0
    elsif x  = 0 and y >  0 then
        result 90
    elsif x  = 0 and y <  0 then
        result 270
    elsif y  = 0 and x >  0 then
        result 0
    elsif y  = 0 and x <  0 then
        result 180
    elsif x >  0 and y >  0 then
        result arctand (y / x )
    elsif x <  0 and y >  0 then
        result 180 +  arctand (y / x )
    elsif x >  0 and y <  0 then
        result 360 +  arctand (y / x )
    elsif x <  0 and y <  0 then
        result 180 +  arctand (y / x )
    else
        result 0
    end if
end setAngle
 proc youLose
     put "He got you"
end youLose
 loop
    cls
    Mouse.Where (xm, ym, bm )
    for i  : 1 .. eTaleLenght -  1 % making the tale move by making it = to the one infront of it
        eX  (i ) := eX  (i +  1)
        eY  (i ) := eY  (i +  1)
    end for
    eAngle  := setAngle  (xm - eX  (eTaleLenght ), ym - eY  (eTaleLenght )) % gets the angle from enemy to mouse
    eX  (eTaleLenght ) + = round (cosd (eAngle ) * eSpeed ) % uses trig x=cos(angle)*radius | this gives the difference in the direction 
    eY  (eTaleLenght ) + = round (sind (eAngle ) * eSpeed ) % uses trig y=sin(angle)*radius | to move towards the mouse
    for i  : 1 .. eTaleLenght  % draws the tale and enemy
        Draw.FillOval (eX  (i ), eY  (i ), i, i,  31-i* 3)
    end for
    View.Update
    delay (25)
    exit when hasch % prematurely exits when keyboard is hit (nice for testing)
    if Math.Distance (xm, ym, eX  (eTaleLenght ), eY  (eTaleLenght )) < eTaleLenght  then % checks to see if he ketches you (ads a game flare to it)
        youLose  %(Jordan YOU LOSE)
        exit
    end if
end loop
  | 	  
 
 
Now fix up those old basic games you use to make, and know that your enmys no longer look stupid.
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
		 
		Sponsor Sponsor 
		 
  
		 | 
		
 | 
	 
	 
		  | 
	 
				 
		Mackie
 
  
 
    
		 | 
		
		
			
				  Posted: Tue Mar 25, 2008 10:09 pm    Post subject: RE:[Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				This is an excellent tutorial! It's very infomative, and you communicate your ideas very well.
 
 
+20 bits.
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		zylum
 
  
 
    
		 | 
		
		
			
				  Posted: Wed Mar 26, 2008 1:03 am    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				The information presented is very useful, good job! I would however change the title of the tutorial. It describes the thought process of how to derive a function that gives the polar angle between two points, which has many applications, not only enemy following. 
 
 
FYI, there is a simpler method for enemy following using similar triangles. Maybe you can make a second part describing it  
 
 
Also, just to clean things up a bit and make it a 'proper' tutorial, read the tutorial about how to make good tutorials    ie. using headings and color and such to make it more visually appealing. 
 
 
+ Karma
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		swami
 
  
 
    
		 | 
		
		
			
				  Posted: Wed Mar 26, 2008 4:51 pm    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				:O omg.... Amazing!!!! Very well put, it made me think i could fly. :S if thats a good thing  
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		LaZ3R
 
 
 
    
		 | 
		
		
			
				  Posted: Wed Mar 26, 2008 5:28 pm    Post subject: RE:[Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				Very nice and very well coded. 
 
 
The procedure for youLose is kind of pointless in THIS case since it consists of nothing but a single line of output, but if more stuff was added it would make sense  
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		Nick
 
  
 
    
		 | 
		
		
			
				  Posted: Wed Mar 26, 2008 6:33 pm    Post subject: RE:[Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				| youLose would be better as a function too
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		swami
 
  
 
    
		 | 
		
		
			
				  Posted: Mon Mar 31, 2008 5:09 pm    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				| lmao 'youLose' ... awesome Beaner xD http://www.losethegame.com/   <---- inside joke...
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		riveryu
 
  
 
    
		 | 
		
		
			
				  Posted: Sat May 17, 2008 6:14 pm    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				Just for people who want to learn/review some Trigs...
 
 
Dave' Short Trig Course  - good stuff I learned from when i was gr 10 (right now)
 
 University of Guelph Trig Review  - good jam packed summary/review...contains more than you need for this though
 
 
"If you google everything, then you'll get everything."
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
		 
		Sponsor Sponsor 
		 
  
		 | 
		
 | 
	 
	 
		  | 
	 
				 
		richcash
 
 
 
    
		 | 
		
		
			
				  Posted: Sat May 17, 2008 6:31 pm    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				| Trig Without Tears. That's a good approach to trigonometry too. It does not encourage "memorizing symbols in order".
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		Saad
 
  
 
    
		 | 
		
		
			
				  Posted: Sat May 17, 2008 7:32 pm    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				The tutorial is good, and great job on it. Finding the angle is a valid way to do it but why do more work then needed.
 
I shall be going over a method that doesn't directly involve finding the angles but works on similar triangles (its still based on trig but you have no need to know it).
 
 
The Theory
 
 
From the following picture
 
 
 
 
 
We can see that both triangles are similar. Knowing that we can derive the following equations
 
 
 
 
 
For those that don't understand the following
 
 
 
 
More simply put,   is the same as the projected length of the direction and   is the distance from the player and object
 
 
Now knowing this we can re-arrange and find that
 
 
 
And
 
 
 
 
The Code
 
 
And so lets use these equations in some code
 
	  | Turing: | 	 		  View.Set ("offscreenonly")
type Position  :
    record
        x, y  : real
    end record
const ENEMY_MOVEMENT_SPEED  := 1 %% This is the same as the projection length (Same as |Direction|) 
var enemy  : Position  := init (200,  200)
loop
    var mouseX, mouseY, mouseButtonState  : int
    Mouse.Where (mouseX, mouseY, mouseButtonState )
    var distance  := Math.Distance (mouseX, mouseY, enemy.x, enemy.y )
    var deltaX  := mouseX - enemy.x
     var deltaY  := mouseY - enemy.y
     %% Here we plug into the equations
    var xDir  := (deltaX  * ENEMY_MOVEMENT_SPEED ) / distance
     var yDir  := (deltaY  * ENEMY_MOVEMENT_SPEED ) / distance
 
    enemy.x + = xDir
 
    enemy.y + = yDir
     Draw.FillOval (round (enemy.x ), round (enemy.y ), 5,  5,  black)
    View.Update ()
    Time.Delay (10)
    cls ()
end loop
  | 	  
		
	
  
          
						 
	
	
		
	 
	
		|  Description: | 
		
			
		 | 
	 
	
		|  Filesize: | 
		 23.13 KB | 
	 
	
		|  Viewed: | 
		 500 Time(s) | 
	 
	
		
  
 
  | 
	 
	 
	 
			
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		r691175002
 
 
 
    
		 | 
		
		
			
				  Posted: Sat May 17, 2008 8:47 pm    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				| More specifically, what Saad has described is normalizing the displacement vector between you and the target then scalar multiplying it by the speed.
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		richcash
 
 
 
    
		 | 
		
		
			
				  Posted: Sat May 17, 2008 10:55 pm    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				Good job, Saad. And good job The_Bean, you're tutorial is still useful as it shows how to find the polar angle.
 
 
If you look at the The_Bean's method, you'll notice he uses cos(angle) and sin(angle), which we all know is x/radius and y/radius respectively, which leads us to the exact same equations as in Saad's method, so finding the polar angle is extraneous for the purpose of "following". This problem can be thought of in terms of trigonometry, but if you reduce properly you'll lead to doing the same thing as in similar triangles method (which can also be thought of in terms of vectors, as r691175002 pointed out).
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		chipanpriest
 
  
 
    
		 | 
		
		
			
				  Posted: Tue Dec 06, 2011 7:45 pm    Post subject: Re: [Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				| Hey could somebody post a code where there's no tail? :p the tail's messing me up
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		Zren
 
  
 
    
		 | 
		
		
			
				  Posted: Tue Dec 06, 2011 9:06 pm    Post subject: RE:[Tutorial] "Perfect" Following  | 
	
				
				 | 
			 
			 
				
  | 
			 
			
				There was probably a better (or more recent) tutorial I should of linked you to. Oh well. The function setAngle is properly known as atan2 in other languages. Here's an example using radians.
 
 
	  | Turing: | 	 		  
fcn atan2  (x, y  : real) : real
    if x  = 0 and y  = 0 then
        result 0
    elsif x  = 0 and y >  0 then
        result Math.PI /  2
    elsif x  = 0 and y <  0 then
        result Math.PI +  Math.PI /  2
    elsif y  = 0 and x >  0 then
        result 0
    elsif y  = 0 and x <  0 then
        result Math.PI
    elsif x >  0 and y >  0 then
        result arctan (y / x )
    elsif x <  0 and y >  0 then
        result Math.PI +  arctan (y / x )
    elsif x >  0 and y <  0 then
        result Math.PI * 2 +  arctan (y / x )
    elsif x <  0 and y <  0 then
        result Math.PI +  arctan (y / x )
    else
        result 0
    end if
end atan2
 type Point  :
    record
        x, y  : real
    end record
var m  :
    record
        x, y, b  : int
    end record
var p  : Point  := init (100,  100)
var speed  := 1. 5
View.Set ("offscreenonly")
loop
    % Input
    Mouse.Where (m.x, m.y, m.b )
    % Calculate where we will go
    var d  : Point
     if Math.Distance (p.x, p.y, m.x, m.y ) < speed  then
        % Target will be reached this frame
        d.x  := m.x
 
        d.y  := m.y
     else
        % Step towards target
        var angle  := atan2  (m.x - p.x, m.y - p.y )
        d.x  := p.x + speed  * cos (angle )
        d.y  := p.y + speed  * sin (angle )
    end if
    % Collision detection
    % Move to point if no collision
    p := d
     % Render Frame
    cls
    
    Draw.FillOval (round (p.x ), round (p.y ), 3,  3,  black)
    
    View.Update ()
end loop
  | 	  
		
 | 
			 
			
				 | 
			 
		  | 
	 
	 
		 | 
		
		 | 
	 
	  
		  | 
	 
				 
		 | 
	 
 
	
	
	 
	
	 |