GMS 2.3+ Why use "static function()" inside a struct function??

FoxyOfJungle

Kazan Games
I tried to look in the manual but I couldn't find exactly what I wanted, it's because I'm using structs and I ended up in a situation where I need to use one function inside the other, but I saw people using static, but I don't know what the function is, I click on the function and it doesn't open anything in the manual.

GML:
function obj_instance(_x, _y) constructor
{
    idd = -1;
    xx = _x;
    yy = _y;
  
    v_text = "Test";
  
  
    static core = function()
    {
        // step
        if keyboard_check_pressed(vk_enter)
        {
            this_is_a_function("room_level1");
        }
      
      
        // draw
        draw_text(10, 10, v_text);
    }
}

And I also have a problem, when I press Enter and call the function this_is_a_function, it appears an error, indicating that this is a variable and was not found, but it is actually a global function.

GML:
function this_is_a_function(index)
{
    // destroy all instances
    global.game_instances[0] = 0;
    global.game_instances_n = 0;
  
    // set the id of the new room
    for (var r=0; r<array_length(global.game_room); r+=1)
    {
        var _struct = global.game_room[r];
        var _struct_array = variable_struct_get_names(_struct);
      
        for (var i=0; i<array_length(_struct_array); i+=1;)
        {
            if (variable_struct_get(_struct, _struct_array[i]) == index)
            {
                var _level = _struct.indx;
                global.game_room_index = _level;
            }
        }
    }
  
    // enable room loading
    global.game_room_can_load = true;
}
Any idea?
Thanks.
 

FoxyOfJungle

Kazan Games
About static: https://manual.yoyogames.com/GameMaker_Language/GML_Overview/Method_Variables.htm

If you receive an error message, please post the full error message.
Oh, that's what I was looking for, thanks!

Anyway, the error that appears is this:

Code:
___________________________________________
############################################################################################
ERROR in
action number 1
of Draw Event
for object <undefined>:

Variable <unknown_object>.game_room_goto(100050, -2147483648) not set before reading it.
at gml_Script_anon_obj_menu_gml_Room_room_core_Create_4507_obj_menu_gml_Room_room_core_Create (line 200) -                      game_room_goto("room_level1");
############################################################################################
gml_Script_anon_obj_menu_gml_Room_room_core_Create_4507_obj_menu_gml_Room_room_core_Create (line 200)
gml_Script_game_objects_layer_script_end_gml_Room_room_core_Create (line 331) -                      global.game_instances[i].core();
If it confused you, I'm trying to create a test game where you don't use objects, so I'm trying to call the function inside a layer_script_end, but it's like the function doesn't exist, and it's already been declared.
I tried to declare the function using gml_pragma, but it doesn't work either.
 

FoxyOfJungle

Kazan Games
The whole game works this way, this is not a serious project, I'm just trying to take this challenge to see how far I can go.
It is in the creation code of the first room, there is nothing inside the room, nor layers, everything is generated procedurally.

GML:
/*==============================================================================

              ------>>  Platformer Game by Foxy Of Jungle <<------

No object is used in this project, the whole game is found in that piece of code.
I don't recommend using this to make a new game, it serves only as a way to
study GML and it was just a personal challenge.

==============================================================================*/


/// GAME INIT ///

#region functions
// core
function game_init_essentials()
{
    //
}

gml_pragma("global", "game_init_essentials()");



// gui
function gui_mouse_x() {return device_mouse_x_to_gui(0);}
function gui_mouse_y() {return device_mouse_y_to_gui(0);}
function gui_w() {return display_get_gui_width();}
function gui_h() {return display_get_gui_height();}



// game
/// @func alert()
function alert()
{
    var _alert = string(argument[0]);
    if (argument_count > 1) {_alert += ": " + string(argument[1]);}
    show_debug_message(_alert);
}



/// @func game_room_add(index, room_name)
/// @arg index
/// @arg room_name
function game_room_add(index, room_name)
{
    /// @func _game_room(_ii, _nn)
    _game_room = function(_ii ,_nn) constructor
    {
        //variaveis para pegar à esquerda
        indx = _ii;
        name = _nn;
    }

    return new _game_room(index, room_name);
}



