• 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!

GML want to get Instance's coordination

Hey peepz i'm back again!

I have a map that is draggable with the mouse.
Now i have some icons that are on the map itself, and i want to keep those icons "attached" to the map when dragged.

I came up with this code which will put the object on the map, but takes a lot of time to get it in the right spot,
so that's not really an option if i want to put a lot of icons on the map. Plus it looks like it is dragging behind. so it looks a bit weird.
GML:
pinpoint_obj.x = Map_obj.x + 2375;
pinpoint_obj.y = Map_obj.y + 1895;
A better solution is to get the coords of the Instance which i put in the room myself,
but i have no idea how to get that, or even how to put that to use to make it "Attached" to the map

Do any of you geniuses have an idea how this work?
Or perhaps know a better solution which doesn't drag behind?
Thanks in advance
 

woods

Member
from what i understand, basically it has to do with the order of operations ;o)

..calculating the new position of the icon in relation to the moved map before you draw anything
 

Vusur

Member
End Step will fix your problem (don't change it, if you don't need it) BUT it is more of an ugly fix. You are delaying the logic problem.
To understand what is happening, we have to dive into events, event order, instance order and so on. So first some theoretically stuff.

First the code for a simple demo project (glad I did this back then and still have it):
IDE v2.3.2.560
Runtime v2.3.2.426

5 objects, named obj_a, obj_b, obj_c, obj_d and obj_e. The code for events are all the same. It outputs on the console, what event we are in and from which object it comes. The output is triggerd with space, for all events or alt, just for the step event (used later).

GML:
//Begin Step
if(keyboard_check_pressed(vk_space))
{
    var _obj = object_get_name(object_index);
    show_debug_message("In: " + _obj + " | Event: Beigin Step"); 
}

//------------------------

//Step
if(keyboard_check_pressed(vk_space) || keyboard_check_pressed(vk_alt))
{
    var _obj = object_get_name(object_index);
    show_debug_message("In: " + _obj + " | Event: Step"); 
}

//------------------------

//End Step
if(keyboard_check_pressed(vk_space))
{
    var _obj = object_get_name(object_index);
    show_debug_message("In: " + _obj + " | Event: End Step"); 
}

//------------------------

//Draw
if(keyboard_check_pressed(vk_space))
{
    var _obj = object_get_name(object_index);
    show_debug_message("In: " + _obj + " | Event: Draw"); 
}
A obj_creater, that creates obj_e with the key "E" and obj_d with the key "D". Also a draw event, that draws a text for the instance array (active instances, order).

GML:
//Key Press - D
instance_create_layer(x, y, "Instances", obj_d);

//-----------

//Key Press - E
instance_create_layer(x, y, "Instances", obj_e);

//-----------

//Draw
for(var _i = 0; _i < instance_count; _i++)
{
    var _inst = instance_id[_i];
    var _obj = object_get_name(_inst.object_index);
    draw_text(x, y + _i*16, string(_inst) + ": " + _obj);
  
  
}

The setup
In your room, put obj_a, obj_b, obj_c and obj_creater. obj_d and obj_e are created through code later.
In your IDE, you see on the left side the Room Editor. You'll finde the "Instances Layer Properties - YourRoomName" (ILP later) and "Instance Creatoion Order - YourRoomName" (ICO later). If you don't see ICO, look for "Properties - YourRoomName" and click on "Instance Creation Order".

Now we are getting fancy. We are creating some orders. We want to compare later, so we change some orders (drag elements in ILP and ICO).
Order for instances dragged into the room: obj_creater, obj_c, obj_b, obj_a
Order for ILP: obj_creater, obj_b, obj_a, obj_c
Order for ICO: obj_creater, obj_a, obj_c, obj_b

Three different orders, so maybe we see some interesting stuff. Lets start the program!

First we look at the drawn text from obj_creater. The text draws the instance stack and what object each instance is. Outcome:
Code:
//First test
ID: 100000: obj: obj_creater
ID: 100002: obj: obj_a
ID: 100003: obj: obj_c
ID: 100001: obj: obj_b
What do we see. Does it match any order we created? Well, the objects matches the ICO. Makes sense. It's a stack. And the IDs? Well if we sort them, they match ILP. Also makes sense. So there are connections. But what happens, if we press space? Outcome:

Code:
//First test
In: obj_a | Event: Begin Step
In: obj_b | Event: Begin Step
In: obj_c | Event: Begin Step
In: obj_a | Event: Step
In: obj_b | Event: Step
In: obj_c | Event: Step
In: obj_a | Event: End Step
In: obj_b | Event: End Step
In: obj_c | Event: End Step
In: obj_b | Event: Draw
In: obj_a | Event: Draw
In: obj_c | Event: Draw
First the obvious stuff. The events have an order. First ALL Begin Step events of all (active) instances in the room, then step, then end step and then draw. Good to know and what I expected. But is there an order for the objects within the event type? Draw event makes sense. Its the ILP. One of the reasons, why sprites overlap in a specific way. But what is with the other event types? Can we find one of our orders in it? NO! It doesn't match how we put the objects in our room. It doesen't match the ILP and it doesn't match the ICO. But from where comes this order? It looks like the order in my Asset browser. This matches the order on how I created the objects. Let's confirm that and delete the object obj_a and reacreate(duplicate) it(I also did this with obj_d).
With that, the order for the created objects in my game becomes: obj_creater, obj_b, obj_c, obj_e, obj_a, obj_d.
Now recreate the order meantioned above. How you put the objects in your room, ILP and ICO. We run the programm again.

Code:
//second test
ID: 100000: obj: obj_creater
ID: 100002: obj: obj_a
ID: 100003: obj: obj_c
ID: 100001: obj: obj_b
Same as before. And what happens, if we press space?

Code:
//second test
In: obj_b | Event: Begin Step
In: obj_a | Event: Begin Step
In: obj_c | Event: Begin Step
In: obj_b | Event: Step
In: obj_a | Event: Step
In: obj_c | Event: Step
In: obj_b | Event: End Step
In: obj_a | Event: End Step
In: obj_c | Event: End Step
In: obj_b | Event: Draw
In: obj_a | Event: Draw
In: obj_c | Event: Draw
Draw event matches ILP. Good. And the Step event? Well... it's a big ?????. It looks like it matches the ILP. But with the previous test we know, it can't be ILP. It's a coincidence now. The order it was created in the asset browser can't be it either, because that order became b, c, a. Maybe because I duplicated a, deleted the old one and renamed it? Then lets delete a for real and recreate it manually. I make it short. No difference. Now the assumption is, while we have control over the draw event with ILP and the instance stack with ICO, we have no control in a step event with which instance is processed first. But does creating new instances on the fly change stuff? Now obj_d and obj_e come handy.
We run again, but this time I press "D" and then "E". This creates obj_d first and then obj_e.
What does the outcome look like:

Code:
//third test
ID: 100000: obj: obj_creater
ID: 100002: obj: obj_a
ID: 100003: obj: obj_c
ID: 100001: obj: obj_b
ID: 100005: obj: obj_d
ID: 100006: obj: obj_e
This makes sense. New ID+1 and ontop of the instance stack. And what is with the event order (I cut begin and end)?

Code:
//third test
In: obj_e | Event: Step
In: obj_b | Event: Step
In: obj_d | Event: Step
In: obj_a | Event: Step
In: obj_c | Event: Step

In: obj_e | Event: Draw
In: obj_d | Event: Draw
In: obj_a | Event: Draw
In: obj_b | Event: Draw
In: obj_c | Event: Draw
Waaaaaiiiit. Didn't we say draw matches ILP? This doesn't look like it anymore? And even worse, look at the step event? While draw looks like a pattern, step is more random than ever. Ok, next run, I press "E" first and then "D".

Code:
//fourth test
ID: 100000: obj: obj_creater
ID: 100002: obj: obj_a
ID: 100003: obj: obj_c
ID: 100001: obj: obj_b
ID: 100005: obj: obj_e
ID: 100006: obj: obj_d
Makes sense and confirms ID+1 and ontop of the instance stack. And the event order?

Code:
//fourth test
In: obj_e | Event: Step
In: obj_b | Event: Step
In: obj_d | Event: Step
In: obj_a | Event: Step
In: obj_c | Event: Step

In: obj_d | Event: Draw
In: obj_e | Event: Draw
In: obj_a | Event: Draw
In: obj_b | Event: Draw
In: obj_c | Event: Draw
Waaaaait 2.0 now with extra confusion. Step is the same, but draw changed again? There is a pattern with draw. The stuff we put manually in the room matches the ILP. New stuff will be processed first in draw.

