Share Your Code

Follow (and avoid) Object

Posted by canupa.com, Posted on January 18, 2012, Last updated April 30, 2012

I ported this nice AS2 follow-object script by Philip Radvan
see the original post here http://www.freeactionscript.com/2009/04/enemy-behavior-run-away-follow-player/

fin.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
local fin = {}
local radians = 180/math.pi  --> precalculate radians
 
fin.distanceBetween = function ( pos1, pos2 )
         local sqrt = math.sqrt
        if not pos1 or not pos2 then
                return
        end
        if not pos1.x or not pos1.y or not pos2.x or not pos2.y then
                return
        end
        local factor = { x = pos2.x - pos1.x, y = pos2.y - pos1.y }
        return sqrt( ( factor.x * factor.x ) + ( factor.y * factor.y ) )
end
 
 
 
fin.doFollow = function (follower, targetObject, missileSpeed, turnRate, doRotate, runAway)
 
        local missileSpeed = missileSpeed or 8
        local turnRate = turnRate or 0.8
      
        -- get distance between follower and target
        
        local target = targetObject
        
        local distanceX = target.object.x - follower.object.x;
        local distanceY = target.object.y - follower.object.y;
        
        -- get total distance as one number
        local distanceTotal = fin.distanceBetween (follower.object, target.object)
        
        -- calculate how much to move
         local moveDistanceX = turnRate * distanceX / distanceTotal;
         local moveDistanceY = turnRate * distanceY / distanceTotal;
        
        -- increase current speed
        follower.moveX = follower.moveX + moveDistanceX; 
        follower.moveY = follower.moveY + moveDistanceY;
                
        -- get total move distance
        local totalmove = math.sqrt(follower.moveX * follower.moveX + follower.moveY * follower.moveY);
        
        -- apply easing
        follower.moveX = missileSpeed*follower.moveX/totalmove;
        follower.moveY = missileSpeed*follower.moveY/totalmove;
        
        -- move follower (or runner)
        
        if runAway then
                follower.object.x = follower.object.x - follower.moveX;
                follower.object.y = follower.object.y - follower.moveY;
                if doRotate == true then
                  follower.object.rotation = math.atan2(follower.moveY, follower.xMove) * radians;
                end
        else
                follower.object.x = follower.object.x + follower.moveX;
                follower.object.y = follower.object.y + follower.moveY;
                if doRotate == true then
                  follower.object.rotation = (math.atan2(follower.moveY, follower.moveX) * radians)+180;
                end
        end
 
        -- !!!!! you got to check if we hit the target - here or in main game logic !!!!!
 
end
return fin

usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
-- require the module
local  fin = require("fin")
 
-- fin.doFollow (missileDisplayObject,  targetDisplayObject [, missileSpeed, turnRate, doRotate, runAway])
-- example:
 
local obj1 = {}
local obj2 = {}
obj1.object = display.newCircle( 200, 200, 20 ) -- touch object
obj2.object = display.newCircle( 200, 200, 5 ) -- follower object
 
-- you also have to setup initial speed for each follower object
obj2.moveX = 2
obj2.moveY = 2
 
function mainLogic () 
-- in your game logic loop call the function fin.doFollow
-- follower and target must be display object tables!
-- tinker with missileSpeed and turnRate
 
    -- example 1:
    -- obj2 runs away from obj1, rotating
    fin.doFollow (obj2, obj1, 5, 1, true, true) 
 
    -- example 2:
    -- obj2 follows obj1, rotating
    -->  fin.doFollow (obj2, obj1, 5, 1, true, false)
 
end
 
function onTouch( event )
local phase = event.phase
                if "began" == phase or "moved" == phase then
                                obj1.object.x = event.x
                                obj1.object.y = event.y         
                elseif  "cancelled" == phase then
                end
end
 
 Runtime:addEventListener( "enterFrame", mainLogic );
 Runtime:addEventListener( "touch"         , onTouch )

If you want to see this in action, just check out our game
PARALLAX http://youtu.be/rCtAaB-ct30
this function makes some nice heat seeking missiles :)

any questions?

-finefin

EDIT: here's a sample project. http://dl.dropbox.com/u/4175049/_fixed/doFollow.zip
I also edited the example above.

EDIT II: added doRotate and runAway parameters, also changed the algorithms to use precalculated radians

EDIT III: fixed yMove -> moveY


Replies

nicholasclayg
User offline. Last seen 6 days 18 hours ago. Offline
Joined: 16 May 2011

Could you add some sample display objects (rects, circles or whatever) to demonstrate this?

I was looking at it, and was a bit confused :)

ng

canupa.com
User offline. Last seen 10 weeks 5 hours ago. Offline
Joined: 20 Jun 2011

I edited my post, you should get it now ;)

wintermuteai1
User offline. Last seen 1 year 2 weeks ago. Offline
Joined: 10 May 2011

Very useful, tyvm!

mila.habaer
User offline. Last seen 32 weeks 19 hours ago. Offline
Joined: 13 Aug 2011

Hi canupa,

Thanks for sharing.

Can your code be used for my game for free?

Thanks

