[Solved] Rectangle collision resolution only works on one axis
Posted: Thu May 09, 2024 1:51 am
Hello, I am creating the bones of a 2D top-down game, and am trying to make my 'basic' code something I can reuse for other projects. I originally based my code off the Sheepolution tutorial here https://sheepolution.com/learn/book/23. However, I encountered some strange behavior where colliding from the vertical directions (up or down) causes it to act like its colliding from the horizontal directions. Left and right collisions still work perfectly fine. When I switched whether it was vertically and horizontally collided, it would instead properly collide up and down, and treat left and right like a down collision. I ended up copying the code I used when doing the tutorial, and when that didn't work, from the tutorial itself. I thought that fixed it, and I moved on. However, while I was working on having actors collide, while the collision process itself works, the previous bug has come back, and nothing I do seems to fix it. It seems to be the only issue I'm having with my collision code otherwise.
I'm hoping that someone has a solution to this problem. I'm trying to find a way to do this without using libraries, more so I can understand how it works, and because this game is supposed to be extremely simple physics-wise.
Collisions:
Relevant Actor snippet
I also provided the .love file so you can see the behavior.
EDIT: Forgot to add the file. [EDIT2] Was solved by pgimeno! Thank you for your help!
I'm hoping that someone has a solution to this problem. I'm trying to find a way to do this without using libraries, more so I can understand how it works, and because this game is supposed to be extremely simple physics-wise.
Collisions:
Code: Select all
function Collision:new(x,y,width,height,strength)
Collision.super.new(self,x,y,width,height)
--Collsions
if strength == nil then strength = 0 end
self.strength = strength
self.tempStr = strength
self.last = {}
self.last.x = x
self.last.y = y
end
function Collision:setStrength(val)
if val < 0 then val = 0 end
self.strength = val
end
function Collision:update(dt)
self.last.x = self.x
self.last.y = self.y
self.tempStr = self.strength
end
function Collision:check(col)
if col:is(Collision) then return self:overlap(col) end
return false
end
function Collision:collide(e,dir)
if dir == "left" then
local pushback = self.x + self.width - e.x
self.x = self.x - pushback
elseif dir == "right" then
local pushback = e.x + e.width - self.x
self.x = self.x + pushback
elseif dir == "up" then
local pushback = e.y + e.height - self.y
self.y = self.y + pushback
elseif dir == "down" then
local pushback = self.y + self.height - e.y
self.y = self.y - pushback
end
end
function Collision:wasVerticalAligned(e)
return self.last.y < e.last.y + e.height and self.last.y + self.height > e.last.y
end
function Collision:wasHorizontalAligned(e)
return self.last.x < e.last.x + e.width and self.last.x + self.width > e.last.x
end
--Overwritten by other functions
function Collision:checkResolve(e,dir)
return true
end
function Collision:resolve(e)
--Check if stronger; if true, reverse
if self.tempStr > e.tempStr then
return e:resolve(self)
end
--Check if colliding
if self:check(e) then
self.tempStr = e.tempStr
if self:wasVerticalAligned(e) then
if self.x + (self.width/2) < e.x + (e.width/2) then
self:collide(e,"left")
else
self:collide(e,"right")
end
debugStrings[2] = "Horizontal Collision"
elseif self:wasHorizontalAligned(e) then
if self.y + (self.height/2) < e.y + (e.height/2) then
self:collide(e,"down")
else
self:collide(e,"up")
end
debugStrings[2] = "Vertical Collision"
end
return true
end
return false
end
Code: Select all
function Actor:canCollide(b)
if b == nil then b = false end
self.canCollide = b
end
function Actor:collide(a)
if a == nil then return false end
if a:is(Actor) then
if a:hasCollision() then
--Resolve
local t = self.col:resolve(a.col)
--Set position
if t then
self.x = self.col.x
self.y = self.col.y
a.x = a.col.x
a.y = a.col.y
end
return t
end
end
return false
end
function BasicData.collisions(dt,loops)
if loops == nil then loops = 100 end
--Sort in priority order
table.sort(BasicData.actors,function(a1,a2)
if a1.priority ~= a2.priority then
return a1.priority > a2.priority
end
return a1.id < a2.id
end)
--Check collide
local can_loop = true
local limit = 0
while can_loop do
--Breaking the loop
can_loop = false
limit = limit + 1
if limit >= loops then break end
--Collisions
for i=1,#BasicData.actors-1 do
for j=i+1,#BasicData.actors do
local col = BasicData.actors[i]:collide(BasicData.actors[j])
if col then can_loop = true end
end
end
end
end
EDIT: Forgot to add the file. [EDIT2] Was solved by pgimeno! Thank you for your help!