Need help with selective 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.
Sky_Render
Prole
Posts: 48
Joined: Fri Jul 05, 2019 2:59 am

Need help with selective collision detection

Post by Sky_Render »

I'm having a heck of a time trying to figure out how to code the logic in this engine for selective collision detection. Here's the basic thing I'm after:

* The player should be able to collide with enemies and items, but not their own projectiles
* The enemies should be able to collide with the player, items, and the player's projectiles
* Items should be able to collide with the player and enemies, but not player projectiles
* Player projectiles should only collide with enemies
* Player, enemies, items, and player projectiles should not collide with menu icons

I've tried to figure out how to make this work with categories and masks, but it seems like that particular system is either very buggy or just not well-documented. All of these things set to the same category will refuse to collide with each other if I set any value at all for a single mask, even if each value is distinct. However, if I set 2 mask values for them, they will all collide with each other no matter what those mask values are. There seems to be no middle ground: either everything hits everything or nothing hits anything.

Any help is much appreciated!
sphyrth
Party member
Posts: 260
Joined: Mon Jul 07, 2014 11:04 am
Contact:

Re: Need help with selective collision detection

Post by sphyrth »

As we wait for the more meaningful answers, let me put in my 2 cents:

This is the best visual representation I can produce of what you're trying to achieve. A rough draft suggests that objects shouldn't ask the entities (player, enemies, items, projectiles) what NOT to collide with, but with WHAT they should collide with.
Rough.png
Rough.png (13.25 KiB) Viewed 3785 times
Each arrow represents what entity should detect collisions with. For example, "Player" has 3 arrows going outward. This means that it's detecting collision with 3 entities, namely Items, Enemies, and Enemy Projectiles. It doesn't ask if it should NOT collide with its own projectiles because I assume that's an automatic non-collision. (In your case, maybe all the projectiles fired by the enemies and players are stored in the table... regardless of who fired it. Then maybe there should be an "owner" variable to it so that player will only detect collisions that has owner = 'enemy').

The Menu Icons seems to indicate that your game stores them as "collide-able" entities. But I don't see them fitting in your selective collision detection... no entity should ask if they are colliding with them; ignoring them altogether.

Anyway, most responders can actually help you if you share a sample bit of your code so that it can be examined.

Cheers on your first post!
Sky_Render
Prole
Posts: 48
Joined: Fri Jul 05, 2019 2:59 am

Re: Need help with selective collision detection

Post by Sky_Render »

This is more of a general "how are you supposed to use this feature" functionality question than it is a problem with specific code. And I believe I have found a bit of a kludge of a workaround: just apply multiple fixtures to the same body with different categories. That way they pass through on one "layer" but collide on another, resulting in what I'm after. I would still appreciate examples of how masks are supposed to work, though! As it stands it seems like they're kind of all-or-nothing, which makes it strange that they're even in the engine. I'm certain I'm missing something.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Need help with selective collision detection

Post by pgimeno »

Works for me:

Code: Select all


local lp = love.physics
local lg = love.graphics
local lk = love.keyboard
local world = lp.newWorld(0, 0)

if love._version_major == 0 then
  local setColour = lg.setColor
  function lg.setColor(r, g, b, a)
    if type(r) == "table" then
      setColour(r[1] * 255, r[2] * 255, r[3] * 255, (r[4] or 1) * 255)
    else
      setColour(r * 255, g * 255, b * 255, (a or 1) * 255)
    end
  end
end

-- Global speed for all objects when moving
local GlobalSpeed = 100

-- Categories
local menuCat = 1
local itemCat = 2
local playerCat = 3
local playerBulletCat = 4
local enemyCat = 5
local enemyBulletCat = 6

-- Menu item
local menuItem = lp.newBody(world, 30, 30, "static")
local menuItemSh = lp.newRectangleShape(60, 60)
local menuItemFx = lp.newFixture(menuItem, menuItemSh)
-- Set category
menuItemFx:setCategory(menuCat)
-- Menu items don't collide with anything
menuItemFx:setMask(menuCat, itemCat, playerCat, playerBulletCat, enemyCat,
  enemyBulletCat)

-- Item
local item = lp.newBody(world, 400, 300, "dynamic")
local itemSh = lp.newRectangleShape(40, 40)
local itemFx = lp.newFixture(item, itemSh)
-- Set category
itemFx:setCategory(itemCat)
-- Item can't collide with: player bullets, enemy bullets
itemFx:setMask(menuCat, playerBulletCat, enemyBulletCat)

-- Player
local player = lp.newBody(world, 300, 250, "dynamic")
local playerSh = lp.newRectangleShape(30, 30)
local playerFx = lp.newFixture(player, playerSh)
-- Set category
playerFx:setCategory(playerCat)
-- Players don't collide with player bullets
playerFx:setMask(menuCat, playerBulletCat)

-- Enemy
local enemy = lp.newBody(world, 500, 250, "dynamic")
local enemySh = lp.newRectangleShape(20, 20)
local enemyFx = lp.newFixture(enemy, enemySh)
-- Category
enemyFx:setCategory(enemyCat)
-- Doesn't collide with: enemies, enemy bullets
enemyFx:setMask(menuCat, enemyCat, enemyBulletCat)

