Specific Collisions

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
Ortimh
Citizen
Posts: 90
Joined: Tue Sep 09, 2014 5:07 am
Location: Indonesia

Specific Collisions

Post by Ortimh »

Thank you for visiting my thread.

I need a solution for my current problem with love.physics. Let's imagine that I have 3 bodies attached with their own shapes with fixtures, they're called A, B, and C. I need to make body A not collide with body B (go through) but collides with body C. After quite some time (maybe 5 seconds), body A can collide with body B and C.

Anyone has the solution?

Note: I'm on phone right now so asking for *.love file is impossible.
User avatar
pgimeno
Party member
Posts: 3549
Joined: Sun Oct 18, 2015 2:58 pm

Re: Specific Collisions

Post by pgimeno »

This worked for me:

Code: Select all

lp = love.physics
lg = love.graphics
lm = love.mouse

local LMB = love._version_major*10+love._version_minor < 10 and "l" or 1

local world = lp.newWorld(0, 300, false)

local objects = {
  A = {
    body = lp.newBody(world, 200, 300, "dynamic");
    shape = lp.newCircleShape(50);
    color = { 255, 128, 0 };
  };

  B = {
    body = lp.newBody(world, 400, 300, "dynamic");
    shape = lp.newCircleShape(50);
    color = { 128, 255, 0 };
  };

  C = {
    body = lp.newBody(world, 600, 300, "dynamic");
    shape = lp.newCircleShape(50);
    color = { 255, 255, 0 };
  };
}

objects.A.fixture = lp.newFixture(objects.A.body, objects.A.shape)
objects.B.fixture = lp.newFixture(objects.B.body, objects.B.shape)
objects.C.fixture = lp.newFixture(objects.C.body, objects.C.shape)

-- Enclosing box is in categories 0-14 and collides with categories 0-14
do
  local body = lp.newBody(world, 400, 300)
  lp.newFixture(body, lp.newEdgeShape(-396, -296,  396, -296)):setFilterData(32767, 32767, 0)
  lp.newFixture(body, lp.newEdgeShape(-396,  296,  396,  296)):setFilterData(32767, 32767, 0)
  lp.newFixture(body, lp.newEdgeShape(-396, -296, -396,  296)):setFilterData(32767, 32767, 0)
  lp.newFixture(body, lp.newEdgeShape( 396, -296,  396,  296)):setFilterData(32767, 32767, 0)
end

-- Object A is in category 1 and collides with category 0
objects.A.fixture:setFilterData(2, 1, 0)
-- Object B is in category 1 and collides with category 1
objects.B.fixture:setFilterData(2, 2, 0)
-- Object C is in categories 0 and 1 and collides with categories 0 and 1
objects.C.fixture:setFilterData(3, 3, 0)


local font = lg.newFont(40)
lg.setFont(font)
local fontH = font:getHeight()


function love.update(dt)
  if lm.isDown(LMB) then
    local mx, my = lm.getPosition()
    local closest, closestd2
    for k, v in next, objects do
      local x, y = v.body:getPosition()
      local d2 = (x-mx)^2 + (y-my)^2
      if not closest or d2 < closestd2 then
        closest, closestd2 = k, d2
      end
    end
    objects[closest].body:setPosition(mx, my)
    objects[closest].body:setLinearVelocity(0, 0)
    objects[closest].body:setAngularVelocity(0)
  end

  world:update(dt)
end


function love.draw()
  for k, v in next,objects do
    lg.setColor(v.color)
    local x, y = v.body:getPosition()
    local fontW = font:getWidth(k)
    lg.circle("line", x, y, v.shape:getRadius())
    lg.print(k, x, y, v.body:getAngle(), 1, 1, fontW/2, fontH/2)
  end

  lg.setColor(255, 255, 255)
  lg.rectangle("line", 3.5, 3.5, 793, 593)

  if collideAB then
    lg.setColor(0, 255, 0)
    lg.print("A-B collision enabled", 4, 4)
  else
    lg.setColor(255, 0, 0)
    lg.print("A-B collision disabled", 4, 4)
  end
end


