Collision detection

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

Collision detection

Post by nevon »

This always gives me a headache...

I have a table of every enemy on the screen, enemies.onscreen. I have another table for every projectile launched by the user (projectiles.playershots). For each enemy on the screen I want to check if it is currently colliding with one of the projectiles. Here's how I've done it:

Code: Select all

 --this is in love.update()
for i,v in ipairs(enemies.onscreen) do
    local enemywidth = enemies.types[v.type].width/2 --Get the height and width of the enemy sprite, so that we can add an offset
    local enemyheight = enemies.types[v.type].height/2
    for n,c in ipairs(projectiles.playershots) do
        --The sprite origin is centered on the sprite, so we need to add offsets for all sides.
        if collision({v.x-enemywidth, v.x+enemywidth, v.x+enemywidth, v.x-enemywidth}, {v.y-enemyheight, v.y-enemyheight, v.y+enemyheight, v.y+enemyheight}, c.position.x, c.position.y, player.images.shot.width/2) then
            table.remove(v, i)
            table.remove(c, n)
        end
    end
end

--this function is not in love.update(), naturally
function collision(x, y, pointx, pointy, radius) --takes two tables, one for the c-coordinates of the enemy's four corners, and one for the y-coordinates. The rest is the projectile's coordinates and radius.
    local pow = math.pow
    local root = math.sqrt
    for i,v in ipairs(x) do
        if root( pow( (pointy-y[i]),2) + pow( (pointx-v),2 ) ) < radius then --Check if the distance between the projectile and the nearest corner is less than the radius of the projectile
            return true
        end
    end
end
To me, it seems horribly inefficient, but I don't know any other way to do it. Sadly, it doesn't actually work, and after a little while it segfaults - leaving no error message. Is there a better, possibly more efficient way to do this?

In case it might be helpful, here's the data structure of the projectiles and the enemies.

Code: Select all

--Projectiles
projectiles = {
    playershots = {}
}
--Every time a shot is fired, it is created like this
table.insert(projectiles.playershots, {
    start={ -- The starting point of the projectile
        x=player.x,
	y=player.y
    }, 
    target = { --Where it's headed
        x=x,
	y=y
    },
    position={ -- Current position
        x=player.x,
	y=player.y
    },
    direction = math.atan2((y-player.y),(x-player.x)), --The direction in which it is travelling
    v = 5 --speed
})

Code: Select all

--Enemies
enemies = {
    types = {
        {
            name = "ufo",
            sprite = love.graphics.newImage("images/enemies/ufo.png"),
            width, height,
            health = 10,
            v = 100
        }
    },
    onscreen = {}
}

--Whenever an enemy is created, this is run
function createRandEnemy()
    local randint = math.random
    local scaling = randint(75,125)/100
    return {
        type = randint(1 , table.getn(enemies.types) ),
        x = randint(50, screenWidth-50),
        y = -100,
        rotation = 0.0, --not used currently
        scale = {
            x = scaling,
            y = scaling,
        },
        speed = 50,
    }
end
Here's a screenshot to give you a better idea of what I'm trying to do.

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

Re: Collision detection

Post by Robin »

1) You try to remove keys from tables. That doesn't work.

2) ipairs() doesn't really work well with table.remove(). I suggest you change this:

Code: Select all

            table.remove(v, i) -- v should be enemies.onscreen
            table.remove(c, n) -- c should be projectiles.playershots
into this:

Code: Select all

            v.killed = true
            c.killed = true
And at the end do:

Code: Select all

for i=#enemies.onscreen,1,-1 do --do this again with projectiles.playershots
    if enemies.onscreen[i].killed then
        table.remove(enemies.onscreen, i)
    end
end
2) Creating two tables for every collision is horribly inefficient. It doesn't make sense at all. Rewrite it without the tables, and just give 7 arguments: enemyx, enemyy, enemywidth, enemyheight, pointx, pointy, radius.
Help us help you: attach a .love.
User avatar
nevon
Commander of the Circuloids
Posts: 938
Joined: Thu Feb 14, 2008 8:25 pm
Location: Stockholm, Sweden
Contact:

Re: Collision detection

Post by nevon »

Thank you. I don't know what I was thinking (my brain was probably on strike), but I completely forgot to check if the projectile was inside the enemy bounding box. My new collision detection method looks like this:

Code: Select all

function collision(enemyx, enemyy, enemywidth, enemyheight, pointx, pointy, radius)
	local pow = math.pow
	local root = math.sqrt
	if pointx > enemyx-radius and pointy > enemyy-radius and pointx < enemyx+enemywidth+radius and pointy < enemyy+enemyheight+radius then
		return true
	elseif root( pow( enemyx-pointx, 2 ) + pow( enemyy-pointy, 2 ) ) < radius or
	   root( pow( enemyx+enemywidth-pointx, 2 ) + pow( enemyy-pointy, 2 ) ) < radius or
	   root( pow( enemyx-pointx, 2 ) + pow( enemyy+enemyheight-pointy, 2 ) ) < radius or
	   root( pow( enemyx+enemywidth-pointx, 2 ) + pow( enemyy+enemyheight-pointy, 2 ) ) < radius then
		return true
	end
end
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Collision detection

Post by Robin »

So... does it work now?
Help us help you: attach a .love.
User avatar
nevon
Commander of the Circuloids
Posts: 938
Joined: Thu Feb 14, 2008 8:25 pm
Location: Stockholm, Sweden
Contact:

Re: Collision detection

Post by nevon »

Robin wrote:So... does it work now?
It sure does. And it's fairly snappy too! :megagrin:
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 93 guests