NPC chasing the player

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Head Over Heels
Prole
Posts: 9
Joined: Wed Feb 09, 2011 4:40 pm

NPC chasing the player

Post 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.
User avatar
nevon
Commander of the Circuloids
Posts: 938
Joined: Thu Feb 14, 2008 8:25 pm
Location: Stockholm, Sweden
Contact:

Re: NPC chasing the player

Post 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.
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: NPC chasing the player

Post 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
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+.
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: NPC chasing the player

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

hump | HC | SUIT | moonshine
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: NPC chasing the player

Post by Taehl »

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+.
User avatar
slime
Solid Snayke
Posts: 3166
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: NPC chasing the player

Post by slime »

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

(more on vectors here)
Head Over Heels
Prole
Posts: 9
Joined: Wed Feb 09, 2011 4:40 pm

Re: NPC chasing the player

Post by Head Over Heels »

Thanks alot guys, I appreciate the help.
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: NPC chasing the player

Post 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?
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+.
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: NPC chasing the player

Post by bartbes »

A table + unpack? This loadstring stuff is unreadable.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: NPC chasing the player

Post 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.
Help us help you: attach a .love.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot] and 3 guests