/// @func game_room_goto(index)
/// @arg index
function game_room_goto(index)
{
    // destroy all instances
    global.game_instances[0] = 0;
    global.game_instances_n = 0;

    // set the id of the new room
    for (var r=0; r<array_length(global.game_room); r+=1)
    {
        var _struct = global.game_room[r];
        var _struct_array = variable_struct_get_names(_struct);
   
        for (var i=0; i<array_length(_struct_array); i+=1;)
        {
            if (variable_struct_get(_struct, _struct_array[i]) == index)
            {
                var _level = _struct.indx;
                global.game_room_index = _level;
            }
        }
    }

    // enable room loading
    global.game_room_can_load = true;
}



/// @func game_room_goto_next()
function game_room_goto_next()
{
    // destroy all instances
    global.game_instances[0] = 0;
    global.game_instances_n = 0;

    // next room
    global.game_room_index += 1;

    // enable room loading
    global.game_room_can_load = true;
}



/// @func game_room_goto_previous()
function game_room_goto_previous()
{
    // destroy all instances
    global.game_instances[0] = 0;
    global.game_instances_n = 0;

    // previous room
    global.game_room_index -= 1;

    // enable room loading
    global.game_room_can_load = true;
}



/// @func game_instance_create(x, y, object)
/// @arg x
/// @arg y
/// @arg object
function game_instance_create(x, y, object)
{
    global.game_instances_n += 1;
    global.game_instances[global.game_instances_n] = new object(x, y);
    global.game_instances[global.game_instances_n].idd = global.game_instances_n;

    return global.game_instances[global.game_instances_n];
}

#endregion



#region setup resolution
var screen_width = 0;
var screen_height = 270;

aspect_ratio = display_get_width() / display_get_height();
game_zoom = 1;

screen_width = round(screen_height * aspect_ratio);
//screen_height = round(screen_width/aspect_ratio);

screen_width = round(screen_width);
screen_height = round(screen_height);

if(screen_width & 1) screen_width -= 1;
if(screen_height & 1) screen_height -= 1;

global.Main_Camera = camera_create_view(0, 0, screen_width, screen_height, 0, noone,0,0,0,0);
camera_set_view_size(global.Main_Camera, screen_width, screen_height);

room_set_camera(0, 0, global.Main_Camera);
room_set_view_enabled(0, true);
room_set_viewport(0, 0, true, 0, 0, screen_width*game_zoom, screen_height*game_zoom);

surface_resize(application_surface, screen_width, screen_height);
display_set_gui_size(screen_width, screen_height);
window_set_size(screen_width*game_zoom, screen_height*game_zoom);
#endregion



#region create rooms
global.game_room[0] = game_room_add(0, "room_menu");
global.game_room[1] = game_room_add(1, "room_level1");
global.game_room[2] = game_room_add(2, "room_level2");
global.game_room_index = 0;


// >>> [room_menu] obj_menu
function obj_menu(_x, _y) constructor
{
    // create event
    idd = -1;
    xx = _x;
    yy = _y;

    v_menu_title = "NoObjectsChallenge";



    // events [step and draw]
    static core = function()
    {
        // step
        if keyboard_check_pressed(vk_enter)
        {
            game_room_goto("room_level1");
        }
   
   
   
        // draw
        draw_text(10, 10, v_menu_title);
    }
}



// >>> [room_level1] obj_level1
function obj_level1(_x, _y) constructor
{
    // create event
    idd = -1;
    xx = _x;
    yy = _y;



    // events [step and draw]
    static core = function()
    {
        draw_text(xx, yy, "Level1");
    }
}
#endregion



#region create layers
global.game_layer_game = layer_create(0, "game");
#endregion



#region create other objects
// >>> obj_model [test object, not used]
function obj_model(_x, _y) constructor
{
    // create event
    idd = -1;
    xx = _x;
    yy = _y;



    // events [step and draw]
    static core = function()
    {
        draw_text(xx, yy, "Object_"+string(idd)+string("\nx: ")+string(xx)+string(" | y: ")+string(yy));
    }
}
#endregion



