Mouse Cursor Magnetism Edit

By Joshua Smyth

While developing the user interface for the inventory screen of my Role Playing Game, I came across what I thought was an interesting problem. As a long time computer user I thought I was more than familar with the role of the mouse, infact I would say that it is the single best input device ever created. You know it's designed well because it is so utterly transparent, most of the time you barely notice that you are using it, until it doesn't work as expected of course.

My personal design philosophy on user interfaces is that the interface should work as the user expects it to. So when I created the UI for my RPG and it didn't immediately work as I expected it to,I knew something wasn't right, and I needed to fix it.

The Problem

The thing with mouse cursors is that they are normally shaped like pointers and use only one pixel to determine the location of the actual mouse click. The enlarged image below shows a typical mouse cursor, with its pixel-perfect 'hotspot' in yellow.


The problem I had was that when I wanted the player of my game to pick up an item out of the inventory, I wanted the mouse cursor to change into that item. And Lo and behold, because the items are not pointer shaped, placing the object where the user wanted became a pain. Consider the image below.

File:Mousecursor sword.gif

The sword is the mouse cursor, now it seems fairly obvious that I'm wanting to place the sword in this box. But the game won't let me because the single pixel that defines where the mouse cursor is, is not within this box, though the majority of the graphic is. I am giving erronous feedback to the user, the visual display suggests that I can place the sword in the box, but the underlying invisible programming is saying the user is not clicking on the box. One way of solving this problem would be to change the hotspot location of the cursor to the centre of the graphic, in some ways this makes sense and would be an acceptable solution, however it is not perfect as the hotspot is still defined by some single pixel sized point.

If we expand on the idea of what constitues a mouse click, then we can implement mouse magnetism, or in other words, what we would like to be able to do is this. If an object that can be clicked on is in some general area, local to the mouse, then that is the object that the user intends to interact with.

Collision Detection

I decided to use a trustly collision detection algorithm for my UI problem. Drawing a rectangle around my mouse cursor, I want to check if there is an overlap between my mouse cursor rectangle, and my clickable object rectangle.

File:Mousecursor sword 2.gif

// Note: In my example the rectangles are defined by .left .right .top and .bottom as integer coordinates on the screen.

bool RectRectIntersection(Rect BoxA, Rect BoxB) { // Check for Intersection if ((BoxA.right > BoxB.left) && (BoxA.bottom > { if ((((BoxB.right) > BoxA.left) && ((BoxB.bottom) > { // Hit return true; } else { // no intersection return false; } } else { // no intersection return false; } }

Simple enough, if there is any overlap between the two rectangles then a hit is made. While this is defintely an improvement on matters, we have a problem if there are two objects locally competing for the mouse click.

File:Mousecursor two objects.gif

I've switched back to a standard mouse cursor to emphasise a point. When you define the mouse cursor bounding rect, the centre of the rect should contain the hotspot pixel you wish to use. If we defined our rect by just covering the bitmap image of the cursor, then we could potentially introduce bias to the right hand side.

File:Mousecursor bias.gif

In the example above it looks like object A is closer, but if our mouse rectangle is incorrectly bounded then the click actually goes to object B. As I mentioned before, expectability is key in UI design, we expect object A to be clicked. Therefore object A must be clicked.

So how can we modify our intersection detection algorithm to work out which object is closest, well thats easy enough. After we work out that an intersection has been made, then we can work out the area of the intersection. The object which has the greatest area of intersection is the closest object to our mouse cursor. One last check you may wish to include is a test for a minimim intersection area. Say the click is only made if the area is at least 10%, this is a good parameter for fine tuning the amount of magnetism you want your mouse cursor to have.

Here is the modified code to work out the area of intersection.

// Function returns numerical value as a percentage of coverage over boxA // Where BoxA is the mouse rect int CalculateRectRectCoverage(SDL_Rect BoxA, SDL_Rect BoxB) { int width; int height; int areaofoverlap; int areaBoxA; int overlappercentage;

// first check intersection if ((BoxA.right > BoxB.left) && (BoxA.bottom > { if ((((BoxB.right) > BoxA.left) && ((BoxB.bottom) > { // get height and width properties of our intersection rect width = min_int(abs(BoxA.right - BoxB.left), abs(BoxB.right - BoxA.left)); height = min_int(abs( - BoxB.bottom), abs( - BoxA.bottom));

areaofoverlap = width * height; } else { return 0; // no coverage } } else { return 0; // no coverage } areaBoxA = (BoxA.right - BoxA.left) * (BoxA.bottom -; overlappercentage = int((float(areaofoverlap) / float(areaBoxA)) * 100);

return overlappercentage; }

// Takes two integers and returns the minimum value int min_int(int A, int B) { if (A <= B) { return A; } else { return B; } }