Tile based collision, player catches on corners of tiles despite lack of downward velocity
Posted: Sun Jan 16, 2022 10:47 pm
I'm trying to make a tile based game, and I've been trying to make this collision system. What it does is it takes the player's velocity, checks for collisions between where it is and where it will be, and adjusts the velocity to prevent collision. It's almost working perfectly, but there's a bug that I cannot find the cause of. When the player slides on a row of tiles, they get caught at every corner. They have absolutely no downward velocity, so the issue is not that the tiles are in the wrong order. There's gotta be something wrong with the maths. Another weird thing is that this happens almost every time, yet there will be an extremely rare occasion where this never happens. I have no idea why this might be. Here is the collision code:
Any and all help is greatly appreciated.
Code: Select all
function Entity:collide(e)
local r = {}
r.ret = false
if self.vx == 0 and self.vy == 0 then return r end
local expandedTarget = Entity(e.x - self.width / 2, e.y - self.height / 2)
expandedTarget.width = self.width + e.width
expandedTarget.height = self.height + e.height
r = self:rayVsRect(expandedTarget)
if r.ret == false then return r end
if r.time < 1 and r.time >= 0 then r.ret = true else r.ret = false end
return r
end
function Entity:rayVsRect(e)
local r = {}
r.ret = false
local ray = {}
ray.ox = self.x + self.width / 2
ray.oy = self.y + self.height / 2
ray.mx = self.vx
ray.my = self.vy
local tNear = {}
tNear.x = (e.x - ray.ox) / ray.mx
tNear.y = (e.y - ray.oy) / ray.my
local tFar = {}
tFar.x = (e.x + e.width - ray.ox) / ray.mx
tFar.y = (e.y + e.height - ray.oy) / ray.my
if tNear.x > tFar.x then tNear.x, tFar.x = tFar.x, tNear.x end
if tNear.y > tFar.y then tNear.y, tFar.y = tFar.y, tNear.y end
if tNear.x > tFar.y or tNear.y > tFar.x then return r end
local tHitNear = math.max(tNear.x, tNear.y)
local tHitFar = math.min(tFar.x, tFar.y)
if tHitFar < 0 then return r end
r.cpX = ray.ox + tHitNear * ray.mx
r.cpY = ray.oy + tHitNear * ray.my
if tNear.x > tNear.y then
if ray.mx < 0 then
r.cnX = 1
r.cnY = 0
else
r.cnX = -1
r.cnY = 0
end
elseif tNear.y > tNear.x then
if ray.my < 0 then
r.cnX = 0
r.cnY = 1
else
r.cnX = 0
r.cnY = -1
end
else
if ray.mx < 0 then
r.cnX = 1
else
r.cnX = -1
end
if ray.my < 0 then
r.cnY = 1
else
r.cnY = -1
end
end
r.time = tHitNear
r.ret = true
return r
end