Page 1 of 1

Layered mouse collision

Posted: Wed Apr 25, 2018 5:16 pm
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 4948 times

Re: Layered mouse collision

Posted: Wed Apr 25, 2018 6:01 pm
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!

Re: Layered mouse collision

Posted: Wed Apr 25, 2018 6:23 pm
by 10c8
Thanks! By the way, how would I go about doing those calculations when the origin is the bottom-center (0.5, 1)?

Re: Layered mouse collision

Posted: Wed Apr 25, 2018 7:09 pm
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.

Re: Layered mouse collision

Posted: Wed Apr 25, 2018 7:47 pm
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 4914 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 4914 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 4914 times
It seems to depend on the angle of the card.

Re: Layered mouse collision

Posted: Wed Apr 25, 2018 7:51 pm
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!

Re: Layered mouse collision

Posted: Wed Apr 25, 2018 7:52 pm
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.

Re: Layered mouse collision

Posted: Wed Apr 25, 2018 7:55 pm
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.