用 Struct 实现 JSON 存档系统

前段时间群里有朋友在讨论游戏存档的问题,想起了前两年曾经介绍过几种简单的实现存档的方式,分别是采用内置的 game_save() 函数、ini 配置文件和 ds_map 加密存储,其中 ds_map 数据结构可以兼容 JSON 数据,但用 ds_map 来保存某些 JSON 数据可能就会面临反复套娃(ds_map 和 ds_list 相互嵌套)的问题,尽管大部分时候这可能也算不上问题,但 JSON 数据本身就是一个轻量化的数据结构,结果被 GameMaker 的 DS 函数弄得特别笨重,实在很蠢,幸好 YoYo 在 2.3.1 里引入了 Struct 结构体,前段时间 Shaun Splading 和 Matharoo 也都介绍了用 json 和 struct 来实现存档功能,于是今天就来介绍一下如何使用 Struct(结构体) 和 Array (数组)配合实现一套基于 JSON 数据的存档系统吧。

如果不太清楚 JSON 数据是什么的朋友,请看 json.org

存取文件

首先我们自定义两个方法,分别用于把 Json 数据写成文件保存以及读取已经存在的存档文件,当然这两个方法可以广泛运用在其他场景,而不仅仅是读写存档文件,因此单独抽象出来会更通用。
保存文件

///利用buffer对象将字符串保存为json文件
function SaveString (_string, _filename) {    
    var _buffer = buffer_create(string_byte_length(_string) + 1, buffer_fixed, 1);
    buffer_write(_buffer, buffer_string, _string);
    buffer_save(_buffer, _filename);
    buffer_delete(_buffer);
}

读取文件

///利用buffer对象读取json文件并转为字符串
function LoadString (_filename) {
    var _buffer = buffer_load(_filename);
    var _string = buffer_read(_buffer, buffer_string);

    buffer_delete(_buffer);
    return _string;
}

以上两个函数中我们存取的其实是字符串信息,而将 json 数据和 struct 结构体相互转化则需要用到新引入的 json_stringify() json_parse() 两个函数,下面我会用一个实际的例子来说明一下具体的使用方法。

示例

假设现在我们有这样一个简单的卡牌游戏,每次点击卡牌则卡牌会反转到正面,并且随机显示一个数字,再次点击则翻回背面。

那我们希望可以利用这套系统来保存画面中卡牌的状态,就需要记录一些基本的信息:

  • 卡牌的数量
  • 每张卡牌的属性
    • 坐标
    • 翻转状态
    • 数值

这就是我们需要记录的数据结构,大概会长成下面这个样子:

{
    card_count : 3,        //卡牌数量
    [                      //卡牌属性数组
        {x: 100,y: 200, front: true,  num: 7},  //单张卡属性
        {x: 150,y: 230, front: false, num: 0},
        {x: 200,y: 170, front: true,  num: 3},
    ]
}

那我们现在可以来构造我们的数据保存/读取方法,我们可以在控制其中构造两个存取档案的方法用来
保存

Save = function () {
    // 获取卡片数量
    var _cardCount = instance_number(oCard);
    
    // 创建卡牌数组根结构体
    var _rootStruct = {
        cardCount : _cardCount,
        cards : array_create(_cardCount)
    };
    
    // 写入卡片数据
    for (var i = 0; i < _cardCount; i ++) {
        var _card = instance_find(oCard, i);
        
        _rootStruct.cards[i] = {
            x : _card.x,
            y : _card.y,
            num : _card.num,
            front : _card.front,
        }
    }
    
    // 把结构体转化为json格式
    var _json = json_stringify(_rootStruct);
    // 保存为文件
    SaveJson(_json, "mysave");
}

读取

Load = function () {
    if (!file_exists("mysave")) return;
    
    // 读取JSON转为字符串
    var _json = LoadJson("mysave");
    // 把json数据转化成结构体
    var _rootStruct = json_parse(_json);
    
    // 销毁已存在的卡片卡片
    instance_destroy(oCard);
    
    var _cardCount = _rootStruct.cardCount;
    var _cards = _rootStruct.cards;
    
    for (var i = 0; i < _cardCount; i ++) {
        var _cardData = _cards[i];
        
        // 创建读取的卡片实例
        var _card = instance_create_layer(_cardData.x, _cardData.y, "Instances", oCard);
        _card.num = _cardData.num;
        _card.front = _cardData.front;
    }
}

然后我们在控制器的 step 事件中监听“S”和“L”的按键事件来分别执行上面保存和读取的操作

if (keyboard_check_pressed(ord("S"))) {    //保存
    Save();
}

if (keyboard_check_pressed(ord("L"))) {    //读取
    Load();
}

现在我们启动游戏,随机点选一下卡牌以后,按下“S”键就可以在对应路径下找到一个"mysave"的文件

用文本编辑器打开这个文件可以看到其中记录的数据

当然在按下“L”键时也能正常加载数据。

最后放出示例工程的下载链接,如果文章没有表述清楚的话,直接看一下代码吧,很简单的小项目,仅供参考,实际运用到自己的项目中的话请按需自行修改
链接: 示例工程 密码: 1kdf

2021-01-20 01:17
Comments
Write a Comment