NPC chasing the player

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Prole
Posts: 9
Joined: Wed Feb 09, 2011 4:40 pm

NPC chasing the player

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.

nevon
Commander of the Circuloids
Posts: 938
Joined: Thu Feb 14, 2008 8:25 pm
Location: Stockholm, Sweden
Contact:

Re: NPC chasing the player

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.

Taehl
Dreaming in associative arrays
Posts: 1024
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: NPC chasing the player

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

Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.

vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: NPC chasing the player

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
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine

Taehl
Dreaming in associative arrays
Posts: 1024
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: NPC chasing the player

Vrld, wouldn't that let enemies move twice as fast diagonally than they could horizontally or vertically?
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.

slime
Solid Snayke
Posts: 2853
Joined: Mon Aug 23, 2010 6:45 am
Contact:

Re: NPC chasing the player

The length of a normalized vector is always 1, so no.

(more on vectors here)

Prole
Posts: 9
Joined: Wed Feb 09, 2011 4:40 pm

Re: NPC chasing the player

Thanks alot guys, I appreciate the help.

Taehl
Dreaming in associative arrays
Posts: 1024
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: NPC chasing the player

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
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?
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.

bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: NPC chasing the player

Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: NPC chasing the player

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.