GMS 2.3+ TIP: Calling a method of an object by name like script_execute

BoB3k

Member
I love the new structs and methods -- OO in GMS2 at last.

I just thought I would share a trick i just figured out and people can say, cool neat, or tell me that there's a better way to do it--

I have a scripting engine inside of my game engine for custom actions from within game. I began to use script_execute a lot to just call actual GMS2 scripts by name.
But now with the new methods and the ability to create object methods just like real OO, I still wanted to call them by name.

So say you have a GM Object Guy with

do_stuff = function( thing ){ ... }

defined in its Create event.
You can call it with guy.do_stuff( "a_stuff" ); from within code and it's all oo and fun.

Now, how do you call that by name like you can with script_execute?

Like this:
Code:
// guy is an instance of Guy
var method_name = "do_stuff";
var the_method = variable_instance_get( guy, method_name );
with( guy ){  the_method( "a_stuff" );  }

Cool.
 

angelwire

Member
Awesome tip! One quick note is that the with() statement doesn't seem to be necessary (although it does make the code more clear about what's happening).
 

Nidoking

Member
I believe the with is necessary if the function isn't declared as a method. I don't know why you wouldn't declare it as a method, but that's not how it's shown in the post.
 

Roldy

Member
I believe the with is necessary if the function isn't declared as a method. I don't know why you wouldn't declare it as a method, but that's not how it's shown in the post.
He said he was using it so his in-game scripting system can access instance methods. So he needs to find methods at runtime that may or may not actually exist. Seems flexible and workable using the limited reflection present in GML. What he is doing makes sense and the 'with' seems necessary.
 

angelwire

Member
I've tried it both ways "function do_stuff()" and "do_stuff = function()" and they both work fine without the with{}. Without the with{} you still only access the single instance of "guy" and and everything seems fine. Admittedly I don't know as much about functions and methods as I should, so maybe I'm just not understanding correctly.

Here's the code that works without the "with()"

object1
GML:
var guy = instance_find(object2, 1);
var show_name_method = variable_instance_get(guy, "showName");
var change_name_method = variable_instance_get(guy, "changeName");
change_name_method("JJ");
show_name_method();
object2
Code:
function showName()
{
    show_message(my_name); //Will show the instance's "my_name"
}
function changeName(_string)
{
    my_name = _string;
}

/*also works with:
showName = function()
{
    show_message(my_name);
}
changeName = function(_string)
{
    my_name = _string;
}*/
I have no idea if things are supposed to behave like this or not, but I don't see any downside to not using the "with" other than it being a little bit harder to see what's happening.
 

Nidoking

Member
I would expect the "self" in the function to be the calling instance if you're not using with. You're running change_name_method before you run show_name_method, which totally doesn't make it clear which instance you're affecting, and your statement of "the instance" clears that up not a tiny bit. I also have no idea why you're using strings and reflection when you can just store the dang function in the variable.
 

Roldy

Member
I would expect the "self" in the function to be the calling instance if you're not using with. You're running change_name_method before you run show_name_method, which totally doesn't make it clear which instance you're affecting, and your statement of "the instance" clears that up not a tiny bit. I also have no idea why you're using strings and reflection when you can just store the dang function in the variable.
A method will include the owning instance ID. A named function will not. So yeah, if he knows for a fact he is looking up methods then the 'with' is pointless. However, if calling non methods then the 'with' is needed. The reason I assume he is using strings and 'reflection' because he is reading text files or command inputs at runtime.

GML:
// Create event of MyObject


function namedFunction() {
    show_degug_message("Not a Method " + string(id));
}

foo = function () {
    show_debug_message("Method " + string(id)););
}

bar = namedFunction;  // Bar points to a named function

// In the step event of MyObject


show_debug_message(is_method(foo));  // displays 1 (true)
show_debug_message(is_method(bar));  // displays 0 (false)

var _someInstance = instance_find(MyObject, 1); // Find some instance of myObject

var _call = variable_instance_get(_someInstance, "foo");
show_debug_message(is_method(_call)); // displays 1 (true) foo is a method

// We can't use dot notation to invoke _call()
// But we can invoke call the following ways that produce the same output

_call(); // Will display 'Is Method ' + _someInstance.id

with(_someInstance) { _call();} // Will display 'Is Method ' + _someInstance.id

with(id) {_call();} // Will display 'Is Method ' + _someInstance.id

// Now we change _call to point to a named function

_call = variable_instance_get(_someInstance, "bar");
show_debug_message(is_method(_call)); // displays 0 (false) bar is not a method

// We can't use dot notation to invoke _call()
// But we can invoke call the following ways that produce the DIFFERENT output
// Since _call no longer points to a method with attached instance id, the id becomes 'self' and the 'with'  block is important

_call(); // Will display 'Not a Method' + id

with(_someInstance) { _call();}  // Will display 'Not a Method' + _someInstance.id

with(id) {_call();}  // Will display 'Not a Method' + id

I don't really like the whole anonymous method thing, but it is what it is
 

BoB3k

Member
I use the with because I am declaring the functions in the object create events as variables so that they can be called in code like object methods --

guy1.do_stuff("stuff_for_guy_1");
guy2.do_stuff("stuff_for_guy_2");


So, when you need to call it by name, you have to be in the wanted calling instances scope.
 
Top