Imagine if the create event could take arguments

andev

Member
Perhaps this is the wrong place for this but I would love to start a discussion, even if it never gets implemented.

But yeah, imagine if you could do instance_create(obj, arg0, arg1) etc. And then the create event could look like:

x = argument0;
y = argument1;

Or even if the arguments started after x,y as optional arguments (much like ds_list_add can have more than entry).

Currently the solution seems to be to just use a Create_Object( ) script which works fine, but the instance_create() always seems like boilerplate to me, especially for objects that never even use x/y.

Please note that this functionality would be hidden / optional.

Thoughts?
 

Evanski

Raccoon Lord
Forum Staff
Moderator
you could make it yourself, heres a mock up

just throw it in a script then call the script with arguments
GML:
/// @func Func_instance_create( obj_id, x, y, hp, level)
/// @param {real} obj_id resource name
/// @param {real} x
/// @param {real} y
/// @param {real} hp hp of the created object
/// @param {real} level level of the created object

var _object = argument[0];
var _x = argument[1];
var _y = argument[2];
var _hp = argument[3];
var _level = argument[4];

var obj = instance_create_depth(_x, _y, 100, _object);

with(obj)
{
    hp = _hp;
    level = _level;
}
 
Last edited:

Alice

Darts addict
Forum Staff
Moderator
@EvanSki That's the sort of thing I would do, except I would also add "return id" at the end of "with" block, so that I would receive instance ID for further processing.

It's still imperfect compared to object having arguments passed to constructor - you cannot process the arguments in the Create Event code. If those are objects placed in room editor, you can use object variables to overcome that limitation, though (they're set before Create Event code is called).

Another alternative I consider - once the new GML becomes available - is declaring an "init" function that would be called after all the necessary variables are set in a custom "create object" script. I'd need to try this solution in practice to see how well it applies to real-world use cases, though.
 

Yal

🐧 *penguin noises*
GMC Elder
But yeah, imagine if you could do instance_create(obj, arg0, arg1) etc. And then the create event could look like:

x = argument0;
y = argument1;

Or even if the arguments started after x,y as optional arguments (much like ds_list_add can have more than entry).

Currently the solution seems to be to just use a Create_Object( ) script which works fine, but the instance_create() always seems like boilerplate to me, especially for objects that never even use x/y.

Please note that this functionality would be hidden / optional.
Instance_create* already takes x and y as arguments, so your example is moot.

Not sure I see what's so boilerplate about instance_create()... every call has the bare minimum you need: what to spawn, and where. Boilerplate is when you need to repeat a lot of stuff that don't help with the abstraction, like needing to create not just a QueueReader but also a QueueReaderDataStorage each time you want to read from a queue, and then manually tie them together, instead of the QueueReader just taking care of its own bookkeeping.

Structs (which will be in 2.3) are basically objects but pure data, so you could probably put things that never use coordinates in as structs instead.
 

drandula

Member
The best way to get what you want would be something like the following.
Code:
with(instance_create(....))
{
    x = this;
    y = that;
    othervar = morestuff;
}
Wouldn't this work too for example
GML:
var new_inst = instance_create_depth(0,0,0, obj_debugger)
    new_inst.debug_mode = true;
 
H

Homunculus

Guest
You mean, like a real constructor? Nah, nobody uses those...

Seriously, I generally use the same approach as EvanSki (+ Misu suggestion) for instances that do require some parameters to be set at creation, but it's far from ideal due to the create event being run before the variables are set, which often includes event_inherited as well (calling the parent constructor is another thing I miss).
There is also no way to enforce the usage of this "script constructor" instead of the regular instance create. It's a minor thing, but makes everything a bit more error prone.
 

kburkhart84

Firehammer Games
Wouldn't this work too for example
GML:
var new_inst = instance_create_depth(0,0,0, obj_debugger)
    new_inst.debug_mode = true;
Yup, the "." dot syntax works pretty much the same as the with() construct in this instance. But if you wanted to do something more complicated, like several variables or even call scripts in the context of the newly created instance, the with() statement works better, or at the least looks a little cleaner than a bunch of "someinstance.xxx" calls, at least in my opinion. And you can still use the "other" keyword within the with statement to access the original instance variables.
 

andev

Member
Code:
with(instance_create(....))
{
    x = this;
    y = that;
    othervar = morestuff;
}
I think you're right, that is the closest syntax. But I remember seeing some performance comparison post somewhere saying to avoid "with" at all costs (perhaps that has been fixed now?)

