• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Pixel Perfect Click Detection

T

tukib

Guest
Hey guys. I've been relentlessly searching on the internet, but I haven't found any solution. As the title says, I want a pixel perfect click detection system.
Ideally, for an object to be activated, it needs to be clicked on. This results in an inventory opening, or a machine turning on, et cetera.
What I'm looking for is an object being activated only when the mouse has been clicked, and the mouse is directly above a pixel of that object's sprite. An example would be clicking in the hole of a doughnut sprite. The object will not be activated, because the inside of the sprite is hollow - there are no pixels belonging to that sprite.
In addition to this, if there are multiple objects, only the topmost object that can be clicked on should be activated.

I've taken a sprite from a quick google image search and edited it to show where clicks cause the object to activate (green: activate, red: nothing)


If you still don't understand what I'm trying to figure out, take a look at the BYOND engine and how 'click procs' work. That engine in particular coincidently is almost exactly what I want to achieve.
 

Simon Gust

Member
Sounds like you need to activate precise collision checking
upload_2017-7-25_13-36-18.png

Then, for the code
Code:
if (mouse_check_button_pressed(mb_left))
{
 var inst = collision_point(mouse_x, mouse_y, object, true, false);
 if (inst != noone)
 {
  inst.active = true;
 }
}
It believe it will only affect the instance that is visibly on top.

Just be advised, precise collision checking will ruin your performance.
 

TheouAegis

Member
if your donut is close enough to a circle, you could always just use radio math. you could potentially do that with all shapes, just base the algorithm you need to use on the sprite assigned to that sweet. but then as for clicking on a stack, you would probably want a depth based loop method which checks each and every sweet in that position on a depth based order.
 
T

tukib

Guest
Thank you for your answer, but that only works for one specific object. I could always have one object for every single item/machine/furniture, but I'm wondering if there is a better way? The only solution I see is looping through all objects colliding with the cursor, and selecting the topmost one. Maybe use a second loop that stores the highest/lowest (forgot which way) depth so far and the instance that depth belonged to, then do whatever I want with the object.
I'll try to make my own as soon as I get the chance. In the meantime feel free to suggest anything but don't feel obliged to.
 

TheouAegis

Member
Use a priority queue. Loop through all objects; if they're at the position of the mouse, check if based on their sprite the mouse is colliding with it or not (either using precise collisions or radial math), and if it is colliding then add its ID to the priority queue prioritized by depth. Fetch the top item, then do whatever with it, then clear the priority queue when done.

However if they all have the same depth, this doesn't work as intended, in which case the priority would probably be based on their id, which then doesn't even need a priority queue because the old method of just looping through them all and keeping track of the last subsequent hit should suffice.
 
T

tukib

Guest
I've decided it will be a hassle in the long run to have each item/machine a separate object. So I'm now keeping everything tied to obj_struct. I've tried the collision_point method, but my problem now is that it only selects the instance visibly on top in the room editor. If I set the depth of one instance to -100 in its creation code, it is displayed above all others, but the pointer collisions don't work as expected.

The priority queue method sounds great too. So if I can't get collision_point to work as expected by default, queues will be my backup.
 
Last edited by a moderator:
Top