QT5.14.2自带Examples:JSON Save Game

概述

JSON的全称是”JavaScript Object Notation”,是一种基于文本,独立于语言的轻量级数据交换格式。类似XML,也是一种数据交换格式,XML虽然可以作为跨平台的数据交换格式,XML标记比数据多,而JSON没有附加的任何标记。

默认运行时,会创造一份新的游戏档案,运行效果如下:
在这里插入图片描述
可以通过命令行参数加载存档文件,运行效果如下(运行时需要在项目选项中设置命令行参数):
在这里插入图片描述
下面是本示例运行时所产生的save.json文件(进行了一些名子的修改):

{
    "levels": [
        {
            "name": "Village",
            "npcs": [
                {
                    "classType": 0,
                    "level": 8,
                    "name": "Barry the Blacksmith"
                },
                {
                    "classType": 0,
                    "level": 6,
                    "name": "Terry the Trader"
                }
            ]
        },
        {
            "name": "Dungeon",
            "npcs": [
                {
                    "classType": 1,
                    "level": 20,
                    "name": "Eric the Evil"
                },
                {
                    "classType": 0,
                    "level": 5,
                    "name": "Eric's Left Minion"
                },
                {
                    "classType": 0,
                    "level": 6,
                    "name": "张三"
                }
            ]
        }
    ],
    "player": {
        "classType": 2,
        "level": 20,
        "name": "阿西拜"
    }
}


实现步骤

角色类

定义
#ifndef CHARACTER_H
#define CHARACTER_H

#include <QJsonObject>
#include <QObject>
#include <QString>

//游戏中的角色
//可以是玩家的角色,也可以是非玩家的角色
class Character
{
    //类似 Q_OBJECT,支持 QMetaObject的反射能力。但不支持信号槽机制。
    //本例中要使用的Q_ENUM,需要这个宏的支持
    Q_GADGET;

public:
    enum ClassType {
        Warrior, Mage, Archer
    };
    //Q_ENUM宏向元对象系统注册枚举类型
    Q_ENUM(ClassType)

    Character();
    Character(const QString &name, int level, ClassType classType);

    QString name() const;
    void setName(const QString &name);

    int level() const;
    void setLevel(int level);

    ClassType classType() const;
    void setClassType(ClassType classType);

    //我们研究的重点是read和write
    void read(const QJsonObject &json);
    void write(QJsonObject &json) const;

    void print(int indentation = 0) const;
private:
    //需要维护的数据:名字,级别,职业
    QString mName;
    int mLevel;
    ClassType mClassType;
};

#endif // CHARACTER_H

实现
#include "character.h"

#include <QMetaEnum>
#include <QTextStream>

Character::Character() :
    mLevel(0),
    mClassType(Warrior) {
}

Character::Character(const QString &name,
                     int level,
                     Character::ClassType classType) :
    mName(name),
    mLevel(level),
    mClassType(classType)
{
}

QString Character::name() const
{
    return mName;
}

void Character::setName(const QString &name)
{
    mName = name;
}

int Character::level() const
{
    return mLevel;
}

void Character::setLevel(int level)
{
    mLevel = level;
}

Character::ClassType Character::classType() const
{
    return mClassType;
}

void Character::setClassType(Character::ClassType classType)
{
    mClassType = classType;
}

//! [读取进度]
void Character::read(const QJsonObject &json)
{
    if (json.contains("name") && json["name"].isString())
        mName = json["name"].toString();

    if (json.contains("level") && json["level"].isDouble())
        mLevel = json["level"].toInt();

    if (json.contains("classType") && json["classType"].isDouble())
        mClassType = ClassType(json["classType"].toInt());
}
//! [读取进度]

//! [写进度]
void Character::write(QJsonObject &json) const
{
    json["name"] = mName;
    json["level"] = mLevel;
    json["classType"] = mClassType;
}
//! [写进度]

void Character::print(int indentation) const
{
    const QString indent(indentation * 2, ' ');
    QTextStream(stdout) << indent << "Name:\t" << mName << "\n";
    QTextStream(stdout) << indent << "Level:\t" << mLevel << "\n";

    QString className = QMetaEnum::fromType<ClassType>().valueToKey(mClassType);
    QTextStream(stdout) << indent << "Class:\t" << className << "\n";
}

关卡等级类

类定义
#ifndef LEVEL_H
#define LEVEL_H

#include <QJsonObject>
#include <QVector>

#include "character.h"

//! [当前游戏关卡级别]
//游戏有很多的等级,不同的关卡级别非玩家角色也不同
class Level
{
public:
    Level() = default;
    Level(const QString &name);

