 Computer Science Canada Programming C, C++, Java, PHP, Ruby, Turing, VB   Username:   Password: Wiki Blog Search Turing Chat Room Members
[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.

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.    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."    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".   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

diagram.JPG
Description:
Filesize:  23.13 KB
Viewed:  366 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 Display posts from previous: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First         Page 1 of 1  [ 14 Posts ]
 Jump to:  Select a forum  CompSci.ca ------------ - Network News - General Discussion     General Forums   -----------------   - Hello World   - Featured Poll   - Contests     Contest Forums   -----------------   - DWITE   - [FP] Contest 2006/2008   - [FP] 2005/2006 Archive   - [FP] 2004/2005 Archive   - Off Topic     Lounges   ---------   - User Lounge   - VIP Lounge     Programming -------------- - General Programming     General Programming Forums   --------------------------------   - Functional Programming   - Logical Programming   - C     C   --   - C Help   - C Tutorials   - C Submissions   - C++     C++   ----   - C++ Help   - C++ Tutorials   - C++ Submissions   - Java     Java   -----   - Java Help   - Java Tutorials   - Java Submissions   - Ruby     Ruby   -----   - Ruby Help   - Ruby Tutorials   - Ruby Submissions   - Turing     Turing   --------   - Turing Help   - Turing Tutorials   - Turing Submissions   - PHP     PHP   ----   - PHP Help   - PHP Tutorials   - PHP Submissions   - Python     Python   --------   - Python Help   - Python Tutorials   - Python Submissions   - Visual Basic and Other Basics     VB   ---   - Visual Basic Help   - Visual Basic Tutorials   - Visual Basic Submissions     Education ----------- - Student Life   Graphics and Design ----------------------- - Web Design     Web Design Forums   ---------------------   - (X)HTML Help   - (X)HTML Tutorials   - Flash MX Help   - Flash MX Tutorials   - Graphics     Graphics Forums   ------------------   - Photoshop Tutorials   - The Showroom   - 2D Graphics   - 3D Graphics     Teams ------ - dTeam Public

 Style: Appalachia blueSilver eMJay subAppalachia subBlue subCanvas subEmjay subGrey subSilver subVereor Search: