Page 1 of 1

Messed up World:rayCast()?

Posted: Mon Nov 19, 2018 8:06 pm
by Whatthefuck
Hello, like the title says, it seems like the rayCast method is messed up in the sense that it just straight up seems to skip objects to check for collision against. Here are a couple of pictures to show you what my problem is.
1. there is a :rayCast from the 1st body to the 2nd, there is nothing in-between and everything is OK.
Image

2. placed a wall that serves as an obstacle, the wall seems to be occluding the :rayCast just fine. Everything is OK.
Image

3. now I move a couple of pixels to the right away from the wall, and bam, it's ignoring the obstacle. What the hell is going on?
Image

Additionally, here's a video which shows even more weird stuff: https://www.dropbox.com/s/xefdxde4zrw0c ... 6.mp4?dl=0
Here's another video which showcases more errata when it comes to obstacles and multiple objects: https://www.dropbox.com/s/vw3zmouhexjp5 ... 7.mp4?dl=0

It's as if the query for objects to check the collision against is not working properly.

I've checked the position of the wall obstacle, and it doesn't change before/after I move (it's not meant to, anyway), the wall obstacles are static objects, and collision detection works just fine, but raycasting messes up and just ignores that object completely. Has anyone else had a similar problem? This is incredibly infuriating because I am not doing anything wrong as far as I'm concerned, especially considering that the start/finish positions check out just fine.

This happens using both 0.10.2 and 11.1, so I am at a complete loss right now.

I've googled this issue, but found no one else having the same problem in the LOVE2D community, yet at the same time someone else was having a similar issue with Box2D outside of LOVE2D so maybe this has something to do with Box2D itself?

I've tried calling :setSleepingAllowed(false) on the world object to prevent objects from sleeping (I was thinking maybe the walls were doing that and it was ignoring them?). That didn't help. I tried manually disabling sleeping on wall obstacles, that didn't help either. I'm clueless as to what the hell is going on at the moment.

Any help would be appreciated.

Re: Messed up World:rayCast()?

Posted: Mon Nov 19, 2018 10:54 pm
by pgimeno
Works for me.

Code: Select all

local world = love.physics.newWorld(0, 0, false)

local ray1x, ray1y = 400, 300
local ray2x, ray2y = 700, 500

local intersectx, intersecty
local range

local obstacle = false
local obsBody = love.physics.newBody(world, 900, 0, "static")
local obsShape = love.physics.newRectangleShape(64, 64)
local obsFixture = love.physics.newFixture(obsBody, obsShape)
local obsBody2 = love.physics.newBody(world, 348, 300, "static")
local obsFix2 = love.physics.newFixture(obsBody2, obsShape)

local obsx, obsy = 620, 415

local function fixtures(fixture, x, y, xn, yn, fraction)
  if fraction < range then
    intersectx, intersecty = x, y
    range = fraction
  end
  return fraction
end

function love.update(dt)
  if obstacle then
    obsBody:setPosition(obsx, obsy)
  else
    obsBody:setPosition(1000, 0)
  end

  world:update(dt)

  if love.mouse.isDown(2) then
    ray1x, ray1y = love.mouse.getPosition()
  elseif love.mouse.isDown(1) then
    obsx, obsy = love.mouse.getPosition()
  else
    ray2x, ray2y = love.mouse.getPosition()
  end

  intersectx, intersecty = ray2x, ray2y
  range = 1

  if math.abs(ray1x - ray2x) + math.abs(ray1y - ray2y) > 1 then
    world:rayCast(ray1x, ray1y, ray2x, ray2y, fixtures)
  end
end

function love.keypressed(k)
  if k == "escape" then return love.event.quit() end
  if k == "space" then
    obstacle = not obstacle
  end
end

function love.draw()
  love.graphics.setColor(0,1,1)
  love.graphics.polygon("line", obsBody:getWorldPoints(obsShape:getPoints()))
  love.graphics.polygon("line", obsBody2:getWorldPoints(obsShape:getPoints()))
  love.graphics.setColor(1,0,0)
  love.graphics.line(ray1x, ray1y, intersectx, intersecty)

  love.graphics.setColor(1,1,1)
  love.graphics.print("space to toggle obstacle, lbutton to move obstacle, rbutton to change start of ray")
end
I added a second object that isn't toggled or moveable, just in case, but that didn't change anything.

Can you provide some code to reproduce the issue?

Re: Messed up World:rayCast()?

Posted: Tue Nov 20, 2018 10:06 am
by Whatthefuck
pgimeno wrote: Mon Nov 19, 2018 10:54 pm Works for me.

Code: Select all

local world = love.physics.newWorld(0, 0, false)

local ray1x, ray1y = 400, 300
local ray2x, ray2y = 700, 500

local intersectx, intersecty
local range

local obstacle = false
local obsBody = love.physics.newBody(world, 900, 0, "static")
local obsShape = love.physics.newRectangleShape(64, 64)
local obsFixture = love.physics.newFixture(obsBody, obsShape)
local obsBody2 = love.physics.newBody(world, 348, 300, "static")
local obsFix2 = love.physics.newFixture(obsBody2, obsShape)

local obsx, obsy = 620, 415

local function fixtures(fixture, x, y, xn, yn, fraction)
  if fraction < range then
    intersectx, intersecty = x, y
    range = fraction
  end
  return fraction
end

function love.update(dt)
  if obstacle then
    obsBody:setPosition(obsx, obsy)
  else
    obsBody:setPosition(1000, 0)
  end

  world:update(dt)

  if love.mouse.isDown(2) then
    ray1x, ray1y = love.mouse.getPosition()
  elseif love.mouse.isDown(1) then
    obsx, obsy = love.mouse.getPosition()
  else
    ray2x, ray2y = love.mouse.getPosition()
  end

  intersectx, intersecty = ray2x, ray2y
  range = 1

  if math.abs(ray1x - ray2x) + math.abs(ray1y - ray2y) > 1 then
    world:rayCast(ray1x, ray1y, ray2x, ray2y, fixtures)
  end
end

function love.keypressed(k)
  if k == "escape" then return love.event.quit() end
  if k == "space" then
    obstacle = not obstacle
  end
end

function love.draw()
  love.graphics.setColor(0,1,1)
  love.graphics.polygon("line", obsBody:getWorldPoints(obsShape:getPoints()))
  love.graphics.polygon("line", obsBody2:getWorldPoints(obsShape:getPoints()))
  love.graphics.setColor(1,0,0)
  love.graphics.line(ray1x, ray1y, intersectx, intersecty)

  love.graphics.setColor(1,1,1)
  love.graphics.print("space to toggle obstacle, lbutton to move obstacle, rbutton to change start of ray")
end
I added a second object that isn't toggled or moveable, just in case, but that didn't change anything.

Can you provide some code to reproduce the issue?
I noticed you return fraction in the callback, is that the default use case for the :rayCast method? This wasn't mentioned anywhere. I thought the :rayCast method would automatically test against closest-to-farthest objects, and would return the closest point of impact into the callback method first, not test against all objects between the ray start/finish position and return each point of intersection in an arbitrary order. I think this is why I was having my problems.

Edit: yep, this is why I was having my problems. God fucking damn it. Someone needs to document that it returns points of intersection in an arbitrary order, because some other poor soul might end up spending 5 hours trying to figure out what the hell they did wrong with their world/body/shape/fixture setup and then it turns out the :rayCast method behavior is not what they expected...

Re: Messed up World:rayCast()?

Posted: Tue Nov 20, 2018 12:52 pm
by pgimeno
Whatthefuck wrote: Tue Nov 20, 2018 10:06 am I noticed you return fraction in the callback, is that the default use case for the :rayCast method? This wasn't mentioned anywhere.
Returning 1 worked as well. I started returning 1, but changed it to 'fraction' when I saw the forum post you linked, to try to copy their repro and see if that changed something, but it didn't. However I assumed that the order was going to be arbitrary, so I added the code to check the minimum since the beginning. It didn't make sense in my mind that Box2D first creates a list of fixtures that intersect your line, then sorts it and then calls your callback in sorted order: you can do that yourself if that's what you need, and it would waste time and memory if you don't need it at all (e.g. in some use cases you want to detect whether there's a collision at all, you don't need the closest one).

Now that I notice, it's likely that in that other post, their problem is the same: not picking the nearest match.

Glad it helped!

Re: Messed up World:rayCast()?

Posted: Tue Nov 20, 2018 6:08 pm
by Whatthefuck
pgimeno wrote: Tue Nov 20, 2018 12:52 pm
Whatthefuck wrote: Tue Nov 20, 2018 10:06 am I noticed you return fraction in the callback, is that the default use case for the :rayCast method? This wasn't mentioned anywhere.
Returning 1 worked as well. I started returning 1, but changed it to 'fraction' when I saw the forum post you linked, to try to copy their repro and see if that changed something, but it didn't. However I assumed that the order was going to be arbitrary, so I added the code to check the minimum since the beginning. It didn't make sense in my mind that Box2D first creates a list of fixtures that intersect your line, then sorts it and then calls your callback in sorted order: you can do that yourself if that's what you need, and it would waste time and memory if you don't need it at all (e.g. in some use cases you want to detect whether there's a collision at all, you don't need the closest one).

Now that I notice, it's likely that in that other post, their problem is the same: not picking the nearest match.

Glad it helped!
Yeah, it makes sense why it wouldn't sort them internally, but for some reason I still expected it to work that way. :D
Someone should really document that it returns intersections in an arbitrary order, so that someone else doesn't end up spending several hours trying to figure out what the hell is going on, haha.

Re: Messed up World:rayCast()?

Posted: Tue Nov 20, 2018 7:30 pm
by pgimeno
well, if you have a forum account, you also have a wiki account :) The same user and password works in the wiki.