OFFICIAL GMS 2.3.0 BETA ANNOUNCEMENT

Status
Not open for further replies.

XanthorXIII

Member
To be honest I'm still trying to wrap my head around the proper use of structs/functions and constructors for this. For example in C++ or C# for any of the objects we create we can just create a constructor for that class, pass in our values through that and return the object created to the variable we assign it. In GameMaker it doesn't really appear you have that since if you define a traditional object, you can't define it's own create function that's part of the object. Unless that's where structs now come into play and instead of using traditional objects, we're only using a few of those while the rest are being created as light weight objects. For example, you have a Master Controller in the scene and in the create step you define all the structs for the objects you want in that scene with their functions and the initialize those and then call their update function in the step event etc.... Don't take this as any criticism, I'm just looking for a better structural understanding of what games would look like in 2.3.
 
so with GM2.3 structs, how would one go about instances(heavy object) of a struct?
I used structs to define "Units" say
GML:
tower() contructor
{
hp = 100
name = tower
x  =  0
y = 0
}
this essentially has all the info for that unit. For it to concretely (collisions) exist in the world I would need to associate it with a game instance.
I do it by creating a struct instance [ new tower() ] , giving it to a object instance [ instance_create(x,y, obj_tower) ] and then transfer all variables values from the struct to the instance. (a script heavily lifted from FrostyCat's struct_merge() but copying to instance instead)
This works nicely: it creates game instances that automatically get all the variables from the unit structs i make.
But I'm realising now that it creates a duplication of all variables: the struct and the instance copy and they are not synchronised. This makes sense but it feels inefficient. In theory, I only need to change the instance's variables, since that is what acts and exists in the world. But it feels off to keep an out of date associated struct. Also, when/if saving comes in line, the struct it would be usefull to keep the struct updated (easier to save).

Anyone encountered this design situation? How do you handle it?
  • I can see how I could not even copy the struct variables and simply operate on the struct variables. ie: if collision mystruct.hp -= 10 but stat would be a mess and all instance code would always need "mystruct" prefix
  • Not use instances, do all code from struct. but that loses all the benefits from built in instances.(events)
  • Let variables desynchronise, synchronise them manually when needed.
I know this is more of a design question, but I've seen a lot of discussion on good ways to use the structs and I like to think the situation I've described has been encountered here?
Not gonna lie, I thought I knew what you were asking and pseudo-coded this up but now I'm tired and re-reading your post and not sure this is at all relevant lol but I'll post it anyway for anyone trying to wrap their head around structs here's my brainstorm on how I think an RTS/tower defense or whatever game would work...the idea being that you can have all your GUI/shop related stuff inside the struct so you can use the same variables (or that stuff could be inside a nested struct like I did here):
GML:
function Unit(_hp, _strength, _price, _info, _icon) constructor {
    hp       = _hp;
    strength = _strength;
    price    = _price;
    info     = _info;
    icon     = new Icon(_icon);//see Icon stuff below

    //in-game stuff
    static step = function() {
        on_attacked(obj_player);
        attack(obj_player);
    }

    static on_attacked = function(_attacker) {
        if (place_meeting(other.id, _attacker)) {hp -= _attacker.strength;}
    }

    static attack = function(_target) {//some generic default Unit melee attack
        if (point_distance(_target) < 100) {_target.hp -= strength;}
    }

    static draw = function() {//draw in-game
        draw_self();
        draw_text(x, y - 100, hp);
    }
}


function Icon(_icon) constructor {//GUI/shop related stuff
    icon = _icon;

    static step = function() {
        if (mouse_hover && mb_left_clicked) {purchase();}
    }

    static purchase = function() {
        if (obj_player.gold >= other.price) {
            obj_player.gold -= other.price;
            //add to game/inventory or make available or whatever
        }
    }

    static is_available = function() {
        return (obj_player.gold >= other.price ? c_white : c_red);
    }

    static draw_icon = function(_x, _y) {//draw icon with stats in your GUI/shop
        draw_sprite(_x, _y, icon, is_available);
        draw_text(_x, _y, other.price, other.hp, other.strength, other.info, etc);
    }
}


function Tower() : Unit(100, 10, 1000, "This is a tower", spr_icon_tower) constructor {
    static attack = function(_target) {//overrides the Unit attack method
        instance_create(x, y, obj_projectile);
    }
}


function Turret() : Unit(200, 50, 5000, "This is a turret", spr_icon_turret) constructor {
    attack_timer = 100;//only Turrets will have this

    static attack = function(_target) {//overrides the Unit attack method
        seek_target(_target);

        if (!attack_timer--) {
            attack_timer = 100;
            instance_create(x, y, obj_projectile);
        }
    }

    static seek_target = function(_target) {//only Turrets will have this
        image_angle = point_direction(_target);
    }
}


//obj_tower (traditional GMS object resource)
//Create Event
o = new Tower();
//Step Event
o.step();
//Draw Event
o.draw();

//obj_turret (traditional GMS object resource)
//Create Event
o = new Turret();
//Step Event
o.step();
//Draw Event
o.draw();

//obj_hud (traditional GMS object resource)
//Step Event
with (obj_units) {o.icon.step(_x, _y);}
//Draw Event
for (var _x = 0; _x < 320; _x + 32) {
    for (var _y = 0; _y < 320; _y + 32) {
        with (obj_units) {o.icon.draw_icon(_x, _y);}
    }
}
Something like that I think is how this is supposed to work, excuse my sloppy tired pseudo-code lol...with the extending you don't have to do like turret.unit.hp or like armored_turret.turret.unit.hp, you can use the extending to be able to just do turret.hp or shorten it to o.hp @DragoniteSpam put up a good tutorial on extending structs here for anyone who finds it confusing:
I'm personally using Github Desktop...seems to be fine for me. It does a local repo along with the free cloud one. Even better, I have it in a dropbox
I found source control incredibly confusing and seemingly overly-complicated but I've been using Github Desktop and followed this tutorial which was nice and straight-forward:
Now I understand the overall concept (I'm working solo so I just have Master, Dev, and then whatever feature I'm working on) and I'm starting to like using it and being forced to save notes about what I worked on. I also have mine in a Dropbox dir for extra backup protection (and Dropbox saves previous versions of files too). The only downside is that when I forget to close GMS2.3 before I switch branches or return to Master etc it blows my GMS2.3 up and I have to restart it lol I recommend Github Desktop for people who are new to source control, the console command stuff looks daunting for newbies.
After the questions on whether to use constructors or objects, I thought I'd share my flowchart for choosing which to use (yea I know, lol) Any thoughts?

Should I use a constructor, a singleton struct pattern, or an object to represent an entity?
Does the entity need complex collisions, the GM physics system, or any other build-in object functions that are extremely difficult to code yourself?
Yes:
--Use an object.
No:
--Does the entity need a step or draw function/event at any point?
----Yes:
------Does the entity 'belong to' or 'exist for' any other entity? (e.g. an inventory or map 'belongs to' a player entity)
--------Yes:
----------Use a constructor. (its step or draw events can be called in its parent object)
--------No:
----------Use an object.
----No:
------Should there only be a single instance of the entity?
--------Yes:
----------Use a singleton struct pattern.
--------No:
----------Use a constructor.
This is great! I'm getting a pretty good grasp on when to use what but it's cool to see it laid out in a nice flow-chart like this for easy reference.
There isn't enough time in the GMS 2.3 timeframe to retrofit any of the existing ID-based resources, including data structures. That is why I wrote this.
Can't recommend this enough! Been using it a bunch and it's been awesome. No juggling ds_creates() and destroys() and the function names are shorter than the DS ones. I can't imagine ever going back to DS lists!
Unless that's where structs now come into play and instead of using traditional objects, we're only using a few of those while the rest are being created as light weight objects. For example, you have a Master Controller in the scene and in the create step you define all the structs for the objects you want in that scene with their functions and the initialize those and then call their update function in the step event etc...
It seems like this is the case. Like in my example above, I can't really see a reason to have an obj_tower and obj_turret...there could just be an obj_units that goes through all the units and calls their step/draw events, or even just an obj_game that calls like level.draw(); with (units) {unit.draw();} fx.draw(); gui.draw(); etc with pretty much all of that just being structs. I'm sure there's still reasons to use the normal objects but it feels like you could make an entire game in one traditional object now lol I have NO idea if that's good or bad coding practice!

Also for newbies like myself, I found this guy's videos really useful for thinking in terms of how and why to use structs. It's not GML so you have to translate the code in your head, but conceptually it cleared some things up for me and he explains his thought process each step of the way which can be more useful than just looking at finished tutorial code:


The Frogger series, Space Invaders, and Asteroids were the ones I found the most useful. His code isn't great by any means lol (although the Frogger one has a refactoring vid where he cleans up some of the code and explains his thought process) but he's great at walking you through the concepts...plus he looks like he's having the time of his life programming!
 
Last edited:

gnysek

Member
When the stable release comes out, will there still be a separate download? I still want to cling on to 2.2 in case there are still bugs or conversion problems.
Every version is a separate download. I think that you're asking about separate installation (like beta is now), and the answer is - NO. If you want to stick with 2.2, then you need not to update.

Also, my solution for conversion problems - GMS 2.3 on import asks where to save new project (so by default tries to not override original), and then if something is lost, you still have old data in place (scripts can be opened in Notepad, sprites can be copied). So, nothing will be lost even if it will require some work to move it back.
However, they are making their best to have NO CONVERSION problems (that's why stable is so late now), and if you think there might be one, please download beta now (it works separate from 2.2), check your project and submit it in case of issues, so they can fix it.
 

Joh

Member
Every version is a separate download. I think that you're asking about separate installation (like beta is now), and the answer is - NO. If you want to stick with 2.2, then you need not to update.

Also, my solution for conversion problems - GMS 2.3 on import asks where to save new project (so by default tries to not override original), and then if something is lost, you still have old data in place (scripts can be opened in Notepad, sprites can be copied). So, nothing will be lost even if it will require some work to move it back.
However, they are making their best to have NO CONVERSION problems (that's why stable is so late now), and if you think there might be one, please download beta now (it works separate from 2.2), check your project and submit it in case of issues, so they can fix it.
Is the no seperate installation a fact? I mean I know thats for "stable" when things hopefully don't break too much, but this is quite a big shift. its like GMS3 (or a "2.5" so to speak).
I'm juggling between the two installations because I have a bunch of projects in too deep in <2.3, I feel updating won't be worth the effort. and I'm experimenting with 2.3, which is clearly better, but not trustworthy... yet. but even when it is, i don't think i'd want to upgrade most <2.3 to it... but maybe I'm underestimating the conversion process.

Not gonna lie, I thought I knew what you were asking and pseudo-coded this up but now I'm tired and re-reading your post and not sure this is at all relevant lol but I'll post it anyway for anyone trying to wrap their head around structs here's my brainstorm on how I think an RTS/tower defense or whatever game would work...the idea being that you can have all your GUI/shop related stuff inside the struct so you can use the same variables (or that stuff could be inside a nested struct like I did here):
GML:
function Unit(_hp, _strength, _price, _info, _icon) constructor {
hp = _hp;
strength = _strength;
price = _price;
info = _info;
icon = new Icon(_icon);//see Icon stuff below

//in-game stuff
static step = function() {
on_attacked(obj_player);
attack(obj_player);
}

static on_attacked = function(_attacker) {
if (place_meeting(other.id, _attacker)) {hp -= _attacker.strength;}
}

static attack = function(_target) {//some generic default Unit melee attack
if (point_distance(_target) < 100) {_target.hp -= strength;}
}

static draw = function() {//draw in-game
draw_self();
draw_text(x, y - 100, hp);
}
}


function Icon(_icon) constructor {//GUI/shop related stuff
icon = _icon;

static step = function() {
if (mouse_hover && mb_left_clicked) {purchase();}
}

static purchase = function() {
if (obj_player.gold >= other.price) {
obj_player.gold -= other.price;
//add to game/inventory or make available or whatever
}
}

static is_available = function() {
return (obj_player.gold >= other.price ? c_white : c_red);
}

static draw_icon = function(_x, _y) {//draw icon with stats in your GUI/shop
draw_sprite(_x, _y, icon, is_available);
draw_text(_x, _y, other.price, other.hp, other.strength, other.info, etc);
}
}


function Tower() : Unit(100, 10, 1000, "This is a tower", spr_icon_tower) constructor {
static attack = function(_target) {//overrides the Unit attack method
instance_create(x, y, obj_projectile);
}
}


function Turret() : Unit(200, 50, 5000, "This is a turret", spr_icon_turret) constructor {
attack_timer = 100;//only Turrets will have this

static attack = function(_target) {//overrides the Unit attack method
seek_target(_target);

if (!attack_timer--) {
attack_timer = 100;
instance_create(x, y, obj_projectile);
}
}

static seek_target = function(_target) {//only Turrets will have this
image_angle = point_direction(_target);
}
}


//obj_tower (traditional GMS object resource)
//Create Event
o = new Tower();
//Step Event
o.step();
//Draw Event
o.draw();

//obj_turret (traditional GMS object resource)
//Create Event
o = new Turret();
//Step Event
o.step();
//Draw Event
o.draw();

//obj_hud (traditional GMS object resource)
//Step Event
with (obj_units) {o.icon.step(_x, _y);}
//Draw Event
for (var _x = 0; _x < 320; _x + 32) {
for (var _y = 0; _y < 320; _y + 32) {
with (obj_units) {o.icon.draw_icon(_x, _y);}
}
}
Something like that I think is how this is supposed to work, excuse my sloppy tired pseudo-code lol...with the extending you don't have to do like turret.unit.hp or like armored_turret.turret.unit.hp, you can use the extending to be able to just do turret.hp or shorten it to o.hp @DragoniteSpam put up a good tutorial on extending structs here for anyone who finds it confusing:
Thanks for that and yes, that is what i was talking about. Even RTS/tower defense was quite accurate to what I'm doing. but perhaps the example gave that away. Anyways, your code is pretty much how I went about it.
I didnt think of a grouping "step" or "draw" function though, thats a good idea.

though I only use 1 object
GML:
if valid_placement && mouse_check_button_pressed(mb_left)
    {
        var _new = create_tower_select.create_new();
        _new.instanciate(_x, _y)
        create_tower_select = -1
    }
by clicking on a menu element which is a struct of the tower, it goes in create_tower_select. once I click somewhere, it will create a copy of itself(the starting struct is a template).
then that copy will "instanciate" itself, which is the opposite of what you do I guess. The struct creates an game_maker_generic tower object, and will populate it with the specific struct's variables.
GML:
static instanciate = function(_x, _y){
        var _inst = instance_create_layer(_x,_y,o_c.layer, o_tower);
        x = _x
        y = _y
        instance = _inst
//give the object instance the variables of the struct
        _inst.construct_struct = self
        _inst.sprite_index = sprite
        _inst.image_index  =  image
        _inst.image_speed  =  0
        with(_inst)
        {
            sc_tower_setup(construct_struct)
//A blind copying of all structs variables into the instance
//As well as instance specific variables creations
        }
//transfer the methods too
        _inst.tower_aim = tower_aim
        _inst.tower_shoot = tower_shoot
        return _inst
    }
like this I can create an instance with all the variables from the struct, which can interact with the "instance" world. Now I have duplicate variables since the instance and struct have the save values at creation but eventually desynchronise. I was leveraging the struct variables since I could then use them in the object.
GML:
//#Object tower step event
//these are all variables/methods generated/acquired from the struct
tower_aim()
firewait -= delta_second
if distance_to_object(target)<range
{
    if firewait<=0
        tower_shoot()
    
}
but I do think a pseudo-step like you've shown is smarter. Plus as had been previously suggested, i've been moving away from the instance variables and using more the variables of the struct itself. end goal would be to only use struct variables and only use instances as a container (for collision and such)

Thanks!
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Is the no seperate installation a fact?
Yes. Once it goes into stable then you will be offered an update on your existing 2.2.5 installation. You (currently) won't have to update, but as time goes on you will as the runtimes for things like Android and HTML5 get bug fixes and API changes. Note that you can always not update the stand-alone version, and then use the updated Steam version, so effectively you'll have two separate installs: 2.3 on Steam and 2.2.5 on stand-alone. ;)
 

gnysek

Member
Note that you can always not update the stand-alone version, and then use the updated Steam version, so effectively you'll have two separate installs: 2.3 on Steam and 2.2.5 on stand-alone.
This phrase should be in release notes :p I'm for sure not gonna use 2.2.5 anymore (in fact I'm using 2.3 already on separate branch of my git repo), but for some people that's a clever hint.
 

Selek

Member
Newbish guy here, experimenting with 2.3. I'm trying to develop a card game, which will (I hope) feature more than 100 cards. Right now each card is a struct with 14 variables, such as cost, name, index, etc. Of those 14 variables, only about 4 or 5 are unique on any particular card. Most of the fields are "false" or zero -- e.g., a few cards function as "equipment," but most don't. I imagine I'll end up with many more fields before I'm done; I've only done about 20 cards so far.

So I'm wondering: would it be useful for me to make a Card "class" using a constructor? Then make each new card with the new keyword? I'm thinking one benefit might be that a card's unique properties would stand out more, since it would inherit the default properties common to most cards, but "override" a few with its own unique values, like child instances of object parents. Another benefit might be that as I inevitably come up with new card variables, it would be easier to adjust one constructor function than all 100 cards. Is this why people use constructors? Or am I just better off defining each field for each card?

Also, if one does use a constructor, is it best practice to do so in its own script, or in the create step of an object?

This may seem like an obvious question to you guys, but I'm pretty new to this, so I'd appreciate any guidance. Many thanks in advance.
 

kburkhart84

Firehammer Games
Well, a constructor function returns a struct, so in both instances you are still using structs.

In my opinion, if the majority of your cards are the same 15 values, with maybe 5 that are different, then you could just use a default constructor to initialize the common values, and then in the code just after that change those few varied values.

I also saw some code somewhere, but I can't remember where(it might have been one of @FrostyCat 's things). The idea was that you would put a struct literal as an argument to a constructor/function, and it would overwrite or add all the values to the original struct.
 

