A method variable is essentially a variable that has had a function assigned to it, "binding" the function to an instance and enabling you to use the variable to refer to the function - much like you use a runtime function name to refer to a built-in GML function. The variable used can be local, instance or global in scope (see here for more information on variable scope).
The syntax for creating a method variable is as follows:
<variablename> = function(<parameter1>, <parameter2>, etc... )
{
<statement1>;
<statement1>;
...
}
or
function <variablename>(<parameter1>, <parameter2>, etc... )
{
<statement1>;
<statement1>;
...
}
In general, however, you would use the first form for methods, and the second form for defining script functions, since the second form will also assign a script index to the function name while the first form will be a "true" method (and require the use of the global prefix if used to define a scripted function).
NOTE: You can check this by using both forms in project and then calling the runtime function typeof() on each of them. One will be classed as a "number" - since it returns a script index ID - and the other will be classed as a "method".
So, keep in mind that - in general - we will always be referring to functions that have not been defined with a script index when we are talking about methods and method variables.
Below you can see three simple examples of creating a method variable using different scopes:
// Local
var _debug = function(message)
{
show_debug_message(message);
}
// Instance
do_maths = function(val1, val2, val3)
{
return (val1 * val2) - val3;
}
// Global
global.pd = function(_x1, _y1, _x2, _y2);
{
return point_distance(_x1, _y1, _x2, _y2);
}
Notice that in the above code, the various parameters that are given as inputs for the function are all named and these names are what should be used within the function to refer to the different inputs. Also note that you can use the return statement to return a value from a function for use elsewhere in your code, and that a function with no return value defined, will return undefined.by default.
NOTE: While the variable will be in the chosen scope, the actual function will be bound to the scope that it was initially defined in. For example, script functions are all global scope and "unbound" (ie: they are not associated with any instances), but if you have a script function that creates another function as a method variable within it and then you call this script function from an instance, the function used within the script will be bound to the instance variable as a method. In general this is not something you ever need to think about but for more complex operations with method variables it's worth taking into consideration. This also applies when using other constructs like with - when you create a method variable inside a with, the function will be bound to the instance that is currently in scope.
Once created, the method variable can be used just as you would a runtime function or a script function, for example:
create_vec = function(_x1, _y1, _x2, _y2);
{
var _array;
_array[0] = point_distance(_x1, _y1, _x2, _y2);
_array[1] = point_direction(_x1, _y1, _x2, _y2);
return _array;
}
vec = create_vec(x,
y, mouse_x, mouse_y);
Variables created within a function will follow the same rules as normal and will be scoped according to the keyword used or the scope of the function call. In the above example, we use var so the array variable is in the local scope of the function. If we didn't use the keyword, then the variable would have been created on the scope of the instance that called the function.
Method variables (and script functions) can also take a variable number of arguments, and you can use the built in argument[0 ... n] array for the function parameters. You can then use the built in variable argument_count to check for these extra parameters. For example:
/// @function create_random(object, layer);
/// @param {int} object The object to create an instance of
/// @param {int} layer OPTIONAL! The layer to create it on
///
/// @description Create an instance of the given object at a random position on the current layer or on the (optional) given layer
function create_random(_obj)
{
var _layer = layer;
if argument_count > 1
{
_layer = argument[1];
}
var _x = irandom(room_width);
var _y = irandom(room_height);
instance_create_layer(_x, _y, _layer, _obj);
}
NOTE: You cannot use the array accessor @ when working with the argument[n] array.
Use of the built in argument[n] array is not strictly required, however, and you should be aware too that any arguments that are not provided to the function (but the function has an argument specified) will be initialised to "undefined". This means you do not need to use the above mentioned structure using argument_count, and can instead do something like this:
function my_func( _value)
{
_value = is_undefined(_value) ? 10 : _value;
return _value * 10;
}
The code above would allow you to specify a default value for a parameter if the argument is not supplied to the function.
NOTE: The above code uses the ternary operator, which you can find out more about here.
An interesting feature of method variables (and script functions as well), is that they can have static variables. A static variable is one that is defined the first time that the function is called and that will maintain it's value from then onwards. To create a static variable you need to define it using the static keyword, as shown in this simple example:
counter = function()
{
static num = 0;
return num++;
}
In the above example, the variable "num" is a static variable, and so will be defined as 0 the first time the function is called, but every time the function is called after that, the variable definition will be ignored. So, if you then call this function like this:
for (var i = 0; i < 10; ++i;)
{
show_debug_message(counter());
}
The output will be:
0
1
2
3
4
5
6
7
8
9
If you didn't use the static keyword here then the output would simply be 0 for every iteration of the loop, since the variable "num" will be getting defined as 0 every time the function is called before being returned. Note that a static variable can only be changed inside the original function, outside you are getting a local instance variable (a copy of the static found inside the function) - essentially the shared static variable can only be changed by the function, the shared copy can only be written to by the function.
You can also use the static keyword within a method (and a script function) to create a static function, which - like with variables - simply means that it is a function that will only be defined once the first time the method is used, for example:
Vector2 = function( _x, _y ) constructor
{
x = _x;
y = _y;
static Add = function( _other )
{
x += _other.x;
y += _other.y;
}
}
In the above example, the function Vector2 can be used to create a struct, and the struct will have some variables, one of which is the method variable Add. Since this variable has been defined as static, the function it refers to will only be initialised once the first time the Vector2 function is called, and all further structs created with this function will reference the function Add that was created initially, instead of creating a new function for each struct (for more information on structs and the constructor keyword please see here).
Below we list a few helper functions associated with method variables: