OFFICIAL GML Updates in 2019 (Follow up AMA)

Status
Not open for further replies.

Coded Games

Member
The image was here but it's taken down: http://www.mcvuk.com/.image/c_limit,cs_srgb,fl_progressive,q_auto:good/MTU5MzQzMzA2Mjc3Mzk4MTEy/gms2_sequences_press.jpg

From what I remember new resource among sequences was named "Animation Curves". Not sure what those are, but I believe it might be a resource similar to path, where you can define interpolation (so put a value on y-axis, where x-axis is always between 0 and 1). So you can define lines/curves and then use them to animate (tweening), for speed/percentage of changes between frames, so they aren't linear between A and B values. They seems to be separate resource so can be used outside of sequences too, so you can read values of those interpolations using gml too.
Might be a nice addon, however for non-sequence usage this can be easily done now using paths, or even just scripts (for some most-known easings, like bounce, quad, sin, etc., there are just mathematical functions - you will find those on marketplace in all tweenings examples, or can even reuse code from known JS libaries - it's not hard to rewrite it to GML).

Another example of how complex 2.3 update will be.



Let's say, you have "script0", which code is:
Code:
return argument0 + 5
It will become:
Code:
function script0(argument0) { return argument0 + 5; }
// can be simplified manually to: function script0(a) { return a + 5; }
More complex:
Code:
var a = argument[0], b = (argument_count > 1) ? argument[1] : 0;
return a + b;
will become:
Code:
function script0(){
var a = (argument_count > 0) ? argument[0] : undefined, b = (argument_count > 1) ? argument[1] : 0;
return a + b;
}
Nothing to break here on project opening, while in my opinion change is massive :)
I do realize that functions and scripts are nearly identical in function. The thing that I worry about is if functions will be indexed. For example right now I can easily find every script with a specific name prefix by looping through script IDs and checking their names. Will this be possible in 2.3 with functions? Right now many aspects of my game are defined by scripts with specific names.
 

drandula

Member
Wouldn't it possible to have functions inside variables in 2.3, so I would assume you could also store functions inside array. Now you could loop through array and execute all those functions. With changing functions inside array you could change behavior, pass new functions around or switch places inside array. Maybe while looping through array you could change the array in a way which functions it holds.
Though currently this could also be achieved with script_execute and just tossing around script indexes?
 

gnysek

Member
how documentation will be handled..
for example functions internal to light-weighted objects will they have autocomplete?
I don't think they made a big improvement here, so same as you define variable in objects now and it's then coloured in all others (even if not yet defined for another object), same all variables from structs and function constructors will be colored everywhere (maybe only after a dot) as a regular variable, as it's hard to guess what function is assigned to which variable dynamically. Still, if you use "var" in same block of code, it will have different color then (yellow by default?). So no big changes here probably, except for new syntax (so keywords like struct/function/constructor/new).

It may be then good to take some naming conventions for some of variables/functions to not mix them, and differentiate in code conpletion. For example now I'm always using ALL_CAPS for globalvars to not mix them with local variables, and underscore for temporary variables (except iterators like i, j...), and all my enums always starts with e (like elevel, eweapon_type, ecutscene_action etc.).

I believe that even with so many changes we may still feel like home in GMS 2.3, even more than when moved from 1.x :) Sequences functions might be a complex ones and the only hard to learn in fact. Question is that with so flexible editord to define sequences we need to modify them at runtime at all :p
 

drandula

Member
I was thinking, could you animate your character (Spine-like) in Sequences, then save that and use for animating different object/instances
I had assumption it is mostly inside room editor, but if you could initiate it inside instance, it would be great. (codewise Translating and transforming sequence would allow that)
 

TsukaYuriko

🌠
Forum Staff
Moderator
Please keep in mind that this is the GML updates AMA follow-up topic, not the GML updates speculation topic - as per the community guidelines, please avoid speculation about potential future features and stick to known facts from official sources. :)
 

gnysek

Member
I think that its not animated in Room Editor, but it can be placed there on Asset Layer, and previewed there.
Depending on how many GML functions they prepared for Sequences - lot could be possible, but swapping resource should be first on list, cause copying sequence just to use another sprite of another character sounds bad. Especially if you want to change animation later cause you don't like it.

If we only get a date for open beta... I would get several days off in my work to test new stuff :D

Edit: @up, OK I'll try to keep topic.
 
Last edited:
Keeping on topic, one thing I didn't quite understand is the inheritance regarding lightweight objects.
Will inheritance features be a thing?!