FrostyCat

Redemption Seeker
I also saw some code somewhere, but I can't remember where(it might have been one of @FrostyCat 's things). The idea was that you would put a struct literal as an argument to a constructor/function, and it would overwrite or add all the values to the original struct.
That was indeed one of my posts. The idea is to set all the defaults in a local struct, then use a script to copy over keys from the passed struct, then act normally with the merged content.
 

kburkhart84

Firehammer Games
Do you mean something like what's mentioned here?
That was indeed one of my posts. The idea is to set all the defaults in a local struct, then use a script to copy over keys from the passed struct, then act normally with the merged content.
@FrostyCat 's is the one that I remember. However, @Rui Rosário 's version seems quite similar, and includes checking if it is a constructor first, meaning it is more compatible with incorrect usage but less efficient than FrostyCat's assuming it is used correctly.
 

erayzesen

Member
I'm following your experiences. It sounds like we will able to use some great programming patterns with GMS2. Is it true? I use gms2 for 4 months but I had a problem designing codes without oop most of the time. I came from oop based languages. And the good news, it will change in next version. yes, It's one of the rare coincidences that I'm lucky. :cool:
 
D

Deleted member 45063

Guest
@FrostyCat 's is the one that I remember. However, @Rui Rosário 's version seems quite similar, and includes checking if it is a constructor first, meaning it is more compatible with incorrect usage but less efficient than FrostyCat's assuming it is used correctly.
Yes, my version was meant for a different use case, if you notice it also changes the self reference in the copied methods to point to the new structure since my version is not meant to use a struct for default values but more of extending a struct with "interfaces" / "aspects" of other structures (and it tries to work also with just references to the constructors as you pointed out)
 

