Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more

Tech

Depth base mouse collision

Depthbasemousecollision_blog

Posted by Mike Dailly on 19 December 2017

We've been asked a few times about how to have the mouse only click on the topmost instance of a stack of overlapping instances, and since it's actually pretty straightforward to do, we thought we'd do a little tutorial on just how you go about it.

Let's start by creating a new project and 4 new objects, which we'll call oA,oB, oC and oController. Now create 3 basic sprites - colour them red, green and blue, and you might want to add a white border to them so you can see the overlap later - then assign them to objects oA, oB and oC respectively. You need to open the room editor now, and create two new instance layers so there are three in total (one should already be there from when the project was created), and then name them "oAlayer", "oBlayer" and "oC_layer". Now add various instances of each object to each layer, only placing instances of the object with the same name as the layer you are currently adding to. You should end up with something like this:

Manic Miner Baddies

Now since each layer is set at a separate depth, the idea here is to use the Collision Event to track which object is "closest" to the camera, ie: which one is "on top" with the lowest depth value. Open the object oController now, add a new Create Event event and define the following variables:

/// @description initialise click variables
instance_last_clicked = -1;
global.instance_clicked = -1;

Now create a new Begin Step event in the oController object and add this:

/// @description reset collision ID
global.instance_clicked = -1;

This is what we'll use to track the instances, and which one is currently the top one. Leave the controller object for a moment and add a new Script resource. We'll call it TestInstanceDepth() and in it add this code:

/// @description check to see if we are higher than the current instance
if( global.instance_clicked ==-1 ){
    global.instance_clicked = id;
}else{
    if( global.instance_clicked.depth>depth ){
        global.instance_clicked = id;
    }   
}

This simply checks the global "instance_clicked" variable to see if any instance has been clicked yet, and if not it sets the global variable to the ID of this instance. If the global variable has been set, then the script compares the depths of the instance currently stored with this instance, and if it's lower (and so "above") we set the global variable this instance ID instead.

To use this script, you need to open each of the objects oA,oB, oC, and give them a Mouse Left Down event, and in the event have them call this script:

/// @description detect mouse
TestInstanceDepth();

We can go back to the oController object now, as we want to display the result of this mouse click. For that, we'll add a new Step event containing these lines of code:

/// @description check collision, and do something with it
if( instance_last_clicked != -1){
    instance_last_clicked.image_blend=c_white;
}
if( global.instance_clicked != -1 ){
    global.instance_clicked.image_blend = c_gray;
}
instance_last_clicked = global.instance_clicked;

All we're doing here is colouring the instances a different colour to show when it is being clicked and when not.

You can now add the oController object anywhere in the room, on any instance layer, and then run the project. You should be able to click on the various squares, and only the ones on the top of the pile will darken as you click them. This really is all there is to it. Obviously where we change the colour you could call some kind of collision script based on the object/instance you've clicked on, or even start a drag event letting you drag things around.

Expanding on the Idea

What we've done so far is actually the core idea behind a windowing and icon system - detect what you've clicked on, then "lock" to that and as the mouse moves and move it around too. We weren't going to bother with dragging things in this blog, but what the heck... why not!

To start with, let's change the oController Create event first and rename the "last clicked" variable to something more meaningful:

/// @description initialise click variables
instance_locked = -1;
global.instance_clicked = -1;
inst_dx = 0;
inst_dy = 0;
new_depth = -1;

Note the extra variables that we've added here. We'll use these in the Step event so that instead of just changing the blend colour on mouse down/up, we can now use the new lock variable to drag the object around as the mouse moves. We will also set its depth the be above everything else, popping it to the front - this simulates normal Windows behaviour as you get when clicking on a window that's in the background on your desktop (although you don't have to do this, of course). So, in the oController Step event, change it to the following:

/// @description check collision, and once clicked, lock onto it

// New click event?
if( global.instance_clicked != -1 ){
    global.instance_clicked.image_blend = c_gray;   // set the colour so we can see the lock
    instance_locked = global.instance_clicked;      // lock onto this instance
    instance_locked.depth = new_depth--;            // pop the instance to the top
    inst_dx = instance_locked.x - mouse_x;          // get the difference between mouse and instance
    inst_dy = instance_locked.y - mouse_y;
    global.instance_clicked = -1;
}

// if we're locked on, drag it around.
if( instance_locked != -1 ){
    instance_locked.x =     mouse_x+inst_dx;        // get the new instance location by offsetting
    instance_locked.y =     mouse_y+inst_dy;        // the mouse BACK to the instance   
}

Now we need add a new event to the controller object: a Global Left Released mouse event. This will deactivate the lock once the mouse button is released with the following code:

/// @description deactivate the instance lock
if( instance_locked != -1 ){
        instance_locked.image_blend = c_white;
        instance_locked = -1
        inst_dx = 0;
        inst_dx = 0;
}

Finally, we need to change all the other objects - oA,oB and oC - from a Mouse Left Down event, to a Mouse Left Pressed event so that the checking script will fire only once when clicked.

After you've done that, you can run it and see what we have....

Manic Miner Baddies

Summary

So, there you have it! Easy depth based mouse collision. This very simple example takes moments to create, but once you've got it up and running you'll find that it's a really nice effect that has a multitude of uses, especially when making UI elements. After you've finished this tech blog, why not take a few minutes to "play" with the project you've made? See if you can't get the clicked instance to follow behind the mouse movement, for example? Or maybe see if you can add flicking to an instance, so that a quick click-drag-release imparts momentum on it?

Until next time, happy game making!

Back to Top