本页介绍如何在 GameMaker 中使用 JSON。
JSON(JavaScript 对象表示法) 是一种轻量级数据交换格式,对于人和机器来说都易于读写。它建立在两个基本结构之上:
重要JSON 与数字 无关 。在任何编程语言中,都可以有各种不同容量和补数的数字类型,固定或浮动,二进制或十进制。这会使不同编程语言之间的互换变得困难。相反,JSON 只提供人类使用的数字表示形式:数字序列。所有编程语言都知道如何理解数字序列,即使它们在内部表示上存在分歧。如需了解更多信息,请参阅 ECMA JSON 标准 。
GameMaker 通过两组函数内置支持读取和写入 JSON:
本指南重点介绍函数 json_parse 和 json_stringify。
JSON 标准支持的值数量有限:对象、数组、字符串、数字以及值 true、false 和 null。GameMaker 使用的 数据类型 比这更多,为了以稍后可以读回的方式存储这些数据,它将它们转换为字符串表示形式。json_parse/json_stringify 和 json_decode/json_encode 都会执行这些转换。
注意 您可以通过将 json_parse 的 inhibit_string_convert 参数设置为 true 来禁用解析 JSON 时的自动字符串转换。
下表列出了在 JSON 数据和 GameMaker 之间进行转换时发生的转换:
JSON Type | GM Type/Value | 笔记 |
---|---|---|
true / false | true / false | |
real / number | Real | |
"@i64@hex_value$i64@" | int64 | GameMaker 会将其写为: - 如果在 int32 的有效范围内,则为 int。 - 如果可以在不丢失精度的情况下这样做,则为 double。 - 一个 字符串 ,其前面带有标识符 "@i64@",后面带有标识符 "$i64$"( 如果这两种情况都不适用)。 当您再次读取 JSON,GameMaker 将获取这些标识符并将值转换回 int64( 除非禁用转换)。 警告 如果 JSON 用于服务器或某些其他非 GameMaker 目标,这些值不合适,因此应避免使用。 |
null | undefined pointer_null | undefined 和 pointer_null 写入为 JSONnull。解析 或 解码 时,JSONnull 始终会读回为 undefined 。 |
string | String | |
"@@infinity$$", @@-infinity$$ | infinity, -infinity | infinity 被写为字符串 |
"@@nan$$" | NaN | NaN 写为字符串 |
object | Struct (or DS Map) | |
array | Array (or DS List) | |
"ref <type> <id>" | Handle | 写入 JSON 时,句柄存储在其字符串表示形式 “ref <type> <id>” 中,读回 JSON 时则转换回运行时值。 使用 json_encode 时会出现例外情况: 您已标记的嵌套 DS Maps 和 DS List 将分别被写入 JSON 对象和数组。 注意 依赖此行为通常没什么用,因为索引会在游戏运行之间发生变化。 |
如果您希望完全控制 JSON 内容的读取或写入方式,您可以选择提供一个针对每个元素运行的过滤器函数,以便 json_stringify 和 json_parse。请参阅功能页面了解更多信息。
编写 JSON 是通过 json_stringify 完成的。例如,以下结构体:
my_struct =
{
test: "this",
val: ["that", "another thing", {"value": "a nested struct"}]
};
可以使用 json_stringify 转换为 JSON 字符串:
struct_json = json_stringify(my_struct);
然后使用文本文件函数写入文件:
var _file = file_text_open_write("struct_as_json.json");
file_text_write_string(_file, struct_json);
file_text_close(_file);
或者缓冲函数:
var _buff = buffer_create(16384, buffer_fixed, 1);
buffer_write(buffer_text, struct_json);
buffer_save("struct_as_json.json");
buffer_delete(_buff);
您还可以通过网络发送 JSON 字符串或将其存储在其他位置 (例如数据库的文本字段中)。
以下文本是输出:
{"test":"this","val":["that","another thing",{"value":"a nested struct"}]}
默认情况下,json_stringify 写入 线性化 的 JSON。在这种情况下,所有内容都放在同一行上,中间没有空格,也没有添加缩进。当每个字节都很重要时,这种格式很有用,尽管它不是特别可读。
prettify 参数可以设置为 true,以向生成的 JSON 字符串添加缩进,使其看起来 " 漂亮",即更易于人类阅读。
以下代码:
struct_json = json_stringify(my_struct, true);
outputs the following JSON:
{
"test":"this",
"val":[
"that",
"another thing",
{
"value":"a nested struct"
}
]
}
与线性化输出相比,上面的 JSON 一目了然地显示了数据的构造方式。
要从之前创建的 JSON 字符串中读取数据,您可以使用 json_parse 将其转换为结构体:
my_struct = json_parse(struct_json);
默认情况下,将检查 JSON 中的字符串值以查看它们是否包含特殊值。如果是,这些值将转换为内部 GameMaker 值。例如:
json = json_stringify({ "infinity": infinity, "NaN": NaN});
show_debug_message(json); // {"NaN":"@@nan$$","infinity":"@@infinity$$"}
data_with_strings_converted = json_parse(json);
show_debug_message(data_with_strings_converted); // { NaN : NaN, infinity : inf }
data_with_strings_unchanged = json_parse(json, , true);
show_debug_message(data_with_strings_converted); // { NaN : "@@nan$$", infinity : "@@infinity$$" }
序列化 描述如下:
In computing, serialization (or serialisation) is the process of translating a data structure or object state into a format that can be stored (e.g. files in secondary storage devices, data buffers in primary storage devices) or transmitted (e.g. data streams over computer networks) and reconstructed later (possibly in a different computer environment).
在 GameMaker 中,结构体就是这样一种数据结构。它可以包含变量和函数作为其成员。当您使用 json_stringify 对结构体的内容进行字符串化时,仅写入结构体的变量,而不写入其函数。
注意 您只能在 GameMaker 中以这种方式序列化 结构 ,而不能序列化 数据结构 或 实例 。
通过手动设置 json_parse 使用 static_set 返回的结构的 静态结构 ,您可以将这些结构恢复到其原始状态。
例如,假设您有一个构造函数 Vector:
function Vector(_x=0, _y=0, _z=0) constructor
{
x = _x;
y = _y;
z = _z;
static add = function(_v2)
{
x += _v2.x;
y += _v2.y;
z += _v2.z;
return self;
}
static sum = function(_v1, _v2)
{
// Note: call using Vector.sum(v1, v2)
return new Vector(_v1.x + _v2.x, _v1.y + _v2.y, _v1.z + _v2.z);
}
static dot = function(_v1, _v2)
{
// Note: call using Vector.sum(v1, v2)
return dot_product_3d(_v1.x, _v1.y, _v1.z, _v2.x, _v2.y, _v2.z);
}
// Other
}
然后,您创建其中一些向量:
v1 = new Vector();
v2 = new Vector(100, 100);
v3 = new Vector(200, 20, 50);
v1.add(v2);
v4 = Vector.sum(v2, v3);
这四个向量可以存储在数组中,并使用 json_stringify 转换为 JSON:
json = json_stringify([v1, v2, v3, v4]);
该 JSON 字符串可以保存或发送到其他地方。
此时,您可能希望在游戏的另一个实例中恢复这些向量,不仅仅是它们的值 (x、y 和 z),还有它们的行为 (add, sum, dot,...)。
要将信息返回到结构中,您可以使用 json_parse 解析 JSON 字符串,然后将各个数组元素分配给之前使用的变量名称:
vectors = json_parse(json);
v1 = vectors[0];
v2 = vectors[1];
v3 = vectors[2];
v4 = vectors[3];
现在,这些结构已从 JSON 字符串中读回,但它们是完全新的结构,不再属于构造函数。这些结构在游戏的原始实例中是 Vector 结构,但现在不再是了。至少,不是 " 官方 " 的,正如您在调用函数 instanceof 时看到的那样:
instanceof(v1, Vector); // false
instanceof(v2, Vector); // false
instanceof(v3, Vector); // false
instanceof(v4, Vector); // false
要告诉 GameMaker 这些结构中的每一个都应再次属于 Vector 构造函数,您可以使用函数 static_set:
var _static_vec = static_get(Vector);
static_set(v1, _static_vec);
static_set(v2, _static_vec);
static_set(v3, _static_vec);
static_set(v4, _static_vec);
通过在结构上使用 static_set,您首先将它们作为简单的 " 数据 " 结构与静态分离,然后将它们作为 Vector 静态结构的 " 子 " 结构重新附加到静态链。由于您可以访问结构所属的静态结构 (或构造函数) 层次结构的所有静态成员,因此您可以通过这种方式为结构提供对 Vector 的 静态链 的访问权限,从而恢复其行为。
从现在开始,v1 到 v4 都是 Vector 的实例!
instanceof(v1, Vector); // true!
instanceof(v2, Vector); // true!
instanceof(v3, Vector); // true!
instanceof(v4, Vector); // true!
您可以像任何其他 Vector 结构一样使用它们:
v1.add(v2).add(v3);
v5 = Vector.sum(v1, v1);
重要 为了使其正常工作,函数必须定义为静态,以便它们属于 构造函数 ,而不是结构体 实例 。如果不将函数声明为静态,则每个结构体实例都应通过执行构造函数来接收该函数的副本。不过,使用 static_set 设置静态结构不会调用构造函数,而是将该结构移动到 静态链 中的不同位置,从而使其能够访问不同静态结构的函数 (请参阅 点运算符如何查找变量名称 )。