gnysek

Member
If there's static in struct, and you override it in one of struct instances, it became local for that one (only). Maybe that's a good way to have same value in most of instances, and different in few?

GML:
function card(v) constructor {
    value = v;
    static cost = 100;
}


card1 = new card(5);
card2 = new card(10);
card3 = new card(15);
card3.cost = 200;

//card1 and card2 cost = 100 and is shared in memory, card 3 cost = 200
 
R

RGiskardR

Guest
I'm playing around with structs, it seems I'm messing some basic knowledge here. Searched the manual and the forums but didn't find an explanation.

Why I can't assign a struct to global variable using the constructor?

Here is the struct:
GML:
function Repository(filename) constructor {   
    _filename = filename;
    _data = -1;    
    load_data = function() {
        var _buffer = buffer_load(_filename);
        var _reader = buffer_read(_buffer, buffer_string);
        buffer_delete(_buffer);
        var _dictionary = json_decode(_reader);
        self._data = _dictionary;
    }    
    get_element = function(_key) {       
        var _element = ds_map_find_value(_data, _key);       
        if (is_undefined(_element)) {
            show_debug_message("key: " + string(_key) + " not found in " + _filename);    
           // todo >>> prevent boom!                   
        }
        return _element;
    }
};
GML:
var local_repo = new Repository(consumables_filename);
instance_repo = new Repository(consumables_filename);
global.consumables_repository = new Repository(consumables_filename); // no variable and no error thrown
Could you explain me why this is not working?
 