#region others variables
//alert(global.game_room[0].indx, global.game_room[0].name)
global.game_instances[0] = 0;
global.game_instances_n = 0;
global.game_room_can_load = true;
#endregion




/// MENU ///
game_room_goto("room_menu");
game_instance_create(0, 0, obj_menu);






// EXECUTE OBJECTS EVENTS
function game_objects_layer_script_end()
{
    /// @func game_objects_layer_script_end()
    draw_clear(c_black);


    // ROOM START
    // load and create rooms and levels
    if (global.game_room_can_load)
    {
        switch (global.game_room_index)
        {
            // room_menu
            case 0:
                show_debug_message("LOAD MENU LEVEL");
                break;
       
       
            // room_level1
            case 1:
                show_debug_message("LOAD LEVEL 1");
                break;
       
       
            // room_level2
            case 2:
                show_debug_message("LOAD LEVEL 2");
                break;
        }
        global.game_room_can_load = false;
    }




    // STEP EVENT
    //show_debug_message(global.game_room_index)
    if keyboard_check_pressed(ord("R")) room_restart();







    // DRAW EVENT
    // draw instances structs here
    if (array_length(global.game_instances) > 0)
    {
        for (var i = 1; i < array_length(global.game_instances); ++i)
        {
            // draw it [draw and step event]
            global.game_instances[i].core();
        }
    }


    // DRAW GUI EVENT
    if (event_type == ev_draw)
    {
        if (event_number == ev_gui)
        {
            draw_text(10, 240, fps_real);
        }
    }
}

layer_script_end(global.game_layer_game, game_objects_layer_script_end);

Answer for those who will see in the future:

 
Last edited:

kburkhart84

Firehammer Games
Variable <unknown_object>.game_room_goto(100050, -2147483648) not set before reading it.
FYI, that error is not actually complaining about the function call itself so much...rather it seems that for some reason there is no scope for it. Its like setting a variable to a random number and then calling variable.some_function(). Since variable doesn't correlate with a struct or an instance id, you get that exact error. I didn't read the whole code so I don't know exactly what the cause for that is in your case, but I wanted to clarify the error since you seemed to think it had to do with the function itself and it doesn't.

EDIT****

About static...the reason we use it on methods in structs is so that there is only one copy of that method in memory. If I have thousands of structs representing something like Vectors, I wouldn't want to have thousands of copies of the cross_product method of those. In general, it works the same for any variable inside any function. It technically initializes the variable once the first time the function is called, and then each time afterwards it refers to the original variable. If you did this in a function that was not a constructor, it would be similar to using a global variable and then accessing it, though not the same as the variable is only available inside that function. And methods created in struct constructor functions are literally the same thing, only the "variable" is a method instead of a number or string.
 

FoxyOfJungle

Kazan Games
for some reason there is no scope for it
That's what I thought the first time I saw the error, this is due to the fact that I'm using the layers to draw, it's like the function was not called before, I tried to make a delay, but if successful, I don't know if it could be a bug, but it does not work.
Is it because I am not using an object for this, so I think that might be the problem? 🤔


you seemed to think it had to do with the function itself
I hadn't even thought about it, the only thing that came to my mind was that it is being called from the layers. 🤔


About static...the reason we use it on methods in structs is so that there is only one copy of that method in memory.
It makes sense!, but like, if I was drawing it every step of the step, then it wouldn't be necessary to do that, would it?

It technically initializes the variable once the first time the function is called, and then each time afterwards it refers to the original variable. If you did this in a function that was not a constructor, it would be similar to using a global variable and then accessing it, though not the same as the variable is only available inside that function. And methods created in struct constructor functions are literally the same thing, only the "variable" is a method instead of a number or string.
I understand, thanks for the explanations, I had done some tests, but it seems that a static variable is executed in all places where the function is called, for example:

Let's say I have obj_a and obj_b, both call the cherry() function. Within this function, I create a "static alpha" variable, if I modify the alpha within the function, it will modify all the ones in which the function is being called, it is possible to initialize and update a variable within the function without having to use objects? But that this variable only changes in the object in which the function is being called?
 