just throw it in a script then call the script with arguments
Yeah I mentioned this in my post, it's probably the cleanest solution available

@EvanSkiI would also add "return id" at the end of "with" block, so that I would receive instance ID for further processing
You can return from a with statement? 😯 I had no idea. Alternatively you could just store the instance_create result in a variable before running the with.

You mean, like a real constructor? Nah, nobody uses those...
What 😂

There is also no way to enforce the usage of this "script constructor" instead of the regular instance create. It's a minor thing, but makes everything a bit more error prone.
This is a really good point I like this. Team projects will need to establish that the script is always used.

Instance_create* already takes x and y as arguments, so your example is moot.
It's meant to be a passthrough example if left blank

Not sure I see what's so boilerplate about instance_create()... every call has the bare minimum you need: what to spawn, and where
Some objects don't even need x/y (data storage objects).

Wouldn't this work too for example
GML:
var new_inst = instance_create_depth(0,0,0, obj_debugger)
new_inst.debug_mode = true;
Yes, but then you must remember all the initialising variables, and also code (as the create event has already happened by the time you're passing in your custom arguments).

And you can still use the "other" keyword within the with statement to access the original instance variables.
👍 I forgot about this
 

Joe Ellis

Member
If you wanted to set certain variables before the create event is run, and the create event will deal with these variables, you could do something like:

GML:
///instance_create_custom(x, y, object, var1, var2, var3)

var ins = instance_create(argument0, argument1, argument2);

ins.var1 = argument3
ins.var2 = argument4
ins.var3 = argument5

with ins
{event_perform(ev_create)}

///create_event

if !variable_instance_exists(id, "var1")
{exit}

spd = var1
dir = var2
grav = var3
That's not really a great example of how you could use this, but I couldn't think of a real situation where it'd be useful, I might edit this if I think of one later
 

kburkhart84

Firehammer Games
I think you're right, that is the closest syntax. But I remember seeing some performance comparison post somewhere saying to avoid "with" at all costs (perhaps that has been fixed now?)
I don't remember there being a bad performance hit or anything suggesting you should avoid using with. In fact, I would think that having things work with the dot syntax would be worse since you are "dereferencing the pointer" or whatever they call it in GMS multiple times, but in the with version you only do it once.
You can return from a with statement? 😯 I had no idea. Alternatively you could just store the instance_create result in a variable before running the with.
I'm assuming it was just meant to store the result in a variable and use that both to run the with, and to return from the function, so it would behave similar to the internal instance creation functions.
👍 I forgot about this
Indeed, this is among the many things I've seen in GMS that makes it so much simpler to use compared to other game engines/frameworks.
 

8BitWarrior

Member
I don't remember there being a bad performance hit or anything suggesting you should avoid using with. In fact, I would think that having things work with the dot syntax would be worse since you are "dereferencing the pointer" or whatever they call it in GMS multiple times, but in the with version you only do it once.
I have found that VM, with() is fine. It's also "ok" with YYC. But it absolutely cripples JS/HTML5 performance compared to dot syntax. If you're building for PC/Console, it's likely a moot issue. But... if you're someone developing HTML5 mobile games, it's worth considering.

Edit:
As a real-world example, the performance of TweenGMS improved 5-8x when I moved away from using the with() statement.
In the link, check out example 17/23 called "Swirl". On PC, when using with(), I would get about 5000-6000 tweens before dipping below 60 fps. After removing with(), I get about 30,000.
 
Last edited:

kburkhart84

Firehammer Games
There it is then...I dabbled a bit with HTML5, but I have only really used the VM, as I've only tinkered in bits with the compiler so I never did any real testing. Its good to know. So basically, on all platforms except for the VM, with is slower than dot syntax, at the least in a general sense.

The next step would be to test things like how many things can you put into a with statement before it becomes faster than having several dot syntax statements.
 

8BitWarrior

Member
The next step would be to test things like how many things can you put into a with statement before it becomes faster than having several dot syntax statements.
Yeah. I have done that in the past, but it might be time for me to test this again, as my thoughts could be outdated. With the new GML changes coming, it would be best to retest all of this anyway!
But yeah, I wouldn't be overly concerned about its performance hit unless you are focused on critical "low-level" code wrapped in heavy loops. At a high-level, it can be super convenient and totally worth a slight performance hit.

Where you might want to reconsider the with() statement is when you have something like...

GML:
for (xx=0; xx<1000; ++xx)
{
    for(yy=0; yy<1000; ++y)
    {
        with(someObject)
        {
            // ... do stuff here
            // Bonus potential for bad...
            with(anotherObject)
            {
                // ... do more stuff
            }
        }
    }
}
But again, if you're not targeting HTML5, it's probably fine.
 

Freddy Jones

Your Main Detective
So you literally just want arguments to be available during the create event? That's it?

There's basically nothing that stops you from putting the argument array and argument count and whatever argument meta information you want in the global scope before your create event happens. All you'd really need to do is create your wrapper around instance create and make sure that information is just available before instance_create gets called.

something like:

GML:
/// make_instance(obj, ...)
global.param = argument         
global.param_count = argument_count
return instance_create_layer(argument1, argument2, "", argument0)
Then create event would simply be
GML:
// create event
x = param[1]
y = param[2]
z = param[3]

Personally, I don't think there's anything wrong with just creating a constructor function per object when I need it. And if you really wanted a creation function that only cared about what object it needed then a new function suffices.



edit:
I guess it's been a while, but I forget that the create event doesn't happen on instance_create - right? And even if it didn't, it's probably fine to just add the whole 'param' array to the created element (or just manually invoke the create event like someone else suggested)

edit 2:
so something like this should be valid if the create event + globals wasn't an option:


GML:
/// make_instance(obj, ...)
with instance_create_layer(argument1, argument2, "", argument0) begin
    param = argument           
    param_count = argument_count
    return self
end
Edit 3?:


I suppose one other reason you'd want to do this is if you wanted to immediately use the returned object after it operated on the parameters. That would certainly call for you to invoke the create event it it doesn't happen instantly already. This technically is far from being how instances were originally meant to be used though.. so it would probably be better to wait on light weight objects for things like that.

I guess if you didn't mind your create event happening twice but also have the ability to have a parameterized event you could stick with the instance level parameter solution like I suggested and call it immediately.

And speaking of parametrizing your events in general.... You could probably make another even more general function which calls events with parameters :p
 
Last edited:

andev

Member
There's basically nothing that stops you from putting the argument array and argument count and whatever argument meta information you want in the global scope before your create event happens.
I mean that's one work around but aside from global variables being slow (from what I remember?) it opens up a whole host of opportunity for bugs with residue data left over from previous constructor calls.

I guess if you didn't mind your create event happening twice
This is not ideal, as it is unnecessary overhead. I guess you could ensure that it only runs once by encompassing the whole create event with an if statement that checks for a variable you set to true when you actually want to run it, but it seems a bit contrived at this point.

but I forget that the create event doesn't happen on instance_create - right?
instance_create immediately calls the create event of the instance. And x,y values passed in can be overwritten by the create event.
 
Last edited:

Freddy Jones

Your Main Detective
I mean that's one work around but aside from global variables being slow (from what I remember?) it opens up a whole host of opportunity for bugs with residue data left over from previous constructor calls.


This is not ideal, as it is unnecessary overhead. I guess you could ensure that it only runs once by encompassing the whole create event with an if statement that checks for a variable you set to true when you actually want to run it, but it seems a bit contrived at this point.


instance_create immediately calls the create event of the instance. And x,y values passed in can be overwritten by the create event.
So basically what I said works? It's really not that slow either. Also residue data from previous calls? If you look at the code there is no residual data, and you just forward the argument count anyways.


X Y values can easily be overwritten by the create event if you wanted them too. Again having the arguments available globally and defined before the event is perfectly sound.

If you really wanted your global values to be cleaned up after the create event you'd just clear it immediately after the instance it's created.

It's such a drop dead easy general solution - gml isn't a dynamic scripting language for nothing and they have been optimizing it since day one.

I doubt this would ever be a bottleneck and nothing stops you from testing either since it's literally the most simple solution lol



Also you quoted me questioning the create event being invoked twice but that was really me just thinking to myself. Still a lot of what I just gave you stands for exactly what you wanted with little to no boilerplate
 

Freddy Jones

Your Main Detective
@andev Actually - I wonder about something now that I think about it. As I was reading this tech-blog: https://www.yoyogames.com/blog/454/ghosted-parent-events-and-object-variables

It actually appears that there exists a functionality, at least kind of, that allows you to references variables set on an object before the create event is called . . . and apparently such values are able to be defined on a per-instance scenario. So interestingly enough, I don't think we have any options to do this with GML - but there seems to be some form of argument instantiation via "variable definitions". It would be cool to have the same feature through some sort of GML function.
 
Top