kburkhart84

Firehammer Games
I'm playing around with structs, it seems I'm messing some basic knowledge here. Searched the manual and the forums but didn't find an explanation.

Why I can't assign a struct to global variable using the constructor?

Here is the struct:
GML:
function Repository(filename) constructor {  
    _filename = filename;
    _data = -1;   
    load_data = function() {
        var _buffer = buffer_load(_filename);
        var _reader = buffer_read(_buffer, buffer_string);
        buffer_delete(_buffer);
        var _dictionary = json_decode(_reader);
        self._data = _dictionary;
    }   
    get_element = function(_key) {      
        var _element = ds_map_find_value(_data, _key);      
        if (is_undefined(_element)) {
            show_debug_message("key: " + string(_key) + " not found in " + _filename);   
           // todo >>> prevent boom!                  
        }
        return _element;
    }
};
GML:
var local_repo = new Repository(consumables_filename);
instance_repo = new Repository(consumables_filename);
global.consumables_repository = new Repository(consumables_filename); // no variable and no error thrown
Could you explain me why this is not working?
It might be a bug. Are you getting an error when you try to access that global variable afterwards?
 
I use gms2 for 4 months but I had a problem designing codes without oop most of the time
This is something worth knowing but it might become a bit obsolete with the new GMS 2.3. You can use game objects as objects like you do in other oop languages. It's definitely not the same but that's what I have been doing with GMS 2.2 and older. Using structs in 2.3 should make this a lot easier and more efficient.
 