    QString name() const;

    QVector<Character> npcs() const;
    void setNpcs(const QVector<Character> &npcs);

    void read(const QJsonObject &json);
    void write(QJsonObject &json) const;

    void print(int indentation = 0) const;
private:
    //需要维护的数据:本级别关卡的名称,非玩家角色列表
    QString mName;
    QVector<Character> mNpcs;
};
//! [当前游戏关卡级别]

#endif // LEVEL_H

类实现
#include "level.h"

#include <QJsonArray>
#include <QTextStream>

Level::Level(const QString &name) : mName(name)
{
}

QString Level::name() const
{
    return mName;
}

QVector<Character> Level::npcs() const
{
    return mNpcs;
}

void Level::setNpcs(const QVector<Character> &npcs)
{
    mNpcs = npcs;
}
//![读:从QJsonObject给mName和mNpcs赋值]
void Level::read(const QJsonObject &json)
{
    if (json.contains("name") && json["name"].isString())
        mName = json["name"].toString();
    //非玩家角色在json中以QJsonArray方式存在
    if (json.contains("npcs") && json["npcs"].isArray()) {
        QJsonArray npcArray = json["npcs"].toArray();
        mNpcs.clear();
        //提高效率,防止增加内容的时候重新分配内存空间
        mNpcs.reserve(npcArray.size());
        //遍历QJsonArray的内容,append到我们的成员变量中
        for (int npcIndex = 0; npcIndex < npcArray.size(); ++npcIndex) {
            QJsonObject npcObject = npcArray[npcIndex].toObject();
            Character npc;
            npc.read(npcObject);
            mNpcs.append(npc);
        }
    }
}
//![读:从QJsonObject给mName和mNpcs赋值]
//![写:将mName和mNpcs的值写入QJsonObject]
void Level::write(QJsonObject &json) const
{
    json["name"] = mName;
    QJsonArray npcArray;
    for (const Character &npc : mNpcs) {
        QJsonObject npcObject;
        npc.write(npcObject);
        npcArray.append(npcObject);
    }
    json["npcs"] = npcArray;
}
//![写:将mName和mNpcs的值写入QJsonObject]
//!
void Level::print(int indentation) const
{
    const QString indent(indentation * 2, ' ');
    QTextStream(stdout) << indent << "Name:\t" << mName << "\n";

    QTextStream(stdout) << indent << "NPCs:\n";
    for (const Character &character : mNpcs)
        character.print(2);
}

游戏档案类

类定义
#ifndef GAME_H
#define GAME_H

#include <QJsonObject>
#include <QVector>

#include "character.h"
#include "level.h"

//! [整个游戏层面的存档]
class Game
{
public:
    enum SaveFormat {
        Json, Binary
    };

    Character player() const;
    QVector<Level> levels() const;

    void newGame();
    bool loadGame(SaveFormat saveFormat);
    bool saveGame(SaveFormat saveFormat) const;

    void read(const QJsonObject &json);
    void write(QJsonObject &json) const;

    void print(int indentation = 0) const;
private:
    //需要维护的数据:玩家信息,当前游戏关卡级别
    Character mPlayer;
    QVector<Level> mLevels;
};
//! [整个游戏层面的存档]

#endif // GAME_H

类实现
#include "game.h"

#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QRandomGenerator>
#include <QTextStream>

Character Game::player() const
{
    return mPlayer;
}

QVector<Level> Game::levels() const
{
    return mLevels;
}

//! [新建游戏是开启新的存档信息]
void Game::newGame()
{
    mPlayer = Character();
    mPlayer.setName(QStringLiteral("Hero"));
    mPlayer.setClassType(Character::Archer);
    mPlayer.setLevel(QRandomGenerator::global()->bounded(15, 21));

    mLevels.clear();
    mLevels.reserve(2);

    Level village(QStringLiteral("Village"));
    QVector<Character> villageNpcs;
    villageNpcs.reserve(2);
    villageNpcs.append(Character(QStringLiteral("Barry the Blacksmith"),
                                 QRandomGenerator::global()->bounded(8, 11),
                                 Character::Warrior));
    villageNpcs.append(Character(QStringLiteral("Terry the Trader"),
                                 QRandomGenerator::global()->bounded(6, 8),
                                 Character::Warrior));
    village.setNpcs(villageNpcs);
    mLevels.append(village);

    Level dungeon(QStringLiteral("Dungeon"));
    QVector<Character> dungeonNpcs;
    dungeonNpcs.reserve(3);
    dungeonNpcs.append(Character(QStringLiteral("Eric the Evil"),
                                 QRandomGenerator::global()->bounded(18, 26),
                                 Character::Mage));
    dungeonNpcs.append(Character(QStringLiteral("Eric's Left Minion"),
                                 QRandomGenerator::global()->bounded(5, 7),
                                 Character::Warrior));
    dungeonNpcs.append(Character(QStringLiteral("Eric's Right Minion"),
                                 QRandomGenerator::global()->bounded(4, 9),
                                 Character::Warrior));
    dungeon.setNpcs(dungeonNpcs);
    mLevels.append(dungeon);
}
//! [新建游戏是开启新的存档信息]

