GameMaker Finding the closest instance above another one

MCHLV

Member
Hello,

How would you go in GMS2 about finding the closest button above another one ? (I found other approaches based on data structures or with "up" variable manually set to store the target) but did not find the answer for my case). I gave it a try below, maybe there are better ways...


I have a simple menu system, where all my buttons are children to a oGUI_Button_Par.
Everything is managed by a controller oGUI_Controller_Menu. It moves the selection, store the selected button (selected_button_id) and trigger the action.

So,when I press the up_key, I want my controller to find the button just above. I think I will use the following code, this would be run in the step event of my controller object when up_key is pressed.

Code:
// 1.check if a button is selected. Select one if not
if ( selected_button_id = noone ) { selected_button_id = instance_nearest(x, y, oGUI_Button_Par); }
if ( selected_button_id = noone ) { exit; }

// 2.find the closest button above by looping in all candidates and finding the best one
// 2.1.setting temp variables to default value
var _top_nearest_button              = noone;
var _distance_to_top_nearest_button = display_get_gui_height();

// 2.2.looping in candidates,(=buttons a-other than the selection, b-above, c-closest)
with (oGUI_Button_Par)
{
if ( id != selected_button_id )                // this button is not the selection
{

    if ( bbox_bottom < other.bbox_top )         // this button is above me
    {
        if ( ( point_distance(x,y, other.x, other.y) < _distance_to_top_nearest_button ) // best candidate so far (closer one)
        {
            _top_nearest_button = id;
            _distance_top_nearest_button
        }
    }
}
}

// 3.do something to _top_nearest_button like giving the focus
selected_button_id = _top_nearest_button ;
with (selected_button_id)
{
    // bla bla
}
Thank you for your time and comments !
M.
 
C

Catastrophe

Guest
I don't see why this wouldn't work, and something like this won't hurt performance so just do whatever works.

One issue is you'll want to make sure the abs(x - other.x) is within a certain range probably, although TBH it depends on how your menu is setup, or maybe you should even check x = other.x. But this is just design.

I would also just suggest putting everything but the bottom part into a script that returns an id, and then you can do whatever.
 

MCHLV

Member
Thank you both.

I would also just suggest putting everything but the bottom part into a script that returns an id, and then you can do whatever.
Yes. Good point, I will build a find_nearest_button_above.
So I will just give it a go and let know how this worked. I was curious if there was more elegant way than my code.
Thanks. M.
 

TheouAegis

Member
Just create all the buttons in the proper from top down, then just index the instances. If you know how many instances there will be, you can loop back up with a normal modulo, otherwise you can loop with an instance_exists.

Code:
if UP {
    if !instance_exists(--selected)
        selected  += instance_number(oGUI_Controller_Menu);
}
if DOWN {
    if !instance_exists(++selected)
        selected -= instance_number(oGUI_Controller_Menu);
}
 

MCHLV

Member
Just create all the buttons in the proper from top down, then just index the instances.
Thank you @TheouAegis.
By Indexing instance, do you mean setting an 'index' variable in my buttons to identify them in my code (instead of id), so that this index is in the proper order ? Or maybe creating my instance in a row so that the native id is the correct order...Just for my understanding.

I am trying to build something as easy as possible for others to use: just drop instance/children in the IDE and the controller will handle the rest (including proper arrows movement and tab).

Also, thanks beacause I I learnt something (I if understood correctly) : you can decrement the variable selected and pass it to the instance_exits function in one line of code.
M.
 

TheouAegis

Member
The native id ordering would be the simplest (in my opinion). The code I posted does have a few kinks (so to speak). It's fine for menus where all the buttons are essentially vertically arranged, even if they're staggered; if you had buttons side-by-side, then pressing up would send you to the bottom of the other column, or pressing down would send you to the top of the other column. Sometimes desired, sometimes undesired.

If it's going to be arbitrary dropping of buttons into the room/system, then you should just be storing everything in a list. Let the program generate a list or 2D array based on where all the buttons are in proximity to each other, then move around the menu using that list/array. Sure, menus won't be noticeably slow, but the proper method is to iterate through a list, not perform numerous proximity checks with every key press. Ideally, you wouldn't even have any objects at all (except the one handling the menu), all the buttons would just be drawn sprites.
 
That was my original thought - but as TheouAegis went on to explain: it's all dependant on the buttons being arranged in a way where you know the structure, and are just using basic maths to figure out what row you're in etc

Not very flexible at all. Having rewritten it twice (deliberate structure / moving through the instance order) it felt that wasn't what you had in mind, as both were too inflexible / easily broken, and in the end I just deleted my suggestions.

If you had a layout that could be altered (say you allowed the player to move around icons, and position them where they wanted - for their convenience) then the buttons would seem to have to be objects, and moving through them would require distance / positioning comparisons and a lot more effort.

Which "version" is right for you would depend on what it is you intended to do. As such, I felt that your original version was more on the right path for being flexible, and would just require expanding.
 

Gavolot

Member
I just wrote down the instance that should be "in focus" now in the global variable. Then I checked if the input of the mouse doesn’t match, then first we make it undefined
Then the code wrote it again in the event which is higher, for example, to start drawing the interface. (draw_gui_begin, draw_gui_end make undefined)
 

MCHLV

Member
@the_dude_abides - Thank you for your advice. I am just doing this to build a system as simple as possible for others to use. So they just can drop their buttons in the IDE (and not bother themself with creation code or data structure).
It is not a big chunk of my projet.
So far I am using a menu engine I built upon an asset from the market place I downloaded a while ago (GMS 1.4 https://marketplace.yoyogames.com/assets/414/simple-menu-engine). It works fine but require the dev. to specify a system names for each button (used instead as a custom id), and map each button with their upper button, lefter button etc...so that the focus is passed properly through keyboard. At the end of the day, I think I will test the code I am thinking of above. But compared to my actual need data structures would be the best solution.

@Gavolot - yes. Agree with you. I am more looking to manage keyboard inputs.

I let you know how thhis goes. M
 
Last edited:
Top