esharo

Member
I want to keep working with the old version
The new version has too many problems and is unstable
However, in order to build to IOS, Xcode has been updated, which makes the old version of GMS unable to build .
This forced me to use the new version again. There were various problems in the new version and could not be built. As a result, my game on ios could not be updated for half a year.

None of the included files can be accessed now on ios.
How to solve this problem,
I can't use font_add() and file_text_open_read() to use file that in "included files"
 

erayzesen

Member
This is something worth knowing but it might become a bit obsolete with the new GMS 2.3. You can use game objects as objects like you do in other oop languages. It's definitely not the same but that's what I have been doing with GMS 2.2 and older. Using structs in 2.3 should make this a lot easier and more efficient.
It doesn't matter. I want to write my independent objects and object methods with gms. I guess we will make it with new constructor functions. Is it true?
 
I want to keep working with the old version
The new version has too many problems and is unstable
However, in order to build to IOS, Xcode has been updated, which makes the old version of GMS unable to build .
This forced me to use the new version again. There were various problems in the new version and could not be built. As a result, my game on ios could not be updated for half a year.

None of the included files can be accessed now on ios.
How to solve this problem,
I can't use font_add() and file_text_open_read() to use file that in "included files"
GMS 2.2.5 can still export compatible iOS builds as far as I know. Just stick to Xcode 11.
 
