Page 1 of 3

NPC chasing the player

Posted: Thu Feb 10, 2011 11:56 am
by Head Over Heels
Hello,

I don't want to spam the forum with topics but I am in need of assistance. I'm trying to find examples of a script/function that would allow me to have a number of 'enemies' chase the player around a fixed area/room, basically flocking to wherever the player has moved to. I suppose a bit like they do in Geometry Wars. Can anyone point me in the right direction here?

I'm running through the tutorials but can't really find anything that will help me with this, the closest being the Hamster Ball example but obviously that is designed to enact player control rather than NPC movement. I haven't used LUA for 4 years or so and I've basically forgotten everything!

Any help appreciated.

Re: NPC chasing the player

Posted: Thu Feb 10, 2011 12:47 pm
by nevon
I'm on my phone, so I'm not gonna give you any example code, but the easiest way to solve it is to simply get the vector between the player and each enemy, and then move the enemy along that vector. Of course you'd have to calculate a new vector each time, as long as the player is moving. This simple approach does not take into account any obstacles or walls. For that, you'd have to take a look at pathfinding algorithms.

Re: NPC chasing the player

Posted: Thu Feb 10, 2011 1:51 pm
by Taehl
A couple functions you may want to use:

Code: Select all

-- Returns the distance between two points.
function math.dist(x1,y1, x2,y2) return ((x2-x1)^2+(y2-y1)^2)^0.5 end

-- Returns the angle between two points
function math.getAngle(x1,y1, x2,y2) return math.atan2(x2-x1, y2-y1) end

-- Averages angles.
function math.averageAngles(...)
	local x,y = 0,0
	for i=1,select('#',...) do local a= select(i,...) x, y = x+math.cos(a), y+math.sin(a) end
	return math.atan2(y, x)
end
Then you could do something like this:

Code: Select all

function love.update(dt)
	for k,v in ipairs(NPCs) do
		local a = math.getAngle(v.x, v.y, player.x, player.y)
		v.x = v.x + math.cos(a) * v.speed * dt
		v.y = v.y + math.sin(a) * v.speed * dt
	end
end

Re: NPC chasing the player

Posted: Thu Feb 10, 2011 2:19 pm
by vrld
Taehl wrote:Then you could do something like this:

Code: Select all

stuff
Or you could do it with vectors to avoid trigonometric funtions. Like nevon suggested:

Code: Select all

for _,enemy in ipairs(NPCs) do
    -- direction is the vector from the enemy to the player
    local direction_x = player.x - enemy.x
    local direction_y = player.y - enemy.y
    local distance = math.sqrt(direction_x * direction_x + direction_y * direction_y)

    if dist ~= 0 then -- avoid division by zero
        enemy.x = enemy.x + direction_x / distance * enemy.speed * dt
        enemy.y = enemy.y + direction_y / distance * enemy.speed * dt
    end
end
Shameless plug: With hump's vector class this becomes:

Code: Select all

for _,enemy in ipairs(NPCs) do
    local direction = player.pos - enemy.pos
    enemy.pos = enemy.pos + direction:normalized() * enemy.speed * dt
end

Re: NPC chasing the player

Posted: Thu Feb 10, 2011 2:28 pm
by Taehl
Vrld, wouldn't that let enemies move twice as fast diagonally than they could horizontally or vertically?

Re: NPC chasing the player

Posted: Thu Feb 10, 2011 8:20 pm
by slime
The length of a normalized vector is always 1, so no.

(more on vectors here)

Re: NPC chasing the player

Posted: Sat Feb 12, 2011 8:47 am
by Head Over Heels
Thanks alot guys, I appreciate the help.

Re: NPC chasing the player

Posted: Sat Feb 12, 2011 8:51 pm
by Taehl
That vector stuff sounds neat. Would it be faster than using trig?

Also to test my understanding, this would correctly normalize a list of numbers, yes?

Code: Select all

function math.normalize(...)
	local n, m, s = select('#',...), 0, "return "
	for i=1, n do m = m + select(i,...) end
	for i=1, n-1 do s = s..select(i,...)/m.."," end
	return loadstring(s..select(n,...)/m)()
end
For instance, math.normalize(2,4,10) returns 0.125, 0.25, 0.625. Also, is there a better way to return a vararg expression than my loadstring hack?

Re: NPC chasing the player

Posted: Sat Feb 12, 2011 8:52 pm
by bartbes
A table + unpack? This loadstring stuff is unreadable.

Re: NPC chasing the player

Posted: Sat Feb 12, 2011 9:49 pm
by Robin
A challenge!

Code: Select all

    function math.sum(...)
        if select('#', ...) == 0 then
            return 0
        end
        return (...) + math.sum(select(2, ...))
    end

    function math.normalize(...)
       local total = math.sum(...)
       local t = {...}
       for i = 1, #t do
           t[i] = t[i]/total
       end
       return unpack(t)
    end
Untested.