FoxyOfJungle

Kazan Games
GML:
counter = function()
    {
    static num = 0;
    return num++;
    }
I'm really abusing this lately, it's super awesome
It's an interesting idea to do! Thanks!
Is there a problem if the variable keeps increasing infinitely?


You can do this too:

GML:
current_time * 0.03
GML:
draw_sprite(spr_test, current_time * 0.03, 10, 10);
The above code was not made to work, but the variable keeps increasing.

This makes it count, however it can be bad since the game is based on a time, for example, if I freeze the window and go back, the variable will have changed.
 
Last edited:

kburkhart84

Firehammer Games
That's what I thought the first time I saw the error, this is due to the fact that I'm using the layers to draw, it's like the function was not called before, I tried to make a delay, but if successful, I don't know if it could be a bug, but it does not work.
Is it because I am not using an object for this, so I think that might be the problem?
It is very possible that you not using any objects is causing this...well that you aren't using any objects, rather the way you are doing things. I think the engine doesn't initialize everything properly until actual steps get executed, and if you are coding this in some kind of loop in a script(which I assume you are doing since you don't have an object with a step event), you may never be letting the engine get to the fully initialized state.

I hadn't even thought about it, the only thing that came to my mind was that it is being called from the layers.
I don't think layers work for this. Methods are variables on objects, or variables in structs. I'm thinking the same issue would arise if you used the room_create code(the one that is attached to the room itself, not an object).

It makes sense!, but like, if I was drawing it every step of the step, then it wouldn't be necessary to do that, would it?
I'm not sure what you mean by "every step of the step." In any case, you don't HAVE to make anything static if you don't want to. You would simply need to either understand you are using more memory than needed if you create more than one of a struct with method variables, and the creation of those structs will take the same amount of time, instead of the performance gained by using static method variables. And for something like the "counter" example above, you would need to either use a global variable or a variable on an object to store the counter value if you insisted on not using a static variable.

Let's say I have obj_a and obj_b, both call the cherry() function. Within this function, I create a "static alpha" variable, if I modify the alpha within the function, it will modify all the ones in which the function is being called, it is possible to initialize and update a variable within the function without having to use objects? But that this variable only changes in the object in which the function is being called?
If you create a static variable in a function, it in a sense "belongs" to the function. As I understand it, it would technically create that variable on objects that are calling it as well, but it would refer to the same original variable, not creating variables on each object(if you understand the difference I'm getting at). I know for sure at the least, with structs, you can then access the variable after the fact elsewhere(which is why you can call the methods), but I'm not 100% if it does the exact same thing with objects, though logically it should.
GML:
counter = function()
    {
    static num = 0;
    return num++;
    }
I'm really abusing this lately, it's super awesome
Indeed, that's an interesting but perfectly valid use. Its one of those things that you may find people say it is a bad thing to do, similar to people who say you should never use global variables...but I personally say if it works for you and doesn't cause you issues, go for it!

The above code was not made to work, but the variable keeps increasing.

This makes it count, however it can be bad since the game is based on a time, for example, if I freeze the window and go back, the variable will have changed.
What about the code doesn't work? Is it part of the whole thing you have in the above code where you are abusing things instead of doing them the "normal" way(for fun and learning, I know)??? If it is, it may be related to that.
 

FoxyOfJungle

Kazan Games
It is very possible that you not using any objects is causing this...well that you aren't using any objects, rather the way you are doing things. I think the engine doesn't initialize everything properly until actual steps get executed, and if you are coding this in some kind of loop in a script(which I assume you are doing since you don't have an object with a step event), you may never be letting the engine get to the fully initialized state.
I also think that way, that must be it.


I don't think layers work for this. Methods are variables on objects, or variables in structs. I'm thinking the same issue would arise if you used the room_create code(the one that is attached to the room itself, not an object).
Yes, I had done that, for example, the gui_w() function and the others don't work inside the layer code either.

I'm not sure what you mean by "every step of the step." In any case, you don't HAVE to make anything static if you don't want to. You would simply need to either understand you are using more memory than needed if you create more than one of a struct with method variables, and the creation of those structs will take the same amount of time, instead of the performance gained by using static method variables. And for something like the "counter" example above, you would need to either use a global variable or a variable on an object to store the counter value if you insisted on not using a static variable.
I wanted to refer to the way the step event works, it is always executed/running, so I thought that with static, the function would not be "always updated", since it has already been called.


What about the code doesn't work? Is it part of the whole thing you have in the above code where you are abusing things instead of doing them the "normal" way(for fun and learning, I know)??? If it is, it may be related to that.
I meant in the sense that it can pass image_index more than enough.
so it may not be suitable, but either way the form I quoted can be used in other things like this:
GML:
image_angle -= current_time * 0.03;
 
It's an interesting idea to do! Thanks!
Is there a problem if the variable keeps increasing infinitely?
Couldn't really tell you, I'm really mostly using it just to keep track of some basic stuff. Working on some kind of sports game, and it's really useful to keep track of some team stats, for example. Of course it's nothing new functionally, but I like how it looks less cluttered in the editor.
 

kburkhart84

Firehammer Games
I wanted to refer to the way the step event works, it is always executed/running, so I thought that with static, the function would not be "always updated", since it has already been called.
Code runs in a sequence, so as soon as you call the function and it runs, and it returns, that variable is updated, so the next time the function is called(regardless of where the call is at), it should have the new value. One thing to note, the manual says that static variables are available in global functions and methods. That should mean you can't use them in any other type of code, so if you have such a variable in your step event, it makes sense that it wouldn't work(though I would think you would get an error).

I meant in the sense that it can pass image_index more than enough.
so it may not be suitable, but either way the form I quoted can be used in other things like this:
GML:
image_angle -= current_time * 0.03;
The code you actually had in the quote wasn't actually doing anything to the image_angle though, it was only using it. The example you put in THIS post will however change the variable. But if it isn't on an object, the variable may not be allowed to have that name(though I know that within structs it would be fine). Also note that if you are doing the above stuff running a massive loop instead of letting GM do the "normal stuff" then yeah, even if you were on an object, the image_angle would continue being outside the ranges of the images because you never let it do the automatic code that handles changing such variables.
.
 

samspade

Member
I have a tutorial on the static keyword. I think it's primary use is for struct made with constructors, but I have used it for real in functions as well now. Primarily when I have a function with a lot of repeated code but there's no reason for anything else to have access to that code. Rather than turning that code into a normal method variable (or global script function) I turn them into static method variables of that function. For example:

GML:
activate_button = function() {
    
    static remove_option = function() {
        var _page_options = gameplay_manager.my_data[$ "Pages"][$ my_parent_page][$ "Options"];
        for (var i = 0; i < array_length(_page_options); i += 1) {
            if (_page_options[i][$ "Option Text"] == my_text) {
                array_delete(_page_options, i, 1);
            }
        }   
    }
    
    /*
    more code that is basically a switch statement
    that will use remove_opition in certain cases
    so rather than repeat the above code in all relevant
    cases, I turned it into a static method variable
    belonging to this function.
    */
    
}
. As I understand it, it would technically create that variable on objects that are calling it as well, but it would refer to the same original variable, not creating variables on each object(if you understand the difference I'm getting at). I know for sure at the least, with structs, you can then access the variable after the fact elsewhere(which is why you can call the methods), but I'm not 100% if it does the exact same thing with objects, though logically it should.
I don't think that that is correct. Static variables belong to the function that creates them, not the object or struct that called or first called the function. If you think of functions as objects themselves, this makes a lot of sense.

 

FoxyOfJungle

Kazan Games
The code you actually had in the quote wasn't actually doing anything to the image_angle though, it was only using it. The example you put in THIS post will however change the variable. But if it isn't on an object, the variable may not be allowed to have that name(though I know that within structs it would be fine). Also note that if you are doing the above stuff running a massive loop instead of letting GM do the "normal stuff" then yeah, even if you were on an object, the image_angle would continue being outside the ranges of the images because you never let it do the automatic code that handles changing such variables.
Oh yes, I forgot that this variable has a lot of zeros, so it should work like this:

GML:
image_angle -= current_time * 0.00000003;


I don't know if using this can be a little slow, but avoid adding some variables.

Thanks for the help too. :)


I have a tutorial on the static keyword. I think it's primary use is for struct made with constructors, but I have used it for real in functions as well now. Primarily when I have a function with a lot of repeated code but there's no reason for anything else to have access to that code. Rather than turning that code into a normal method variable (or global script function) I turn them into static method variables of that function. For example:

GML:
activate_button = function() {
  
    static remove_option = function() {
        var _page_options = gameplay_manager.my_data[$ "Pages"][$ my_parent_page][$ "Options"];
        for (var i = 0; i < array_length(_page_options); i += 1) {
            if (_page_options[i][$ "Option Text"] == my_text) {
                array_delete(_page_options, i, 1);
            }
        } 
    }
  
    /*
    more code that is basically a switch statement
    that will use remove_opition in certain cases
    so rather than repeat the above code in all relevant
    cases, I turned it into a static method variable
    belonging to this function.
    */
  
}


I don't think that that is correct. Static variables belong to the function that creates them, not the object or struct that called or first called the function. If you think of functions as objects themselves, this makes a lot of sense.

I understand, and this accessor ($) that you used, how is it being useful?

I will check your video thanks!
 

kburkhart84

Firehammer Games
I don't think that that is correct. Static variables belong to the function that creates them, not the object or struct that called or first called the function. If you think of functions as objects themselves, this makes a lot of sense.
I know I'm correct, at the least with the second part of my statement. Otherwise, the methods created statically wouldn't be accessible later on outside the actual constructor itself(which would make them useless). I'm not saying that the static variable is accessible to the scope that called the constructor in a normal sense. I'm guessing rather that in the case of constructors they made a special circumstance where the variables created are accessible by the returned struct instead of only the constructor function itself. I don't know however if non-method static variables added in the constructor are also accessible by the struct, but at the least static method variables are(which is the whole point).
 

samspade

Member
I understand, and this accessor ($) that you used, how is it being useful?
It's the struct accessor. This project (from my gamejam game) imported a json file as structs and arrays. So if you are familiar with how JSON data or chaining accessors with maps and lists works, it's the same thing except I'm doing it with structs and arrays. And I'm using the accessor rather than the . notation because some of the variable names are invalid variable names (i.e. have spaces in them) so the only way to get to them is with the $.

Otherwise, the methods created statically wouldn't be accessible later on outside the actual constructor itself(which would make them useless).
Again, although I might misunderstand you, but I think that's incorrect. static methods essentially act (mostly) like unbound global functions and run on the scope of the variable that holds them. You can test this pretty easily in GM. You can see examples here. So all the struct holds for a method variable is an invisible pointer to the static method. This only really matters for structs made through a constructor function which as you pointed out obviously need a way to refer back to the static method somehow.
 

kburkhart84

Firehammer Games
Again, although I might misunderstand you, but I think that's incorrect. static methods essentially act (mostly) like unbound global functions and run on the scope of the variable that holds them. You can test this pretty easily in GM. You can see examples here. So all the struct holds for a method variable is an invisible pointer to the static method. This only really matters for structs made through a constructor function which as you pointed out obviously need a way to refer back to the static method somehow.
There it is. The struct retains a pointer to that static method, which is a type of variable. So if the variable was not a method, rather a number, wouldn't it mean that the strut instances retain the invisible pointer to that static variable?
 

samspade

Member
There it is. The struct retains a pointer to that static method, which is a type of variable. So if the variable was not a method, rather a number, wouldn't it mean that the strut instances retain the invisible pointer to that static variable?
I see what you're saying now and would agree for structs.
 

erayzesen

Member
What I understand is that for struct elements with a static prefix, space is allocated 1 time in memory. This is already explained in the documents.

But the other nice advantage is that it is not global, I have heard that when referring to global variables, search operations can tire game performance.

With this we have 2 advantages.
-Variable requests much faster.
-For each variable, 1 space is allocated in the memory.

I think we can say that static variables are common variables of all struct copies. So for example, we can understand how many structures we have created in this example;

GML:
function my_obj() constructor{
  static struct_counter=0;

  struct_counter++;
}
The struct_counter name variable is created 1 time and its value is increased by 1 each time we build our structure.

Is there any place I get it wrong? As far as I researched, I learned these.
 

FoxyOfJungle

Kazan Games
What I understand is that for struct elements with a static prefix, space is allocated 1 time in memory. This is already explained in the documents.

But the other nice advantage is that it is not global, I have heard that when referring to global variables, search operations can tire game performance.

With this we have 2 advantages.
-Variable requests much faster.
-For each variable, 1 space is allocated in the memory.

I think we can say that static variables are common variables of all struct copies. So for example, we can understand how many structures we have created in this example;

GML:
function my_obj() constructor{
  static struct_counter=0;

  struct_counter++;
}
The struct_counter name variable is created 1 time and its value is increased by 1 each time we build our structure.

Is there any place I get it wrong? As far as I researched, I learned these.
This is correct. The only thing I noticed is that this variable "struct_counter" for example, is "increased" every time the function is executed, it is a little difficult to explain, but for example: I have the following function:


GML:
function draw_sprite_alpha(sprite, x1, y1, x2, y2)
{
    static alpha = 1;
    
    if point_in_rectangle(mouse_x, mouse_y, x1, y1, x2, y2)
    {
        alpha -= 0.01;
    }
    
    draw_sprite_ext(sprite, 0, x, y, 1, 1, 0, c_white, alpha);
}

And I call it on an object:

GML:
draw_sprite_alpha(sprite_index, bbox_left, bbox_top, bbox_right, bbox_bottom);

All instances that have this function have their alpha changed, but I would like to have only one alpha of the instance in which I am hovering over. I don't want to use image_alpha or other built-in variables.


This is what happens:




This is what I want:



Detail: I could use return alpha, but I don't want to do that, because I want everything to be manipulated and drawn only within the function.

Any idea?
 

Nidoking

Member
If you want a different alpha per instance, you need to make it an instance variable. A static variable in the function exists only once. It can only have one value. If all instances depend on that single value, they'll all do the same thing.
 

FoxyOfJungle

Kazan Games
If you want a different alpha per instance, you need to make it an instance variable. A static variable in the function exists only once. It can only have one value. If all instances depend on that single value, they'll all do the same thing.
Yes, in fact, this is the biggest problem, because let's say I have a GUI system, I want to draw a button that is "animated" (changes alpha when I over the mouse for example), so I would have to create variables for it always in the create event if I want to modify the alpha , I dont't want to do that... because it will mess things up.
I suppose I have 20 objects that are buttons, it is complicated to create 20 alpha variables or an array just for that.

Put my UTIKI program in mind, it only uses one object to render the entire GUI, so I create the buttons on it myself, but I don't use instances for the buttons, it's all "manipulated by code and functions".

What do you think on that occasion?


EDIT:
I create buttons in real time in the draw event, that's why I don't want to use the create event to assign variables.
 
Last edited:

erayzesen

Member
This is correct. The only thing I noticed is that this variable "struct_counter" for example, is "increased" every time the function is executed, it is a little difficult to explain, but for example: I have the following function:


GML:
function draw_sprite_alpha(sprite, x1, y1, x2, y2)
{
    static alpha = 1;
   
    if point_in_rectangle(mouse_x, mouse_y, x1, y1, x2, y2)
    {
        alpha -= 0.01;
    }
   
    draw_sprite_ext(sprite, 0, x, y, 1, 1, 0, c_white, alpha);
}

And I call it on an object:

GML:
draw_sprite_alpha(sprite_index, bbox_left, bbox_top, bbox_right, bbox_bottom);

All instances that have this function have their alpha changed, but I would like to have only one alpha of the instance in which I am hovering over. I don't want to use image_alpha or other built-in variables.


This is what happens:




This is what I want:



Detail: I could use return alpha, but I don't want to do that, because I want everything to be manipulated and drawn only within the function.

Any idea?
I gues this is the same thing what I said. I readed the document about static keyword and its example like that;

GML:
counter = function()
 {
    static num = 0;
    return num++;
 }

The description of the document is "In the above example, the variable "num" is a static variable, and so will be defined as 0 the first time the function is called, but every time the function is called after that, the variable definition will be ignored."

So, your example is the same thing.

Code:
static alpha = 1;
This block of code will only run once during the game. It crosses this block on subsequent function calls, once it represents a variable reference given to that struct.

Code:
alpha -= 0.01;
This block will run whenever it is called. It doesn't matter how many times, that's the difference.

if we want a unique variable for each struct instance;

Code:
//no static statement
alpha=0;
We should write with this way.

For a common variable that we will create once for our struct examples, we need to use static. This can be a function variable, which is very useful and functional for functions. Apart from the document I have already shown, static statement is generally used for method functions.

But other than that, if we want only one variable specific to struct instances (like global but specific to that struct only) then we can also use it for standard variables.
 

FoxyOfJungle

Kazan Games
This block of code will only run once during the game. It crosses this block on subsequent function calls, once it represents a variable reference given to that struct.
Yes, this part that the variable is assigned to once I understood, the problem was that I thought I could use this technique so that this variable would serve as a "create event", only inside the function, but I was wrong.


static statement is generally used for method functions.
I think that’s why I shouldn’t use it in this case.


But other than that, if we want only one variable specific to struct instances (like global but specific to that struct only) then we can also use it for standard variables.
You are right to say that, I believe that the way I want to do it is a little impossible, I will see how I can do it.

A detail I forgot to mention: I create buttons in real time in the draw event, that's why I don't want to use the create event to assign variables.
 

Nidoking

Member
I suppose I have 20 objects that are buttons, it is complicated to create 20 alpha variables or an array just for that.
You have two options:
1) Have a separate variable for each button's alpha value. That can be an instance variable, or an array, or a map, or any other means of storing distinct values for each button. If you want 20 values, you need 20 places to put values.
2) Learn to live with every button using the same alpha value.

One variable can't hold more than one value, unless it's an array or other data structure. You must understand this. You cannot program without understanding this.
 

FoxyOfJungle

Kazan Games
I found a really cool way to do what I need, but I decided to use arrays and structs (In fact they are almost the same, but without built-in variables and events). It works like this:

In the Create Event I can "create all objects", but they are actually structs. So I can just use the variable that is contained in it in the draw event, this is good because that way I can make as many GUI objects as I want, as I didn't think about it before??

I can even create the empty struct and add the variables later, I did this to create text boxes, like this:

GML:
function textbox_create()
{
    /// @func textbox_create()

    // create gui object
    var textbox = function() constructor {}
    var L = new textbox();

    // add variables to the structs
    L.tb_text = "";
    L.tb_single_line = false;
    L.tb_read_only = false;
    L.tb_max_chars = 0;

    return L;
}
I could also insert variables into the struct, but I didn't want to.

That way, it will return the ID/struct that I just created and added the variables and I will be able to use it later:

GML:
function textbox_draw(index, x1, y1, x2, y2)
{
    var i = index;

    // draw box
    draw_rectangle(x1, y1, x2, y2, true);

    // draw text
    draw_text(x1, y1, i.tb_text);
}


Create Event:

GML:
// gui object window

// text boxes
gow_textbox[0] = textbox_create();
gow_textbox[1] = textbox_create();
gow_textbox[2] = textbox_create();

gow_textbox[3] = textbox_create();
gow_textbox[3].tb_text = "Add this text to the created textbox";
gow_textbox[3].tb_single_line = true;

Draw Event:

GML:
textbox_draw(gow_textbox[0], 10, 10, 200, 32);
.
.
.
This is just a model I just made, but that's the logic.


To destroy the objects I can do this:

GML:
for (var i = 0; i < array_length(gow_textbox); i+=1)
{
    delete gow_textbox[i];
}


In the case of my Utiki program, I am doing a little differently, but in the exported code, I will use this logic in some UI objects.

Interesting that I had already done this indirectly and had not realized it (because I was still learning to use structs), for example, I had created areas in the interface and I associated the scrollbar varriables with the structs that created the areas.
 
Last edited:
Top