It might just be me. HTML5 linear interpolation settings in the game options does nothing. I had to use gpu_set_tex_filter(false); to set the linear interpolation off.

I also noticed in HTML5 that image_index is always an integer so if the sprite fps is less than the game fps or if you increment the image_index by magnitudes less than 1, the image_index value will round down and not increase.

Am I the only one who is experiencing this or has somebody run to the same problem?
 

gnysek

Member
I remember there were some bugs with image_index in HTML5 even in GMS 1.x, so there might be still some issue (as you said, mainly because of rounding).

Btw. 3 weeks passed since last beta release, and still no update... previous one were more often. I hope it's because they want the next one to be "final beta".
 

Zhanghua

Member
I remember there were some bugs with image_index in HTML5 even in GMS 1.x, so there might be still some issue (as you said, mainly because of rounding).

Btw. 3 weeks passed since last beta release, and still no update... previous one were more often. I hope it's because they want the next one to be "final beta".
No, if you urge to get the final version, you would get the stuff like last final, the 2nd last final and the lastest final version, etc.

Just kidding but it's the tough life
 
Last edited by a moderator:
Random question about static: does anyone have an example of when you WOULDN'T want to use static for the functions in a constructor? For instance:
GML:
function Enemy(_x, _y) constructor {
    x = _x;
    y = _y;

    static attack = function(_target) {
        //attack code here
    }
}
...when would you NOT want to have "static" in front of "attack" there? What would the benefit or trade-off be? Even when doing something complex like extending from the Enemy constructor and overwriting functions etc I can't see any reason to not make them all static inside a constructor.
 

