• Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

GMS 2.3+ How to use methods? What's "self" in methods? How to get the method's "parent instance" from within the method?

Erik Leppen

Member
I'm trying to wrap my head around the new 2.3 functionalities and end up getting errors I have no clue about. Can someone please tell me how to actually use these new functions in order to gain any advantage over the old way of doing things?

What i want to do is couple any scripts that "belong" to an object, as methods of that object. However, when I try to define them, and then call them, the "self" variable doesn't point to the instance that owns the method. How do I get access to that instance?

In GM2.2 I had an object "nodeobject" and a function "node_unlink(node)". Now I want to have a node object with a method "unlink()" in each instance, so that instead of node_unlink(mynode), I can say mynode.unlink(). This sounds reasonable, right? So here's what I try to do to accomplish that.

The "node" script with the methods:
GML:
function node_create_gm23 () {
    var newnode = instance_create_depth(0, 0, 0, nodeobject);
    newnode.track = noone;
    newnode.index = undefined;

    newnode.unlink = function () {
        if instance_exists(self.track) { //this gives the error
            self.track = noone;
            self.index = undefined;
        }
    }

    return newnode;
}
Another script where I define some function that happens to need the unlinking functionality of nodes:
GML:
function nodes_unlink_all(array_of_nodes) {
    var nodelen = array_length(array_of_nodes);
    for (var nodeind = 0; nodeind < nodelen; nodeind += 1) {
        var nod = array_of_nodes[nodeind]; //this is a nodeobject instance - I checked
        nod.unlink();
    }
}
Event code in an event of an object called "game":
GML:
var no1 = node_create_gm23();
var no2 = node_create_gm23();
nodes_unlink_all([no1, no2]);
The error I'm getting is that the variable "game.track" is not defined. Er, no, I don't want game.track, I want node.track, that's why I said "self.track" in the definition of the unlink() method. So, how should I get the instance that owns the method from within the method, no matter where it's called? I found a method_get_self, but that requires the method as its argument, but I don't think I can reference the method from within its own code... (or can I? If so, how?)

I tried replacing self with nothing (i.e. just using "track"); doesn't work. I also tried replacing self with newnode; also doesn't work.

How should I go about this? Or am I doing this all wrong? Is there a good tutorial about how to use this whole "method" stuff? Remember, the reason I want this is organisational. I had a working game in 2.2, but I want to benefit from the new stuff.

Any insights welcome :)
 

Posh Indie

Member
The "self" keyword is tied to the calling instance, so I assume "game" is the name of an object you have created (So the error would be correct, as in the current state that would be the calling instance).

To create a Lightweight Object (which sounds like what you are trying to do), you will need to wrap those methods in something like:

GML:
function Node() constructor {
    ...
}
This allows you to create instances of the lightweight objects using the "new" keyword (Which will help with your use the the "self" keyword. More on that shortly).

I am not sure if the methods would need to change here, but I usually use the format:

GML:
method_name = function() {
    ...
}
within that lightweight object.

Then within the "game" object (If my assumption was correct) you would want to create an instance of your node object, something like:

GML:
node = new Node();
"self" should now reference the Node lightweight object instance, instead of the explicit object resource housing the code.

Now, after typing all of this out I could have even misundertood you. If that is the case, apologies!
 
Last edited:

FrostyCat

Member
In this case, you need to either use method() to bind it to newnode instead of the instance running the node_create_gm23:
GML:
    newnode.unlink = method(newnode, function () {
        ....
    });
Or use with from the start so that the binding target defaults to the new node:
GML:
    with (newnode) {
        track = noone;
        index = undefined;
        unlink = function() {
            ...
        }
    }
Method ownership binding is one of the less documented and unique intricacies of GML 2020. From my experience, here are the first few rules to remember:
  1. There are 2 distinct function declaration syntaxes: the named syntax which is used to declare global functions with no built-in self identity (i.e. function FUNCTIONNAME(...) { ... }), or the anonymous syntax which is used to declare instance/struct functions with a built-in self identity (i.e. function(...) { ... })
  2. Functions declared at the top level using the named function syntax DO NOT get the method type, and behave exactly like scripts from legacy GML. They don't come with a built-in self, they get their self identity from whoever runs them. You also can't store them in a variable and call them with variable(...) like with a method, in these cases you still need to use script_execute(variable, ...).
  3. You can use method() to create a method that runs a script or a method with the given subject designated as self.
    GML:
    instMethod = method(inst, scr_something);
    instMethod(); // This runs scr_something with inst as self
  4. In an untyped struct literal, functions declared using the anonymous syntax get the method type, and binds to that struct. Their self identity is that struct.
    GML:
    x = 3;
    y = 4;
    strc = {
        x: 6,
        y: 8,
        norm: function() {
            return point_distance(0, 0, x, y);
        }
    };
    show_message(strc.norm()); //Shows 10
  5. In any code run by an instance that is NOT enclosed within a struct literal, functions declared using the anonymous syntax get the method type, and binds to the current instance. Their self is the instance that was current when the declaration was encountered, not when it is run.
    GML:
    /* obj_example */
    act = function() {
       show_message("I am " + object_get_name(object_index));
    }
    act(); // Shows obj_example
    
    var action = act;
    with (obj_something_else) {
        action(); // Still shows obj_example because the method carries its original scope with it
    }
 

Erik Leppen

Member
The "self" keyword is tied to the calling instance, so I assume "game" is the name of an object you have created
Er, yes, forgot to mention that.

To create a Lightweight Object (which sounds like what you are trying to do)
Well, I was trying to create a normal object, because I like to keep using things like with-loops and instance_destroy/instance_exists. Also I don't want the node objects to be "children of" the game object. They're still "things in the room" so I think using normal objects would be the way to go.

But yeah, I'm not sure yet when or where to use structs in general, so this is another question that will rise. I was thinking of using a Node struct to hold all functions that aren't instance-bound, such as node_nearest, so I can write Node.nearest() instead. But I'm not sure this has any benefits. But I don't believe I need an actual constructor for that.


GML:
    newnode.unlink = method(newnode, function () {
        ....
    });
This sounds like it does what I want to. Will try.

In fact your point 3 of your list shows I can "pre-define" the function and then when creating the instance, just bind it as a method.

By the way, your list is very useful, and informative, I think to anyone using methods. Maybe @Nocturne can use this and add this to the official documentation?

Thanks to both for the extensive answers!
 
Top