Layered mouse collision

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
10c8
Prole
Posts: 6
Joined: Wed Apr 25, 2018 4:53 pm
Contact:

Layered mouse collision

Post by 10c8 »

Hey, I'm currently working on a card game. On the screen, each player's hand is drawn as a fan of cards (image below), where cards may slightly overlap each other, this means that by simply checking if the mouse is within each card's rectangle sometimes returns more than one card. I figured I could check which one of them has the bigger index and make this one the "hovered" card, but I thought there might be a better way of doing this?

The cards are all kept on a table, each one has `x` and `y` coordinates and a `rotation`. They're drawn in order (from 1 to ...).
img.png
img.png (125.07 KiB) Viewed 5239 times
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Layered mouse collision

Post by ivan »

I figured I could check which one of them has the bigger index and make this one the "hovered" card
You are correct, this is by far the simplest technique.
Suppose you have a numerically indexed table of cards - you are drawing them in order so cards with a higher index are drawn on top.
Next, you want something to "query" those cards:

Code: Select all

cards = {}

function query(cards, x, y)
  -- NOTE: iterate in reverse so that the top card is selected first
  for i = #cards, 1, -1 do
    if testCard(cards[i], x, y) then
       return cards[i], i
    end
  end
end

function testCard(card, x, y)
  -- WARNING: assumes the top left corner of the card is the origin (0, 0)
  -- convert to local coords
  local lx, ly = x - card.x, y - card.y
  -- rotate so that we are axis aligned
  local c = math.cos(card.angle)
  local s = math.sin(card.angle)
  local rx = c*lx - s*ly
  local ry = s*lx + c*ly
  -- axis aligned test
  return rx >= 0 and rx <= card.width and ry >= 0 and ry <= card.height
end
That's all you need, really. Note that my example assumes that 0, 0 is the top-left corner of the card.
It's easier if you rotate cards around their center but then your "axis aligned test" needs to be modified slightly.
Good luck!
User avatar
10c8
Prole
Posts: 6
Joined: Wed Apr 25, 2018 4:53 pm
Contact:

Re: Layered mouse collision

Post by 10c8 »

Thanks! By the way, how would I go about doing those calculations when the origin is the bottom-center (0.5, 1)?
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Layered mouse collision

Post by ivan »

Believe me, the math is much simpler and more scalable if use the "center" option and it makes drawing easier too.
You only need to change the last line:
Top left corner:

Code: Select all

return rx >= 0 and rx <= w and ry >= 0 and ry <= h
Center:

Code: Select all

return rx >= -w/2 and rx <= w/2 and ry >= h/2 and ry <= h/2
Bottom-center:

Code: Select all

return rx >= -w/2 and rx <= w/2 and ry >= -h and ry <= 0
Assuming that:
0,0 is the to-left corner of the window
card.x, card.y = origin point of the card (window coordinates)
card.angle = angle of the card in radians (objects rotate counter-clockwise as this value increases)
edit: the rotation might be reversed if your y-axis increases down (or South) which is customary when drawing with Love2d
Personally I take it one step further and program my games with the Y axis increasing up (North) and I assume that 0,0 is the center of the window - it makes the trigonometry calculations even simpler.
Last edited by ivan on Wed Apr 25, 2018 8:03 pm, edited 4 times in total.
User avatar
10c8
Prole
Posts: 6
Joined: Wed Apr 25, 2018 4:53 pm
Contact:

Re: Layered mouse collision

Post by 10c8 »

I tried your code and this happens:
Captura de tela de 2018-04-25 16-42-35.png
Captura de tela de 2018-04-25 16-42-35.png (91.79 KiB) Viewed 5205 times
It only seems to detect a collision correctly when there's a single card (with almost no rotation):
Captura de tela de 2018-04-25 16-44-09.png
Captura de tela de 2018-04-25 16-44-09.png (22.75 KiB) Viewed 5205 times
Captura de tela de 2018-04-25 16-44-26.png
Captura de tela de 2018-04-25 16-44-26.png (15.57 KiB) Viewed 5205 times
It seems to depend on the angle of the card.
User avatar
10c8
Prole
Posts: 6
Joined: Wed Apr 25, 2018 4:53 pm
Contact:

Re: Layered mouse collision

Post by 10c8 »

10c8 wrote: Wed Apr 25, 2018 7:47 pm I tried your code and this happens:


It only seems to detect a collision correctly when there's a single card (with almost no rotation):


It seems to depend on the angle of the card.
Nevermind, your code assumes that the card rotates counter-clockwise with the angle, but they rotate clockwise. So I just changed the instances of rotation to -rotation!
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Layered mouse collision

Post by ivan »

I'm pretty sure that the technique I posted works...
It might not work using copy/paste - additional work may be required, depending on how you draw the cards.
You need to include a .love file if you expect me to help you any further.
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Layered mouse collision

Post by ivan »

10c8 wrote: Wed Apr 25, 2018 7:51 pm your code assumes that the card rotates counter-clockwise with the angle
That's a standard assumption in trigonometry (math.cos/math.sin) it's just a question of how you decide to draw the cards.
Again, it's very important to distinguish between your logic/math code and your rendering/drawing code.

PS. Actually, if your Y-axis increases down like in love2d, that might flip the angle.
Post Reply

Who is online

Users browsing this forum: No registered users and 5 guests