I know we can probably do something like:

Code:
function A() {
   // constructor for 'A' object
   foo = 0;
   bar = 1;
   func = function() {
       // stuff
   }
};

function B() {
   // constructor for 'B' object, inherits 'A'
   A();
};

var a = new A(); // instance of 'A'
var b = new B(); // instance of 'B,' which inherits 'A'
as stated by @Nux

but will this give us the ability to do stuff like?
Code:
var c = is_ancestor(a, b);
or even call a base method from within a "derived" lightweight object?
Code:
function B() {
   // constructor for 'B' object, inherits 'A'
   A();

  func = function() {
    base.func(); // or in any other way....   A.func()
    // do stuff
  }
};
I put this into consideration because even if you change the light object after you create it (modify the instance), its "prototype" is static so the functions/variable definitions are there.
"A.func()" seems to be a valid approach, to me.
 

GMWolf

aka fel666
Keeping on topic, one thing I didn't quite understand is the inheritance regarding lightweight objects.
Will inheritance features be a thing?!

I know we can probably do something like:

Code:
function A() {
   // constructor for 'A' object
   foo = 0;
   bar = 1;
   func = function() {
       // stuff
   }
};

function B() {
   // constructor for 'B' object, inherits 'A'
   A();
};

var a = new A(); // instance of 'A'
var b = new B(); // instance of 'B,' which inherits 'A'
as stated by @Nux

but will this give us the ability to do stuff like?
Code:
var c = is_ancestor(a, b);
or even call a base method from within a "derived" lightweight object?
Code:
function B() {
   // constructor for 'B' object, inherits 'A'
   A();

  func = function() {
    base.func(); // or in any other way....   A.func()
    // do stuff
  }
};
I put this into consideration because even if you change the light object after you create it (modify the instance), its "prototype" is static so the functions/variable definitions are there.
"A.func()" seems to be a valid approach, to me.
You should be able to call a base method from a derived.

Just from those examples, it seems like "inheritance" isn't really inheritance, it's just calling the base constructor function in the derived constructor function.
Really a constructor function is just a function, and using new is a shortcut to create an empty LWO, and call that function on it. Nothing fancy.
LWO don't have type, just members (variables).

But, you could add type yourself by adding a type field.