//! [加载存档:调用read函数]
bool Game::loadGame(Game::SaveFormat saveFormat)
{
    //    enum SaveFormat {Json, Binary};
    QFile loadFile(saveFormat == Json
        ? QStringLiteral("save.json")
        : QStringLiteral("save.dat"));

    if (!loadFile.open(QIODevice::ReadOnly)) {
        qWarning("Couldn't open save file.");
        return false;
    }

    QByteArray saveData = loadFile.readAll();

    QJsonDocument loadDoc(saveFormat == Json
        ? QJsonDocument::fromJson(saveData)
        : QJsonDocument::fromBinaryData(saveData));

    //loadDoc.object返回文档中包含的QJsonObject。
    read(loadDoc.object());

    QTextStream(stdout) << "Loaded save for "
                        << loadDoc["player"]["name"].toString()
                        << " using "
                        << (saveFormat != Json ? "binary " : "") << "JSON...\n";
    return true;
}
//! [加载存档:调用read函数]

//! [存档:调用write函数]
bool Game::saveGame(Game::SaveFormat saveFormat) const
{
    QFile saveFile(saveFormat == Json
        ? QStringLiteral("save.json")
        : QStringLiteral("save.dat"));

    if (!saveFile.open(QIODevice::WriteOnly)) {
        qWarning("Couldn't open save file.");
        return false;
    }

    QJsonObject gameObject;
    write(gameObject);
    QJsonDocument saveDoc(gameObject);
    saveFile.write(saveFormat == Json
        ? saveDoc.toJson()
        : saveDoc.toBinaryData());

    return true;
}
//! [存档]

//! [读:jason->mPlayer,jason->mLevels]
void Game::read(const QJsonObject &json)
{
    if (json.contains("player") && json["player"].isObject())
        mPlayer.read(json["player"].toObject());

    if (json.contains("levels") && json["levels"].isArray()) {
        QJsonArray levelArray = json["levels"].toArray();
        mLevels.clear();
        mLevels.reserve(levelArray.size());
        for (int levelIndex = 0; levelIndex < levelArray.size(); ++levelIndex) {
            QJsonObject levelObject = levelArray[levelIndex].toObject();
            Level level;
            level.read(levelObject);
            mLevels.append(level);
        }
    }
}
//! [读:jason->mPlayer,jason->mLevels]

//! [写:mPlayer->jason,mLevels->jason]
void Game::write(QJsonObject &json) const
{
    QJsonObject playerObject;
    mPlayer.write(playerObject);
    json["player"] = playerObject;

    QJsonArray levelArray;
    for (const Level &level : mLevels) {
        QJsonObject levelObject;
        level.write(levelObject);
        levelArray.append(levelObject);
    }
    json["levels"] = levelArray;
}
//! [写:mPlayer->jason,mLevels->jason]

void Game::print(int indentation) const
{
    const QString indent(indentation * 2, ' ');
    QTextStream(stdout) << indent << "Player\n";
    mPlayer.print(indentation + 1);

    QTextStream(stdout) << indent << "Levels\n";
    for (Level level : mLevels)
        level.print(indentation + 1);
}

mian函数

#include <QCoreApplication>
#include <QTextStream>

#include "game.h"
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    QStringList args = QCoreApplication::arguments();
    bool newGame = true;
    if (args.length() > 1)
        newGame = (args[1].toLower() != QStringLiteral("load"));
    bool json = true;
    if (args.length() > 2)
        json = (args[2].toLower() != QStringLiteral("binary"));

    Game game;
    if (newGame)
        game.newGame();
    else if (!game.loadGame(json ? Game::Json : Game::Binary))
            return 1;
    // Game is played; changes are made...

    QTextStream(stdout) << "Game ended in the following state:\n";
    game.print();
    if (!game.saveGame(json ? Game::Json : Game::Binary))
        return 1;

    return 0;
}


  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值