FrostyCat

Redemption Seeker
Random question about static: does anyone have an example of when you WOULDN'T want to use static for the functions in a constructor?
When that function is a callback that can vary between instances of that constructor, you absolutely would not want to use static. This is something that I've already explained in an earlier reply:
In GML 2020, using static means you want every instance built from the constructor to have the exact same implementation of the marked function. There are times when you want individual instances to have varying implementations, where you would NOT use static.

For example, this tick-based delayer class:
GML:
function Delayer(_action, _ticks) constructor {
    action = _action;
    ticks = _ticks;

    static tick = function() {
        if (--ticks == 0) action();
    };
}
Create:
GML:
nextRoomAction = new Delayer(function() {
    room_goto_next();
}, room_speed*2);
Step:
GML:
nextRoomAction.tick();
Every instance of this Delayer class ticks the same way, so tick uses static. But the action being performed varies between instances, so it is NOT static.
 

gnysek

Member
Yeah, with proper declaration (as above) all methods in struct can be always static. And if you need to really have something different in one case, just write:
GML:
nextRoomAction.tick = function() { /* code here*/ };
and that one instance will have it's own method without changing constructor.

So, generally it seems that even if you have a case where static is not the choice you need, you can still change code in a way to have static and maybe save memory.
 
GML:
function Delayer(_action, _ticks) constructor {
    action = _action;
Ohhh okay I see what you mean...I was focused on learning when TO use static before so I didn't even process the opposite part of your example lol and so if you wanted a default action for some reason, this would be the equivalent I assume:
GML:
function Delayer(_ticks) constructor {
    ticks = _ticks;

    action = function() {
        //some default action
    }

    static tick = function() {
        if (--ticks == 0) action();
    };
}

nextRoomAction        = new Delayer(room_speed * 2);
nextRoomAction.action = function() {room_goto_next();}

otherAction = new Delayer(room_speed);//uses the default action
That all makes sense now, thanks! I can't get over how powerful these features are. The Jsons stuff you did is like cheating, my code went from this huge complicated mess of converting stuff from arrays to a mess of ds_maps and ds_lists...to literally just effortless code like:
GML:
level = {
    name: "Dungeon of Doom",
    chests: [
        {x:  10, y:  10, item: obj_sword},
        {x: 400, y: 400, item: obj_shield},
    ],
    enemies: [
        {
            obj: obj_goblin,
            x: 100,
            y: 100,
            path: {
                speed: 10,
                loop: true,
                points: [
                    {x:    0, y:    0},
                    {x:  200, y:    0},
                    {x:    0, y:  200},
                    {x: -200, y:    0}
                    {x:    0, y: -200}
                ]
            }
        },
        {
            obj: obj_skeleton,
            x: 300,
            y: 200,
            path: {
                speed: 5,
                loop: false,
                points: [
                    {x:   0, y: 0},
                    {x: 500, y: 0}
                ]
            }
        }
    ]
}

jsons_save(filename_level1, level);
...just making or modifying a struct in whatever way and saving/loading it in one line. The number of times I've been rewriting my <2.3 code and gone "and now I have to add...oh wait, that's it! wtf, I don't even need that mess from that other script" or been able to turn huge spread out messes into just a few lines contained in a nice clean constructor or struct has me constantly in awe of how much easier it is to code now!

(edit: actually here's my whole function for saving options, maybe it'll help someone else get an idea of one way to approach things lol...I'm finding I use constructors instead of structs even for single things because I like being able to automatically run some code by default like an init() or load() at the bottom):
GML:
enum DIFF {EASY, MED, HARD}
enum GFX  {LOW,  MED, HIGH}

function Options() constructor {
    path  = DATA_DIR + "options.json";

    static defaults = function() {
        settings = {
            version: 1,
            graphics: {
                detail: GFX.HIGH,
                aa: true,
                fullscreen: false
            },
            audio: {
                bgm_vol: 10,
                sfx_vol: 10
            },
            gameplay: {
                difficulty: DIFF.EASY
            }
        }
    }

    static save = function() {
        jsons_save(path, settings);
    }

    static load = function() {
        if (file_exists(path)) {
            settings = jsons_load(path);
        } else {
            defaults();
            save();
        }
    }

    load();
}

//obj_main Create Event
options = new Options();

//then from anywhere in my game I can use macros for
//obj_main.options and obj_main.options.settings and easily
//do like SETTINGS.difficulty = DIFF.HARD and OPTIONS.save()
So, generally it seems that even if you have a case where static is not the choice you need, you can still change code in a way to have static and maybe save memory.
Ya the thing confusing me was when you print out a struct, the struct doesn't show static methods but shows the non-static ones in it, so I thought maybe there's some kind of nuance where if you're extending a constructor and overwriting a method originally defined in the one you're extending from, maybe you'd want to have one or both of those not be static for some kind of memory or cache or garbage collecting reasons or something technical way beyond my understanding lol

But I get it now, if action is static then changing action will change it for everyone...it makes sense when I remember to remind myself that the stuff defined in constructors & structs are just normal variables and follow the same rules normal variables do. I think the : with a , VS = with a ; still throws me off sometimes lol
 
Last edited:

Ednei

Member
I want to keep working with the old version
The new version has too many problems and is unstable
However, in order to build to IOS, Xcode has been updated, which makes the old version of GMS unable to build .
This forced me to use the new version again. There were various problems in the new version and could not be built. As a result, my game on ios could not be updated for half a year.

None of the included files can be accessed now on ios.
How to solve this problem,
I can't use font_add() and file_text_open_read() to use file that in "included files"
I noticed that the instability of GM 2.3 often appears when a Windows update is running in the background. This already happened in previous versions. I usually experience momentary loss of sound, or failures in ds_lists.
 

FrostyCat

Redemption Seeker
I've downloaded beta v13 and done some quick runs on my existing work. UWP YYC is thankfully fixed now, but HTML5 has taken a dozen steps backwards again because now it seems to have issues with passing functions and methods around (which my GMS 2.3 libraries depend on quite exquisitely).

Among the exports I have build access to, the following exports seem ready for stable release:
  • Windows VM+YYC
  • UWP VM
  • Mac VM (once Catalina is fixed)
  • Ubuntu VM+YYC (once Mac-to-Ubuntu is fixed)
  • Android VM+YYC
What has concerned me throughout the closed and open betas, and still does, is how inconsistent YoYo's cross-platform testing appears to have been. Windows VM is basically the gold standard this whole time with all features working perfectly, and then every other export lags far behind it, especially HTML5. The other exports in the list above have improved, but only after other beta testers and I poked hundreds of holes in each, some pretty fundamental and should have failed a comprehensive self-test. And what about the exports that I didn't have access to during the beta and didn't get significant reporting activity from others (Mac YYC, iOS VM+YYC, console)? Would their implementation of GML 2020 be just as flimsy as the closed beta because they haven't been field-tested this whole time?

I've seen mentions of "GMDeath" and "YYGTest" on Mantis before, so it seems the "comprehensive self-test" I mentioned does exist. If it's an automated suite, then ideally it would have been touch-and-go this whole time. So why does it seem so often that only Windows VM has been validated against it? Will it improve going forward?
 

jobjorgos

Member
Is GameMaker Studio 2.3 still open beta? since I can not download it anywhere on yoyogames, or do I have to wait now until 2.3 launch is final?
 

gnysek

Member
I just came with an idea for destructors, which won't be added.

What if we could get weak references (weak reference is a reference, which garbage collector doesn't take into account as link to resource) ? That should allow to create for example a ds_map, which tracks all ds_xxx created by our struct, and perodically checks if weak reference to struct exists. If not - then it deletes ds_xxx structure too. Downside is that we still need separate tracking for every ds type, and that probably needs to be global, but this could help prevent memory leaks.
 

Zhanghua

Member
Does anybody meet the Struct Dependence issue of YYC compile?

My struct A is in Script SrcrA, And B is in Script ScrB and inherits the A.
GML:
//In Script file ScrA
function A() constructor {}
GML:
//In Script file ScrB
function B():A() constructor {}
When using yyc to compile, the ide just give the error "bailing details below"

And I have to paste the Code of ScrB to the script file A and pass the compile.

GML:
//In Script file ScrA
function A() constructor {}
function B():A() constructor {}
 
Status
Not open for further replies.
Top