Pixel-Perfect Sprite Clicks
Tags: miniscript, minimicro, programming, beginners
Published: 2023-10-13 15:53:55

A common need in any graphical program is to detect clicks on a button or other sprite.  The Mini Micro wiki contains an article on "How to detect a click on a sprite," but (at least at the time of this writing), it assumes a simple rectangular click area.  What if your sprite is an odd shape, and you really need to detect clicks only on the visible part of the sprite?

In this case, we need _pixel-perfect click detection_.  The basic idea is: first do your simple rectangular-bounds check; and then if that passes, check the pixel color of the sprite image at the location of the mouse.  Consider it a valid click only if the pixel there is not transparent.  (Or, if your sprite has some solid background color, then check for that color instead.)

Let's start by reviewing the simple rectangular check first.  Launch Mini Micro, `edit` a fresh program, and paste in this short demo code:

```
clear
sp = new Sprite
sp.image = file.loadImage("/sys/pics/Wumpus.png")
sp.localBounds = new Bounds
sp.localBounds.width = sp.image.width
sp.localBounds.height = sp.image.height
sp.x = 480; sp.y = 320; sp.scale = 2
display(4).sprites.push sp

while true
	if sp.contains(mouse) then
		sp.tint = color.green
	else
		sp.tint = color.white
	end if
end while
```

This sets up a sprite, loading our beloved Wumpus from the system pictures.  It assigns a `localBounds` based on the image size, so that we can use the `contains` method to check whether the mouse is over those bounds.  (This accounts for the position, scale, and rotation of the sprite automatically.)  In our main loop, we tint the sprite green when the mouse is within the sprite bounds, and white when it is not.

![Mouse-over test with simple rectangular bounds check](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bpuy0hgxsigll2uz23u8.gif)

Now let's extend this to pixel perfection!  Press control-C to break out of the previous demo, and `edit` again.  Modify the code as follows, with a new `overSolidPixel` function to check whether a given x,y position (such as the mouse) is over a non-transparent pixel in the sprite image.

```
clear
sp = new Sprite
sp.image = file.loadImage("/sys/pics/Wumpus.png")
sp.localBounds = new Bounds
sp.localBounds.width = sp.image.width
sp.localBounds.height = sp.image.height
sp.x = 480; sp.y = 320; sp.scale = 2
display(4).sprites.push sp

Sprite.overSolidPixel = function(pos)
	x = (pos.x - self.x) / self.scale + self.image.width/2
	y = (pos.y - self.y) / self.scale + self.image.height/2
	c = self.image.pixel(x,y)
	text.row = 25; print c  // (for debugging)
	return c[-2:] > "88"
end function

while true
	if sp.contains(mouse) and sp.overSolidPixel(mouse) then
		sp.tint = color.green
	else
		sp.tint = color.white
	end if
end while
```

This `overSolidPixel` function starts by converting the global x/y position given into coordinates local to the sprite image.  (The code as shown assumes a non-rotated sprite with uniform scale.  Extending it to support-non uniform scale and rotation is left as an exercise for the reader.)  Then we look up the pixel color at that point in the sprite, and return whether the last two digits (the alpha channel) are greater than "88", i.e., are closer to fully solid (FF) than transparent (00).

Now when you run it, you'll find you can mouse over the corners or gaps in the sprite without it turning green.  Only when the mouse is over a solid part of the sprite image, do we consider it really "over" the sprite.

![Mouse-over test with pixel perfect check](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tie4o0q7q8dggr3r36z3.gif)

So there you have it!  The next time you find a rectangular bounds isn't good enough for click detection on your sprite, remember this technique, and you'll have pixel perfection in no time.