静态结构

每个函数都有一个"静态结构",其中存储了它的静态变量。您可以使用static_get获取该结构:

function counter()
{
    static count = 0;
    return count ++;
}

repeat (10) counter();

// Get static struct of counter()
var _static_counter = static_get(counter);

// Both of these read the same variable
show_debug_message(counter.count); // 10
show_debug_message(_static_counter.count); // 10

对于构造函数也是如此。每个构造函数都有一个静态结构,其中存储了它的静态变量和静态方法。

从构造函数创建的每个结构都从该静态结构访问其静态变量。

警告您无法从从未调用过的函数访问静态变量,因为静态变量是在第一次调用函数时初始化的。尝试这样做将会出现错误并导致游戏崩溃。

静态链

当您使用构造函数继承时,这些构造函数形成一个"静态链" -- 一个静态结构链,其中每个子级都链接到其父级。

例如,假设您有一个构造函数 item,以及一个构造函数 potion,它是 item 的子级:

function item() constructor {}

function potion() : item() constructor {}

var _potion = new potion();

您可以使用 static_get(potion) 获取 potion 的静态结构-这是存储 potion 的静态变量的位置。让我们将其称为 static_potion

现在,如果您调用 static_get(static_potion),您将获得 item 的静态结构!这与您从 static_get(item) 获得的结构相同。

function item () constructor {}
function potion () : item () constructor {}

var _potion = new potion();
var _static_potion = static_get(potion);

show_debug_message(static_get(item) == static_get(_static_potion)); // true (1)

这是因为 itempotion 构造函数的父级,因此 item 的静态结构链接到 potion 的静态结构。

顶级构造函数的静态结构(即那些没有父构造函数的静态结构)共享相同的父结构。该结构是"根"静态结构,其父结构为undefined

var _static_item = static_get(item);         // the static struct of item
var _root = static_get(_static_item);        // the static struct of all top-level static structs
var _must_be_undefined = static_get(_root);  // undefined

此共享结构是所有结构的根父结构,并定义在结构转换为字符串时调用的默认toString函数。

这意味着您可以获取结构的完整静态链,如下所示:

static_chain = [];
var _node = static_get(potion);                        // the static struct to start at
while (!is_undefined(_node))
{
    array_push(static_chain, _node);
    _node = static_get(_node);
};

array_foreach(static_chain, show_debug_message);       // output the path to the root struct

父级和父级中的变量名称相同子构造函数

由于静态变量属于定义它们的构造函数,因此可以在子构造函数中定义与父构造函数的静态变量同名的静态变量。例如:

function shape () constructor
{
    static count = 0;
    count++;
    
    static shapes = [];
    array_push(shapes, self);
}
function rectangle () : shape () constructor
{
    static count = 0;
    count++;
}
function square () : rectangle () constructor
{
    static count = 0;
    count++;
}
function ellipse () : shape () constructor
{
    static count = 0;
    count++;
}

现在,每个形状都有自己的count静态变量,用于跟踪该形状的项目数量。子形状也会增加其父形状的计数,因为它们除了运行自己的构造函数之外,还运行父形状的构造函数。

s1 = new shape();        // Added 1 shape
s2 = new rectangle();    // Added 1 rectangle (and therefore also 1 shape)
s3 = new square();       // Added 1 square (and therefore also 1 rectangle and 1 shape)
s4 = new ellipse();      // Added 1 ellipse (and therefore also 1 shape)

show_debug_message($"Number of shapes: {shape.count}");          // 4
show_debug_message($"Number of rectangles: {rectangle.count}");  // 2
show_debug_message($"Number of squares: {square.count}");        // 1
show_debug_message($"Number of ellipses: {ellipse.count}");      // 1

点运算符如何查找变量名称

假设您正在使用点运算符(即struct.variable_name)在结构体中查找特定变量。

如果该结构包含具有该名称的非静态变量,则点运算符返回该变量。如果没有,点运算符返回静态​​链中具有该名称的第一个变量,检查当前静态结构,然后根据需要遍历整个静态链,直到遇到具有该名称的变量。如果在静态链中的任何位置都找不到变量名称,GameMaker将抛出错误。

例如:

function root() constructor
{
    static show = function()
    {
        show_debug_message("root");
    }
}

function child() : root() constructor { }

function child_with_static_func() : root() constructor
{
    static show = function()
    {
        show_debug_message("child_with_static_func");
    }
}

function child_with_func() : root() constructor
{
    show = function()
    {
        show_debug_message("child_with_func");
    }
}

child1 = new child();
child1.show();

child2 = new child_with_static_func();
child2.show();

child3 = new child_with_func();
child3.show();

上面的代码中发生了以下情况:

父级的静态变量或方法

在某些情况下,您可能希望从子构造函数中访问父构造函数的静态变量或方法。为了实现这一点,您可以沿着静态链向上访问父级的静态变量:

function parent() constructor
{
    static init = function() { show_debug_message("Parent Innit?"); }
}

function child() : parent() constructor
{
    static init = function()
    {
        var _static = static_get(self);
        var _static_parent = static_get(_static);
        _static_parent.init(); // Calls the parent's init()
        
        show_debug_message("Child Innit!");
    }
}

检查多重继承

您可以使用 is_instanceof 来检查结构是否属于给定的构造函数,或者是否将该构造函数作为父构造函数。

这是通过检查结构是否在其静态链中的任何位置具有给定构造函数的静态结构来完成的。

function item() constructor {}

function potion() : item() constructor {}

function enemy() constructor {}

var _potion = new potion();

show_debug_message(is_instanceof(_potion, potion)); // true (1)
show_debug_message(is_instanceof(_potion, item)); // true (1)
show_debug_message(is_instanceof(_potion, enemy)); // false (0)

上面的代码显示了三个构造函数:itempotion,它是 item 的子类,以及 enemy,它是一个单独的构造函数。

它从 potion 构造函数创建一个结构。使用 is_instanceof(),我们可以检查新结构:

通过这种方式,您可以检查结构是否"是"某物,因为 potion 是 item,因此它为两个构造函数返回 true。然而,如果不是真的,反过来: item 不是 potion

改变静态结构

函数 static_set 允许您更改函数的静态结构 (构造函数或非构造函数)。通过这种方式,您可以更改构造函数及其结构可用的静态变量,还可以更改构造函数所属的 " 静态链"。

此函数的推荐用例是反序列化。如果您从 JSON 加载结构,那么这些结构将不属于任何构造函数,但是您可以通过使用 static_set 将构造函数 " 应用 " 到一个结构,以便该结构接收其共享的静态变量,并且您可以运行 is_instanceof 来检查其类型。

var _potion = json_parse(_json_string);

show_debug_message(is_instanceof(_potion, potion)); // false (0)

var _static_potion = static_get(potion);
static_set(_potion, _static_potion);

show_debug_message(is_instanceof(_potion, potion)); // true (1)

在上面的代码中,从 JSON 字符串加载 potion 结构时,它只被创建为一个简单的结构,不属于构造函数,也没有任何静态变量。

在将 potion 的静态结构应用于它之后,它将成为 potion 的实例 (如用 is_instanceof() 测试的那样),并且还获得包含在 potion 构造函数中的任何静态变量和静态方法。