local pBullet = lp.newBody(world, 350, 250, "dynamic")
local pBulletSh = lp.newRectangleShape(10, 10)
local pBulletFx = lp.newFixture(pBullet, pBulletSh)
-- Category
pBulletFx:setCategory(playerBulletCat)
-- Doesn't collide with: items, player, player bullets, enemy bullets
pBulletFx:setMask(menuCat, itemCat, playerCat, playerBulletCat, enemyBulletCat)

--[[
local eBullet = lp.newBody(world, 450, 250, "dynamic")
local eBulletSh = lp.newRectangleShape(10, 10)
local eBulletFx = lp.newFixture(eBullet, eBulletSh)
-- Category
eBulletFx:setCategory(enemyBulletCat)
-- Doesn't collide with: items, enemies, player bullets, enemy bullets
eBulletFx:setMask(menuCat, itemCat, enemyCat, playerBulletCat, enemyBulletCat)
--]]

local objects = {
  menuItem, item, player, enemy, pBullet--, eBullet
}
local shapes = {
  menuItemSh, itemSh, playerSh, enemySh, pBulletSh--, eBulletSh
}
local objColours = {
  {.5,0,1}, {0,0.5,1}, {0,1,0}, {1,0,0}, {0.5,0.7,0.5}--, {0.7,0.5,0.5}
}

local selected = 1

function love.update(dt)
  world:update(dt)

  -- Keyboard handling
  local xVel = 0
  local yVel = 0
  if lk.isScancodeDown("up", "w") then
    yVel = yVel - 1
  end
  if lk.isScancodeDown("left", "a") then
    xVel = xVel - 1
  end
  if lk.isScancodeDown("down", "s") then
    yVel = yVel + 1
  end
  if lk.isScancodeDown("right", "d") then
    xVel = xVel + 1
  end
  -- Quick normalization
  if xVel ~= 0 and yVel ~= 0 then
    xVel, yVel = xVel * 0.7071067811865475, yVel * 0.7071067811865475
  end
  objects[selected]:setLinearVelocity(xVel * GlobalSpeed, yVel * GlobalSpeed)
end

function love.draw()
  for i = 1, #objects do
    lg.setColor(objColours[i])
    lg.polygon("fill", objects[i]:getWorldPoints(shapes[i]:getPoints()))
    if i == selected then
      lg.setColor(1,1,1)
      lg.circle("fill", objects[i]:getX(), objects[i]:getY(), 4)
    end
  end
end

function love.keypressed(k, scan)
  if k == "escape" then
    return love.event.quit()
  end
  if k:find("^[1-6]$") then
    selected = tonumber(k)
    return
  end
end
(1-5 to change item to move; arrows or WASD or ZQSD to move; player is green, enemy is red, bullet is pastel green, item is blue, menu icon is magenta)

I don't know why your menu icons need to be part of the world. That sounds like a design flaw: extra burden to the physics engine for no good reason.
Sky_Render
Prole
Posts: 48
Joined: Fri Jul 05, 2019 2:59 am

Re: Need help with selective collision detection

Post by Sky_Render »

I looked up a Box2D tutorial on the subject, and it actually solved my problem! For those who don't know, categories define what type of collision object you're looking at, and masks define what categories your fixtures won't collide with. As the tutorial put it, category means "I am a..." and masks mean "and I won't touch...".

Thank you for trying to help me out, I do appreciate it!
User avatar
zorg
Party member
Posts: 3436
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Need help with selective collision detection

Post by zorg »

Yes, that's exactly what pgimeno's code did above your post.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
sphyrth
Party member
Posts: 260
Joined: Mon Jul 07, 2014 11:04 am
Contact:

Re: Need help with selective collision detection

Post by sphyrth »

Sky_Render wrote: Sat Jul 06, 2019 1:20 am ...category means "I am a..." and masks mean "and I won't touch...".
And this one helped ME figure out what you guys are talking about.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Need help with selective collision detection

Post by ivan »

I think the categories need to be pow2:
1,2,4,8,16,32
So you can combine them. For example a "bullet" could be both in the "projectile" and "enemy" categories.
Example:
Player 1
Enemy 2
Agent 3 (2+1)
Projectile 4
Player projectile 5 (4+1)
Enemy projectile 6 (4+2)
Item 8
Sky_Render
Prole
Posts: 48
Joined: Fri Jul 05, 2019 2:59 am

Re: Need help with selective collision detection

Post by Sky_Render »

I want to set menus as objects, but unfortunately Lua's metatable structure is about as smooth and intuitive as a spike-covered Rubik's cube. Apparently the standard object[key].data = value (ex. menu[1].x = 64 menu[3].sprite = love.graphics.newImage("button.png"), etc.) format that literally every other programming language in existence uses is not something you can easily implement in Lua, and I have yet to figure out how you do it. Ergo menus are still locked to physics objects because those at least you CAN array as objects. If someone could explain metatable object definition so I can just use the basic object[key].data = value format or something equivalent to it, I would appreciate it greatly! Every last metatable tutorial I've found just instructs on how to make metatables... act like regular tables. Why would I want to use metatables to emulate regular tables? It is a mystery.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Need help with selective collision detection

Post by ivan »

I think you need to study up on tables.
Tables can be numerically indexed lists or non-numerically indexed. What you are describing requires basic manipulation of tables (no metatables required).

Metatables are more complicated but can be super useful if you want to trigger Lua code when somebody is accessing or modifying your table.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 38 guests