canupa.com
User offline. Last seen 10 weeks 5 hours ago. Offline
Joined: 20 Jun 2011

@mila,

long(ish) answer: feel free to copy, paste, rewrite, tweak, evolve, share, use, publish, utilize, avail, devote yourself to, link to and talk about it! As stated, credits go to Mr. Radvan from freeactionscript.com - you can find some game code gold there and ActionScript isn't much different than LUA. Porting that function was quite a no-brainer...

short answer: absolutely! ;)

have fun
-finefin

mila.habaer
User offline. Last seen 32 weeks 19 hours ago. Offline
Joined: 13 Aug 2011

Hi Manupa, than k you soooo much.

I am testing your sample project, I like to have more followerObject, say I added one more ,followerObject4.object, but when I run the program, the one I added not showing up, what I am doing wrong, pls see the code:(Thanks in advance)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
local  fin = require("fin")
 
 
-- create some objects
 
local touchObject = {}
local followerObject = {}
local followerObject2 = {}
local followerObject3 = {}
 
local followerObject4 = {}
 
touchObject.object = display.newCircle( display.contentWidth/2, display.contentHeight/2, 20 )
 
followerObject.object = display.newCircle( 200, 200, 8 )
followerObject2.object = display.newCircle( 200, 200, 4 )
followerObject3.object = display.newCircle( 200, 200, 12 )
followerObject4.object = display.newCircle( 200, 200, 16 )
 
-- define individual/initial speed
followerObject.moveX = 2
followerObject.moveY = 2
followerObject2.moveX = -2
followerObject2.moveY = -2
followerObject3.moveX = 2
followerObject3.moveY = 2
 
followerObject4.moveX = 2
followerObject4.moveY = 2
 
 
 
 
 function mainLogic ()
 
        
        -- fin.doFollow (missileDisplayObject,  targetDisplayObject [, missileSpeed, turnRate])
                fin.doFollow (followerObject,  touchObject ,5, 1)
                fin.doFollow (followerObject2,  touchObject ,10, 1.2)
                fin.doFollow (followerObject3,  touchObject ,3, 0.3)
                fin.doFollow (followerObject4,  touchObject ,12, 0.8)
 
 end
 
function onTouch( event )
local phase = event.phase
                if "began" == phase or "moved" == phase then
 
                                touchObject.object.x = event.x
                                touchObject.object.y = event.y
                
                elseif  "cancelled" == phase then
                end
end
 
 Runtime:addEventListener( "enterFrame", mainLogic );
 Runtime:addEventListener( "touch"         , onTouch )

mila.habaer
User offline. Last seen 32 weeks 19 hours ago. Offline
Joined: 13 Aug 2011

Hi canupa.com,

Please disregard my previous question, it works fine, My bad, I did something wrong, I added more FollowerObjects, working fine......

naveen_pcs
User offline. Last seen 1 hour 38 min ago. Offline
Joined: 9 Apr 2011

Wow, I've been looking for something like this for a while!

Thanks so much! I'll try it out tonight and hopefully everything goes smoothly :)

mroberti
User offline. Last seen 1 week 3 days ago. Offline
Joined: 20 Nov 2010

I'd really like to see enemy avoidance added to this lib. i'd kick you down some $$ via paypal to make it worth your while. :) Always loved the flocking/avoidance algorithms!!

canupa.com
User offline. Last seen 10 weeks 5 hours ago. Offline
Joined: 20 Jun 2011

@mroberti + all

I added a runAway flag in the doFollow function. This, again, was quite a no-brainer (just take a look at line 49ff. - really simple!)

so I give it to you for free ;)

If you want to say thank you and support us, just load/buy our apps and leave some nice reviews

http://itunes.apple.com/de/artist/canupa/id334723985

THAT would be really appreciated!

cheers!
-finefin

mroberti
User offline. Last seen 1 week 3 days ago. Offline
Joined: 20 Nov 2010

[SOLVED]

Misnamed xMove and moveX. :) Rename them all the same throughout the code and it works perfectly!

canupa.com
User offline. Last seen 10 weeks 5 hours ago. Offline
Joined: 20 Jun 2011

@mroberti oh right, I forgot about that ;) ...fixed

mroberti
User offline. Last seen 1 week 3 days ago. Offline
Joined: 20 Nov 2010

Hey just checking, I've tried on a pretty new build and I'm getting

1
2
x ..-1.#IND
y ..-1.#IND

when I print out the follower's x and y. Any clues as to where I'm screwing up? I haven't tried this in a while!

yogi-s
User offline. Last seen 16 weeks 5 days ago. Offline
Joined: 30 Jul 2012

can I get the follower's rotation value?

canupa.com
User offline. Last seen 10 weeks 5 hours ago. Offline
Joined: 20 Jun 2011

take a look at line 62

1
2
3
 if doRotate == true then
       follower.object.rotation = (math.atan2(follower.moveY, follower.moveX) * radians)+180;
end

you could do something like this
1
2
3
4
5
 if doRotate == true then
     local rotationValue = (math.atan2(follower.moveY, follower.moveX) * radians)+180
     follower.object.rotation = rotationValue
     print ("rotationValue: "..rotationValue)
end

-finefin