Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 [Tutorial] "Perfect" Following
Index -> Programming, Turing -> Turing Tutorials
View previous topic Printable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
The_Bean




PostPosted: 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
Sponsor
sponsor
Mackie




PostPosted: 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




PostPosted: 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 Wink

Also, just to clean things up a bit and make it a 'proper' tutorial, read the tutorial about how to make good tutorials Smile ie. using headings and color and such to make it more visually appealing.

+ Karma
swami




PostPosted: 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 Very Happy
LaZ3R




PostPosted: 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 Smile
Nick




PostPosted: Wed Mar 26, 2008 6:33 pm   Post subject: RE:[Tutorial] "Perfect" Following

youLose would be better as a function too
swami




PostPosted: 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




PostPosted: 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
Sponsor
sponsor
richcash




PostPosted: 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




PostPosted: 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

Posted Image, might have been reduced in size. Click Image to view fullscreen.

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:  424 Time(s)

diagram.JPG


r691175002




PostPosted: 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




PostPosted: 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




PostPosted: 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




PostPosted: 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:   
   Index -> Programming, Turing -> Turing Tutorials
View previous topic Tell A FriendPrintable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 1  [ 14 Posts ]
Jump to:   


Style:  
Search: