SOLVED [Using Layers] GMS 2 draws instances with the inverse depth 🤔

FoxyOfJungle

Kazan Games
Hello!
I had to create this topic because I spent hours trying to work around the following problem: Game Maker Studio 2 draws objects with depth in the reverse order of how they were created... Basically I have the same problem as that person on this topic (in which I didn't find the right answer).


1 - I'm creating layers manually, I just want to insert instances in those layers, in which the last created ones are on top of the others.

See the following situation:


2 - This is what a have:


(Note that every new instance is created at the lowest possible level)



3 - This is what I want: For this to work, I need to remove the draw event from the object and draw it manually in the draw event of the obj_controller, however, this causes the depth of the layers to be disregarded.





4 - Code: How am I doing all this?

>> obj_controller: <<

CREATE EVENT:

GML:
global.Layer_Index = 0;
global.Layer_Array[0] = layer_create(0, "Default Layer");

STEP EVENT:

GML:
// create the instance in the current layer being used
if mouse_check_button_pressed(mb_left)
{
    instance_create_layer(mouse_x, mouse_y, global.Layer_Array[global.Layer_Index], obj_block);
}


// change what layer is being used
if keyboard_check_pressed(vk_left) global.Layer_Index -= 1;
if keyboard_check_pressed(vk_right) global.Layer_Index += 1;


// clamp current layer index
global.Layer_Index = clamp(global.Layer_Index, 0, array_length(global.Layer_Array)-1);


// reorder layers depth (I don't know if this is necessary and how I would change it...)
if layer_exists(global.Layer_Array[global.Layer_Index])
{
    for (var i = 0; i < array_length(global.Layer_Array); i+=1)
    {
        layer_depth(global.Layer_Array[i], -i);
    }
}

KEY PRESS: D (destroy current layer with every instance inside):

GML:
layer_destroy(global.Layer_Array[global.Layer_Index]);
global.Layer_Index = array_length(global.Layer_Array);

KEY PRESS: A (add a new blank layer)

GML:
global.Layer_Array[array_length(global.Layer_Array)] = layer_create(0,"LayerName_"+string(irandom(999)));
global.Layer_Index = array_length(global.Layer_Array)-1;

DRAW GUI:

GML:
// draw layer list
for (var i = 0; i < array_length(global.Layer_Array); i+=1)
{
    var _id = global.Layer_Array[i]
    if layer_exists(_id)
    {
        var _name = layer_get_name(_id);
    }
    else
    {
        var _name = "--------";
    }

    draw_text(600, 10, "LAYER LIST:\n=============")
    draw_text(600, 60 + i*20, _name);
}


// DEBUG
draw_set_color(c_lime);
draw_text(10, 10, fps_real);
draw_text(10, 30, fps);
draw_set_color(c_white);

var _id = global.Layer_Array[global.Layer_Index];
if layer_exists(_id) {var _name = layer_get_name(_id);} else {var _name = "---";}

draw_text(10,100,"Layers (array_lenght): "+string(array_length(global.Layer_Array))); //layers quantity
draw_text(10,120,"Layer Current (layer_array_index): "+string(global.Layer_Index) +string(" | ") +string(_name) ); //current layer index


>> obj_block: <<

CREATE EVENT:

GML:
// get the name and depth just for debug
my_layer = layer_get_name(layer)+string(" | ")+string(layer_get_depth(layer));

DRAW EVENT:

GML:
//draw object normally
draw_self();
draw_set_halign(fa_center);
draw_text(x, y-48, my_layer);
draw_set_halign(fa_left);

RIGHT PRESSED:

GML:
// destroy if the object is on the current layer
if (layer == global.Layer_Array[global.Layer_Index])
{
    instance_destroy();
}

OBJECTIVE:

Create instances, and their depths are always closer to the camera than the ones below (the top most), respecting the depth of the leyer in which the instance is.
Could someone help me with any solution? I will be very grateful! Thank you.
 
Last edited:

TheouAegis

Member
My idea is to use layer_script_end() for each layer. In the script, check if it is the Draw event, get a list of all instances on the layer, then draw the list in reverse. I can't test it myself, so I'm just tossing it out there.
 

FoxyOfJungle

Kazan Games
My idea is to use layer_script_end() for each layer. In the script, check if it is the Draw event, get a list of all instances on the layer, then draw the list in reverse. I can't test it myself, so I'm just tossing it out there.
Hey, I tried, but it only works with the first layer created, when I try to create a new layer, strange things happen, when I create a new instance, it creates the instance "delayed" and not in the actual position of the mouse, I don't know what to do. Thanks for the answer.


GIF:




Code: (CREATE EVENT of obj_controller)

GML:
// SETUP LAYERS
global.Layer_Index = 0;
global.Layer_Array[0] = layer_create(0, "Default Layer");


// Instances draw scripts
function layer_script_obj_block()
{
    /// @func layer_script_obj_block()
    if (event_type == ev_draw)
    {
        if (event_number == 0)
        {
            var _array = layer_get_all_elements(global.Layer_Array[global.Layer_Index]);
           
            for (var i = 0; i < array_length(_array); i+=1;)
            {
                //if (layer_get_element_type(_array[i]) == layerelementtype_instance)
                //{
                    with (instance_find(obj_block, i))
                    {
                        draw_self();
                        draw_set_halign(fa_center);
                        draw_text(x, y-48, my_layer);
                        draw_set_halign(fa_left);
                    }
                    //show_debug_message(_array[i]);
                //}
            }
        }
    }
}

layer_script_end(global.Layer_Array[global.Layer_Index], layer_script_obj_block);
 
Last edited:

TheouAegis

Member
You wouldn't be using instance_find(), you'd be using layer_instance_get_instance() and there might still be slow down with that, especially the more layers you have.
 

FoxyOfJungle

Kazan Games
You wouldn't be using instance_find(), you'd be using layer_instance_get_instance() and there might still be slow down with that, especially the more layers you have.
Oh, I did not know... 😅 Thanks!, well, it worked, however, if I change the layer, it stops drawing the other instances... It must be some silly problem but I'm not able to identify it. 🤔 Look:



Code:

GML:
// SETUP LAYERS
global.Layer_Index = 0;
global.Layer_Array[0] = layer_create(0, "Default Layer");


// Instances draw scripts
function layer_script_obj_block()
{
    /// @func layer_script_obj_block()
    if (event_type == ev_draw)
    {
        if (event_number == 0)
        {
            var _array = layer_get_all_elements(global.Layer_Array[global.Layer_Index]);
          
            var i = array_length(_array)-1;
            repeat (array_length(_array))
            {
                if (layer_get_element_type(_array[i]) == layerelementtype_instance)
                {
                    var L = layer_instance_get_instance(_array[i])
                    with (L)
                    {
                        show_debug_message(L);
                        draw_self();
                        draw_set_halign(fa_center);
                        draw_text(x, y-48, my_layer);
                        draw_set_halign(fa_left);
                    }
                }
                i -= 1;
            }
        }
    }
}

layer_script_end(global.Layer_Array[global.Layer_Index], layer_script_obj_block);
 
Last edited:

FoxyOfJungle

Kazan Games
Nevermind, I got it working! Thank You @TheouAegis
Was a simple loop.

Final Code:

GML:
// SETUP LAYERS
global.Layer_Index = 0;
global.Layer_Array[0] = layer_create(0, "Default Layer");


// Instances draw scripts
function layer_script_obj_block()
{
    /// @func layer_script_obj_block()
    if (event_type == ev_draw)
    {
        if (event_number == 0)
        {
            var u = 0;
            repeat (array_length(global.Layer_Array))
            {
                var _array = layer_get_all_elements(global.Layer_Array[u]);
          
                var i = array_length(_array)-1;
                repeat (array_length(_array))
                {
                    if (layer_get_element_type(_array[i]) == layerelementtype_instance)
                    {
                        var L = layer_instance_get_instance(_array[i])
                        with (L)
                        {
                            // Draw Object
                            draw_self();
                            draw_set_halign(fa_center);
                            draw_text(x, y-48, my_layer);
                            draw_set_halign(fa_left);
                        }
                    }
                    i -= 1;
                }
                u += 1;
            }
        }
    }
}

layer_script_end(global.Layer_Array[global.Layer_Index], layer_script_obj_block);
 
Last edited:
Here's a touched up version of FoxyOfJungle's solution.

There are a few things to keep in mind when using this technique:
  • This will technically render anything on the "items" layer twice if the instances have "visible" as true. (If "visible" is false, they will still draw using this technique)
  • Game Maker Studio developers have no guarantees about render order, so this solution might break in the future if "layer_get_all_elements" changes the order of the elements returned.

GML:
//
//
// Call this on "Room Start" in persistent controller object
//
// Create objects with:
// instance_create_layer(x,y,global.itemLayer,obj_item);
//
global.itemLayer = layer_create(1, "items");

function layer_script_render_in_create_order() {
    if (event_type != ev_draw) {
        return;
    }
    if (event_number != 0) {
        return;
    }
    var elements = layer_get_all_elements(layer);
    for (var i = array_length(elements)-1; i >= 0; i--) {
        var el = elements[i];
        if (layer_get_element_type(el) != layerelementtype_instance) {
            continue;
        }
        var inst = layer_instance_get_instance(el);
        with (inst) {
            // note(jae): 2022-07-30
            // wont work unless object has draw event
            event_perform(ev_draw, 0);
            
            // If no draw event, just use this:
            // draw_self();
        }
    }
}
layer_script_end(global.itemLayer, layer_script_render_in_create_order);
 
Top