Again, this is all based on the info that was.made public, and there may or may not be more to LWO in the actual release (I wouldn't know, I was offered to join the beta but I haven't heard back from them since :( )
 

gnysek

Member
There might be no constructors per se. That's why the called it structs created by function, or lightweight objects, not class. A constructor is a whole body of function like this, so when something will inherit, then "parent" code is executed first, then child. To override parent method, you probably need to do it this way:

Code:
function A(x) {
   a = x;
   b = function() { return 7;}
}

function B(x, y) : A(x) {
   // we already have a and b, if we want to rewrite b, probably the way is:
   c = b; // assign old function to another variable
   b = function() {
        var _parent = c(); // this should be a parent function in fact
        return _parent + 2;
   }
}
That's how I see it.
 
There might be no constructors per se. That's why the called it structs created by function, or lightweight objects, not class. A constructor is a whole body of function like this, so when something will inherit, then "parent" code is executed first, then child. To override parent method, you probably need to do it this way:

Code:
function A(x) {
   a = x;
   b = function() { return 7;}
}

function B(x, y) : A(x) {
   // we already have a and b, if we want to rewrite b, probably the way is:
   c = b; // assign old function to another variable
   b = function() {
        var _parent = c(); // this should be a parent function in fact
        return _parent + 2;
   }
}
That's how I see it.
The reassignment will pollute the Lightweight object won't it?!
After a few "inheritances" with some re-declarations we will have a LOT of variables.

I still think this would be the best approach

Code:
function A() {
   // constructor for 'A' object
   foo = 0;
   bar = 1;
   func = function() {
       // stuff
   }
};

function B() {
   // constructor for 'B' object, inherits 'A'
   A();
   func = function() {
      A.func();
      // NEW STUFF
   }
};
as I see it func is just a function scoped inside the LWO "namespace". LWO A (before being instantiated) is just a "prototype".
From an implementation point-of-view you cannot change A's definition (you cannot change a function you defined) so its contents could all be sort-of "static",
therefor allowing for us to do things like A.func();. In relation to scope... the scope could not be A so the scope should be the scope from where the call was made

If you do this in the create event of an object:
Code:
A.func();
then the scope should be THAT instance of THAT object.

this is the way I see things but as this is a topic to ask about GML2019 new features I would like to hear something from the developers (staff).
 

gnysek

Member
I'm wondering, wouldn't it be a mess:

Code:
a = struct {
    v: 5,
    s: "text"
}

function b() { // alternative: b = function() {
    v = 5;
    s = "text";
}

c = new b();
// we can get values by using: a.v, a.s, c.v, c.s, but not by b.s and b.v
if we call both "structs" just because we can access variables from them, while have different syntax. It looks weird.
 
I'm wondering, wouldn't it be a mess:

Code:
a = struct {
    v: 5,
    s: "text"
}

function b() { // alternative: b = function() {
    v = 5;
    s = "text";
}

c = new b();
// we can get values by using: a.v, a.s, c.v, c.s, but not by b.s and b.v
if we call both "structs" just because we can access variables from them, while have different syntax. It looks weird.
I'm guessing probably, the function is just for functions and struct is just for "LWO".

so your code:
Code:
function b() { // alternative: b = function() {
    v = 5;
    s = "text";
}
b();
is actually a function that sets variables.
you cannot call new nor access its variables.

and
Code:
struct b() { // no alternative  {
    v: 5,
    s: "text"
}
var c = new b();
is a struct which you can call new on.
you can access it's variables using c.v or c.s;

probably we could have a struct variant
Code:
var a = struct {
   a: 2,
   b: "hello"
}
much like the anonymous functions, to declare temporary structs.

This actually make sense. The GML post we got from them is from early 2019.
Right now the naming/use-case could have changed while maintaining features.
(I guess that's what happened).

And probably that's also easier for the parser and IDE to work with autocomplete and error messages that way, since there are no types...

For a new user it would be strange if the keyword function could be a function or a LWO... (even though that's how it "kinda" works with JS)


NOTE: btw, is there a way to do inline code here in the forums like in discord `my code` ?!
 
Last edited:

DukeSoft

Member
Any plans for smaller datatypes like int32 or char?
We can make value objects with LWO's. So probably; yes we can make very slow and cumbersome datatypes :p

So basically, no. I do think the engine is moving more and more towards (real) native datatypes though..
 

GMWolf

aka fel666
So basically, no. I do think the engine is moving more and more towards (real) native datatypes though..
At least with what we know now, it just doesn't make sense.
We wouldn't see any real world benefits from smaller types.
The bloat around each variable, the non contiguous memory access, it just won't ever matter if you use an int32 rather than an int64
 

FrostyCat

Member
Not my specialty in particular, but how much more utility would there be in native extension development if extension arguments supported the smaller types?
 

vdweller

Member
Well a smaller datatype would be useful in large grids eg obstacle maps. Yes you can use a buffer and yes there is bit packing but all those workarounds begin to feel more and more like a hack. Plus as gmwolf said speed-wise it will all still go down the toilet.
 
One way I see this happening is by adding a new resource type much like "shaders" where we could code at lower level
The data transference back and forth to GML would still be typeless so a int8 would need to be wrapped into a "GMLVariable" (type that could represent any value) to be passable to the GML side, but the heavy lift would be done at a lower level. I don't see GML having a change that big to allow types, any time soon
 
Last edited:

DukeSoft

Member
We wouldn't see any real world benefits from smaller types.
I was leaning more towards strict types like integer, float, string, array, null, perhaps boolean and the like. Typehinting in objects as well as interfaces / extendability and visibility would be awesome. But we're a long road from that.

The fact that I said I think we're moving there are based on the is_ functions :) https://docs2.yoyogames.com/source/_build/3_scripting/3_gml_overview/checking_data_types/index.html
 

FrostyCat

Member
I was leaning more towards strict types like integer, float, string, array, null, perhaps boolean and the like. Typehinting in objects as well as interfaces / extendability and visibility would be awesome. But we're a long road from that.

The fact that I said I think we're moving there are based on the is_ functions :) https://docs2.yoyogames.com/source/_build/3_scripting/3_gml_overview/checking_data_types/index.html
I too think strict typing will help with hinting and catching problems at compile time, that has never been my objection (in fact that isn't even my quote, it's GMWolf's). But it's perfectly possible to talk about strict typing without talking about non-Boolean sub-32-bit types such as bytes and shorts, which outside of integration with native libraries I also see relatively little utility.
 

DukeSoft

Member
I too think strict typing will help with hinting and catching problems at compile time, that has never been my objection (in fact that isn't even my quote, it's GMWolf's). But it's perfectly possible to talk about strict typing without talking about non-Boolean sub-32-bit types such as bytes and shorts, which outside of integration with native libraries I also see relatively little utility.
Oops, I knew something was off when I clicked "insert quotes" and I saw a different text, sorry *facepalm*

I do agree with you. Main reason I'd like stricter datatypes would be the datastructures - or at least get them replaced with pointers :p

@xDGameStudios this might be worth looking into: https://github.com/kraifpatrik/yyc-overwrite :+)
 

FrostyCat

Member
Strict typing was explicitly stated as a will-do-but-not-now in the AMA. It's one thing to think about what it would be like if it was sanctioned, but let's stop asking for it to be in GMS 2.3 already.
Has strong typing been considered? Are there any future plans for it?

ANSWER - Yes. Not currently.
* When chained accessors were last discussed, the promotion of data structures to true types was stated as a prerequisite. Has this been done?

ANSWER - We have found a way to do it without that as a prerequisite, this is good as that is a lot of work that would just delay these features.
* Are any existing resource types (e.g. data structures, buffers, etc.) retrofitted to support the OOP way of initialization?

ANSWER - Not in our first iteration, we will be revamping these in the future.
 

kraifpatrik

Member
GML is a simple scripting language for writing game logic and in terms of speed it's nowhere near native code. If performance is your concern, I would recommend you to write dynamic libraries in C++ or whatever.
 
Last week I asked for some additional information (state of the work, we might call it) regarding the new features.
I guess the development team (or the Boss xD) didn't authorized any information leak.
 
QUESTION:
Will it be possible to define variables inside structs dynamically?

for example:
Code:
var s = {
   my_var: 10000;
}

s.my_other_var = 300;
or even
Code:
var s = { }

s.var1 = 100;
s.var2 = 300;
Thank you so much for the time.
 

FrostyCat

Member
QUESTION:
Will it be possible to define variables inside structs dynamically?

for example:
Code:
var s = {
   my_var: 10000;
}

s.my_other_var = 300;
or even
Code:
var s = { }

s.var1 = 100;
s.var2 = 300;
Thank you so much for the time.
This has already been answered in the AMA:
The AMA answers article said:
* What happens if you set a previously inexistent field for a lightweight object after it has been instantiated? Example:
Code:
function Vec2(_x, _y) {
  x = _x;
  y = _y;
}
var foo = new Vec2(0, 0);
foo.z = 0; // What will the runner do about this?
ANSWER - This will just be added to the lightweight object in the same way as it currently works in GML, the lightweight object is not sealed.
 

vdweller

Member
GML is a simple scripting language for writing game logic and in terms of speed it's nowhere near native code. If performance is your concern, I would recommend you to write dynamic libraries in C++ or whatever.
The problem with dlls is the overhead of calling them in certain cases. While calling a dll function once to calculate a path in the c++side is very fast, constantly fetching values can be cumbersome. For example, in the pathfinding example, you can have the path nodes defined as structs in c++. However in gamemaker there are cases where you need to access them outside of pathfinding multiple times, like when creating a higher-hierarchy region. Calling node_get_x() 256 times just to fetch a x value is the dll approach bottleneck.

Things get much worse if you consider making your game moddable. Essentially a Lua script calls a gml script that calls a C++ function...just to get a single value.

All this could perhaps be avoided if we were simply officially allowed to inline C++ code in gml.
 

GMWolf

aka fel666
sorry didn't see it there, my bad!
Regarding room transitions... will structs be like Instances (and get destroyed) or behave like arrays and be permanent?
I'm guessing the later but just wanted to be sure!
Garbage collected like arrays.
So as long as you have a reference to one it will stay. As soon as nothing has a reference to it it wil be destroyed.
(Should deal with circular references too, hopefully).
 
The problem with dlls is the overhead of calling them in certain cases. While calling a dll function once to calculate a path in the c++side is very fast, constantly fetching values can be cumbersome. For example, in the pathfinding example, you can have the path nodes defined as structs in c++. However in gamemaker there are cases where you need to access them outside of pathfinding multiple times, like when creating a higher-hierarchy region. Calling node_get_x() 256 times just to fetch a x value is the dll approach bottleneck
Depending on how far you're willing to go you could create a buffer in GM that you supply to the C++ side as the whole memory that it has available and then you have access to the raw state of the DLL by poking at the buffer from GML. Just an idea I had, didn't actually test it although I'm intrigued about it's usability so I might test it soon-ish
 
Depending on how far you're willing to go you could create a buffer in GM that you supply to the C++ side as the whole memory that it has available and then you have access to the raw state of the DLL by poking at the buffer from GML. Just an idea I had, didn't actually test it although I'm intrigued about it's usability so I might test it soon-ish
Your idea made my smile a little.. because it's actually awesome!!
 

vdweller

Member
Depending on how far you're willing to go you could create a buffer in GM that you supply to the C++ side as the whole memory that it has available and then you have access to the raw state of the DLL by poking at the buffer from GML. Just an idea I had, didn't actually test it although I'm intrigued about it's usability so I might test it soon-ish
I have also thought about it. It's...not a very good idea for a large project.
 

kraifpatrik

Member
One of the problems with the buffer is that it's not that fast to read on the GML side. Something like a "native_array", with specific C++ data type and its pointer available, would be much handier to have.

@vdweller Seems like your project has very specific needs, but we don't know much about it. Did you make a thread here, discussing the path finding problem? Or do you care to make one? We may come up with something practical if we put our heads together.
 

vdweller

Member
One of the problems with the buffer is that it's not that fast to read on the GML side. Something like a "native_array", with specific C++ data type and its pointer available, would be much handier to have.

@vdweller Seems like your project has very specific needs, but we don't know much about it. Did you make a thread here, discussing the path finding problem? Or do you care to make one? We may come up with something practical if we put our heads together.
Yes, there is a thread I made about pathfinding, but of course a simulation game poses other challenges as well. But this is not about this specific project. Since this is an AMA I am asking these questions in a more general manner to see if there are any plans for GML to go that way in the future.
 
Last edited:

gnysek

Member
GML update opens many new ways of doing things internally in runners, as they made a big rewrite for them to work. So after things will stabilise (at current pace I believe 2.3 won't become stable before end of March for sure), they for sure gonna work into other improvements. Like changing all resources to their own "type" (like int, string, etc.) instead of giving them integer ID (so you can't write "sprite0 + 5" anymore), then garbage collector for resources and ds structures (arrays have it already, structs should have them too, but who knows).

So, the first batches of changes is already answered in this AMA. What will be next - time will show, give them time to first roll out this little late update first. Even if they says "no" for something now, who know what will change in a year or two - some changes that are comes now, were also "not planned" for so many years.
 

Henry00

Member
GML QUESTION:

Will there be destructors for the delete operator?
Code:
function MyAlgorithm()
{
    // constructor
    data = ds_map_create();

    // destructor
    delete = function() {
        ds_map_delete(data);
    }
}

var alg = new MyAlgorithm();
delete alg;
Sure you can also write:
Code:
var alg = new MyAlgorithm();
alg.delete();
delete alg;
Or wait for the garbage collector. But I personally prefer to just clean up and let the GC do as little work as possible.
 

GMWolf

aka fel666
Or make your own struct that wraps maps and provides these methods... ;)
That's the worst of both worlds!
Not only do you have to manage memory yourself, but you still leave an object around for the GC to collect!

Manually deleting steucts would mean finding all references to the struct and setting them to undefined. That would be just as slow if not slower than automatic GC!

Unless dangling pointers are a thing YYG are comfortable having GM users deal with (I'm sure they are not)
 

FrostyCat

Member
Unless dangling pointers are a thing YYG are comfortable having GM users deal with (I'm sure they are not)
Dangling handles are a thing that YoYo had been making GM users deal with since the day it took over. That's not much of a step up from dangling pointers.
 

gnysek

Member
In fact the above is a very good question. And from what I saw already, there's no destructors (a constructor is just a whole struct/function body). There's a "delete" operator, but seems that when using ds_xxx structures inside structs we gonna create massive memory leaks, cause there's no difference between:
Code:
var s = {a: ds_map_create();}
and:
Code:
var s = {a: 1}
And there's no way to unset ds_xxx created that way, except of remembering to remove it before delete:
Code:
ds_map_delete(s.a);
delete s;
And if there's garbage collector for structs (which I believe is) - then it can be really misleading.
 

GMWolf

aka fel666
Dangling handles are a thing that YoYo had been making GM users deal with since the day it took over. That's not much of a step up from dangling pointers.
I guess that's true.
We'll have to see what YYG came up with but I suspect it's all GC based.
 
Status
Not open for further replies.
Top