But what does this mean for us? Well, at this point I would say, expecting a specific order in a event_type is not a good idea. Draw has a pattern, but the steps look like random. We are depending on the mercy of GM. I didn't found a way to manipulate it thou. If someone knows about it, I would appreaciate it.

Now we apply the new knowledge to your problem. I repeat again, step_end will fix it, but I don't like it. I visualize the problem with the step "stack"/order and explain, why the stuff lagged behind. We already know, step itself is random or atleast I didn't see a pattern for which instance is executed first. Your step stack looked probably like this:

Code:
In: obj_e | Event: Step
In: obj_b | Event: Step <- sets pinpoint_obj coords to map_obj coords
In: obj_d | Event: Step
In: obj_a | Event: Step <- map_obj gets new coords through dragging
In: obj_c | Event: Step
This means, the x and y value of pinpoint_obj is always calculated with old map_obj x and y. They are depending in one way, through the step order, but we don't have control over the step order. It could have been differently.

Code:
In: obj_e | Event: Step
In: obj_b | Event: Step <- map_obj gets new coords through dragging
In: obj_d | Event: Step
In: obj_a | Event: Step <- sets pinpoint_obj coords to map_obj coords
In: obj_c | Event: Step
This means, pinpoint_obj would get the actual new coords from this step cycle. But again, we have no control over it. Putting the code into end step fixes it, because step end is executed AFTER all step events are done. You "force" this behaviour. Sounds good, but can lead to a future problem. Lets express this dependency in a shorter way.

A -> B == A depends on B ==> step B -> end_step A
Now we increase the chain.
A -> B -> C ==> begin_step C -> step B -> end_step A
Oh, oh.. with three it works, but what is with four? We are out of different step events. *sad noises* Now we would depend on the "random" step execution order. I admit, this fabricated case is a really rare case, but nice to know.
But can we solve this in a different way? Yes. The Idea is setting the map coords BEFORE we set pinpoint coords that depend on map coords. We take control over it. When it doesn't work with different step events(step, not begin, end, step), maybe within the same step in the same instance?

In your code, it would look like this:

GML:
//in case setting coords is in a independent object
//this is the same step event

Map_obj.x = //your new x coord /mouse_x
Map_obj.y = //your new y coord /mouse_y

pinpoint_obj.x = Map_obj.x + 2375;
pinpoint_obj.y = Map_obj.y + 1895;
Now pinpoint_obj gets the "newest" change from map_obj. It shouldn't lack behind (not 100% sure. I don't know if change like obj.x = foo can be read like bar = obj.x in the next line or if GM needs a little time to process it). Maybe you don't need a independent object at all? I mean, pinpoint_obj looks like it is depending on map_obj. So put it in map_obj like this:

GML:
//in map_obj step event

x = //your new x coord /mouse_x /new dragged location
y = //your new y coord

//tell every object, that depends on map_obj, the new coords

pinpoint_obj.x = x + 200;
pinpoint_obj.y = y + 100;
The line order is important. x and y have to be set BEFORE you tell pinpoint_obj about it.

You can even connect them deeper like this:

GML:
//create event for map_obj

//... all the stuff you have already
map_icon_list = ds_create_list(); //this holds all icons that depend on map_obj

//create event for map_icon_1

offset_x = 2375;
offset_y = 1895;
init_done = false;

//create event for map_icon_2

offset_x = 1898;
offset_y = 2828;
init_done = false;

//step event for both icons

if(!init_done && instance_exist(obj_map)) //do it once and check if there is a map
{
    ds_list_add(obj_map.map_icon_list, id); //tell the map, that this icon exists.
    init_done = true; //we are done

}

//step event of map_obj

x = //new x coord
y = //new y coord

var _size = ds_list_size(map_icon_list);
for(i = 0; i < _size; i++)
{
    with(map_icon_list[| i])
    {
        x = other.x + offset_x;
        y = other.y + offset_y;
    }

}
There are a few assumptions. There is only one map_obj instance and the offset variables are named the same in each map_icon object. Doesn't really matter, this is just the general idea. You can tailor it for your personal need.
Now map_obj is the main player for all the map icons. map icon depends on map_obj. Also, the creation order or how GM process the event order doesn't matter for you anymore. Also end_step is not needed.

cheers
 
Last edited:
Top