function love.keypressed(k)
  if k == "space" or k == " " or k == "return" then
    collideAB = not collideAB
    -- Toggle object A colliding with category 1 (true) or 0 (false)
    objects.A.fixture:setFilterData(2, collideAB and 2 or 1, 0)
  end
  if k == "escape" then love.event.quit() end
end
Tested with love2d 0.8.0, 0.9.1, 0.9.2 and 0.10.1.

I tried to use [wiki]World:setContactFilter[/wiki] but it didn't trigger. Does anyone know why? Edit: got it working.
Last edited by pgimeno on Fri May 13, 2016 6:27 pm, edited 1 time in total.
User avatar
Ortimh
Citizen
Posts: 90
Joined: Tue Sep 09, 2014 5:07 am
Location: Indonesia

Re: Specific Collisions

Post by Ortimh »

pgimeno, thank you. But can you explain how this Fixture:setFilterData works? It's hard for me to grasp it.
User avatar
pgimeno
Party member
Posts: 3549
Joined: Sun Oct 18, 2015 2:58 pm

Re: Specific Collisions

Post by pgimeno »

You need the first two parameters, I'm still not sure what the last one is for.

The first one is the categories of the fixture, one bit per category, up to 16.

The second one is the mask (the categories of other fixtures it will collide with).

The number 1 has bit 0 set and the rest clear. If used as a category number, the fixture will be in category 0 and not in categories 1-15. If used as a mask number, it will only collide with those fixtures that have category 0 set.

The number 2 has bit 1 set and the rest clear. If used as a category number, it will be in category 1 and not in categories 0 nor 2-15. If used as a mask number, it will only collide with those fixtures that have category 1 set.

The number 3 has bits 0 and 1 set. If used as a category number, it will be in categories 0 and 1 and not in categories 2-15. If used as a mask number, it will only collide with those fixtures that have either category 0 or category 1 set.

And so on. I've set up the borders to be in (and collide with) categories 0-14 and not in/with category 15 (in case you want a category guaranteed not to collide with anything, not even borders), then objects A and B in category 1 and object C in categories 0 and 1.

A only collides with category 0 if collision between A and B is not desired, and with category 1 if it is.

B collides only with category 1.

C collides with categories 0 and 1.

I could have also made A switch to category 0 while collision between A and B is not desired, so that the categories were more isolated, but I found that to be unnecessary in this particular instance, saving a bit of extra code.

A bit of a mess, but it works :)
User avatar
Ortimh
Citizen
Posts: 90
Joined: Tue Sep 09, 2014 5:07 am
Location: Indonesia

Re: Specific Collisions

Post by Ortimh »

How can I make it work with more than 3 bodies in the world?
User avatar
pgimeno
Party member
Posts: 3549
Joined: Sun Oct 18, 2015 2:58 pm

Re: Specific Collisions

Post by pgimeno »

It depends on what you want them to collide with. If all the other objects can collide with each other, including A and B, just do the same as in object C: add them to categories 0 and 1 and let them collide with categories 0 and 1.
User avatar
Ortimh
Citizen
Posts: 90
Joined: Tue Sep 09, 2014 5:07 am
Location: Indonesia

Re: Specific Collisions

Post by Ortimh »

OK, I'm trying to do it with your way. But I think I found a way but not so sure though. The way is that I use

Code: Select all

world:setCallbacks(function(fixture1, fixture2, contact)
    contact:setEnabled(false)
end)
Thank you either way!
User avatar
pgimeno
Party member
Posts: 3549
Joined: Sun Oct 18, 2015 2:58 pm

Re: Specific Collisions

Post by pgimeno »

I must have made something wrong before, but I tested this again and it worked this time:

Code: Select all

world:setContactFilter(function (f1, f2)
  if f1 == objects.A.fixture and f2 == objects.B.fixture
      or f1 == objects.B.fixture and f2 == objects.A.fixture then
    return collideAB
  end
  return true
end)
(and removing all setFilterData calls everywhere). That callback is intended for exactly this purpose, so you may find it easier.

The setFilterData method is instantaneous: it immediately pushes A and B away when activating collisions between them and they're intersecting. This one isn't always instantaneous.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Amazon [Bot] and 81 guests