• Hey! Guest! The 40th (!!!) GMC Jam will take place between February 25th, 12:00 UTC to March 1st 12:00 UTC. Why not join in this very special anniversary jam! Click here to find out more!

GMS 2.3+ [SOLVED] Passing method doesn't crash while static?

Essentially, I'm trying to do some form of namespacing to aid in passing common functions between out-of-battle characters and in-battle units. Calling a method passed down crashes, but only when it is not static. This appears to be a scope error, as the non-static one appears to be called from the container somehow, despite calling the function specifically through the character struct. This is a bit easier to explain in code, so I'll go ahead and do so:
GML:
// Create event of objCharacterController
var _container = function() constructor {
    // This name variable is for debug purposes so the scope problems are obvious
    name = "Container";

    static heal_hp_static = function(_amount) {
        show_debug_message(name); // Outputs to debug console to show what is calling the function
        var _prevHP = hp;
        hp = min(mhp, hp+_amount);
        show_debug_message(hp);
        return (hp-_prevHP);
    }

    heal_hp = function(_amount) {
        show_debug_message(name);
        var _prevHP = hp;
        hp = min(mhp, hp+_amount);
        show_debug_message(hp);
        return (hp-_prevHP);
    }
}

// commonFunctions is effectively a container struct for functions
// It exists so other objects can pull functions from it
commonFunctions = new _container();
EDIT: Fixed some stuff here. Forgot the static keyword.
GML:
character = function() constructor {
    // For debug output
    name = "Character";

    // Passing methods
    static heal_hp_works = objCharacterController.commonFunctions.heal_hp_static;
    static heal_hp_crashes = objCharacterController.commonFunctions.heal_hp;

    // HP values set below maximum so I have something to restore
    hp = 4;
    mhp = 8;
}

player1 = new character();
GML:
player1.heal_hp_works(2);
player1.heal_hp_crashes(2); // Game crashes!
The debug console reads:
Character
6
Container
<error code, "Variable anon_gml_Object_objCharacterController_Create_0_122_gml_Object_objCharacterController_Create_0.hp(100268, -2147483648) not set before reading it.">
Is this a bug, or is it an expected error? If the latter, why?
 
Last edited:

Nidoking

Member
Does it work if you bind the non-static function as a method? I don't see why you wouldn't just make all of the functions static, but I assume that if you want to store a function from another instance/struct, you'd have to re-bind it as a method for the new instance/struct.
 
Does it work if you bind the non-static function as a method? I don't see why you wouldn't just make all of the functions static, but I assume that if you want to store a function from another instance/struct, you'd have to re-bind it as a method for the new instance/struct.
Is that not what this does?
GML:
static heal_hp_works = objCharacterController.commonFunctions.heal_hp_static;
static heal_hp_crashes = objCharacterController.commonFunctions.heal_hp;
 

kburkhart84

Firehammer Games
Is that not what this does?
Nope...copying the function variable like that makes a reference to the function, and that function will still run in the scope of the object it was created in(unlike global functions). Functions declared like this are typically called methods instead of functions. I actually use this to benefit if you pass in a method to my input system, as it can call the function/method later on, and it runs in the scope of the instance that the method was created on, without actually using a with(){} statement to change scope.

There is a function called method() that can basically copy functions and change the scope of them. It seems that this is more like what you want.

I'm not sure why your errors are working the way they are honestly. I personally would have thought that neither one of those should have crashed, as both methods exist(even if one is static).

As far as trying to namespace stuff...it is a problem I'm familiar with. The only proper solution I've been able to come up with is basically prefixing. My systems have internal and external functions(and variables, objects, etc...). In the case of my input system, all internal stuff is prefixed with "__fhInput" and all external stuff is using "fhInput" and note that that is a double underscore. Doing it like this isn't real namespacing of course...but I DO get some of the benefits of namespacing, as you won't accidently auto-complete anything that is internal to my system unless you happen to use the double underscore...while you still can auto-complete the external stuff using the prefix. It's not perfect, but IMO it's as good an idea as any I've seen so far.
 
Nope...copying the function variable like that makes a reference to the function, and that function will still run in the scope of the object it was created in(unlike global functions). Functions declared like this are typically called methods instead of functions. I actually use this to benefit if you pass in a method to my input system, as it can call the function/method later on, and it runs in the scope of the instance that the method was created on, without actually using a with(){} statement to change scope.

There is a function called method() that can basically copy functions and change the scope of them. It seems that this is more like what you want.

I'm not sure why your errors are working the way they are honestly. I personally would have thought that neither one of those should have crashed, as both methods exist(even if one is static).

As far as trying to namespace stuff...it is a problem I'm familiar with. The only proper solution I've been able to come up with is basically prefixing. My systems have internal and external functions(and variables, objects, etc...). In the case of my input system, all internal stuff is prefixed with "__fhInput" and all external stuff is using "fhInput" and note that that is a double underscore. Doing it like this isn't real namespacing of course...but I DO get some of the benefits of namespacing, as you won't accidently auto-complete anything that is internal to my system unless you happen to use the double underscore...while you still can auto-complete the external stuff using the prefix. It's not perfect, but IMO it's as good an idea as any I've seen so far.
That fixed it! Was unaware of that function and how it worked. The second one crashed because commonFunction doesn't have an hp variable assigned. The function otherwise worked as expected. No idea why one would work any different than the other, though...

GML:
// Fixed
static heal_hp_crashes = method(self, objCharacterController.commonFunctions.heal_hp);
 
Top