Qt文档阅读笔记-对JSON Save Game官方实例解析

冷不防 2022-12-23 11:11 245阅读 0赞

程序运行截图如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxNzg0NDI3NjE_size_16_color_FFFFFF_t_70

JSON Save Game例子展示了如何使用QJsonDocument,QJsonObject,QJsonArray保存和加载存档。

许多游戏有存储的功能,允许玩家存储保存游戏进度或者加载存档。在存储游戏,通常将每个游戏对象序列到一个文件中。存储的文件格式有多种多样。其中的一种方式就是使用Json格式,使用的是QJsonDocument,如果希望文件不可读,那么可以将其转换为二进制,这里可以再来个对称加密,更好。

此例子展示了如何存储文件,并且加载json文件。

Character类

角色类代表游戏里面的NPC。还存储了玩家名字,等级,职业等。

此类还提供了read()和write()方法去序列化其成员变量。

  1. class Character
  2. {
  3. Q_GADGET;
  4. public:
  5. enum ClassType {
  6. Warrior, Mage, Archer
  7. };
  8. Q_ENUM(ClassType)
  9. Character();
  10. Character(const QString &name, int level, ClassType classType);
  11. QString name() const;
  12. void setName(const QString &name);
  13. int level() const;
  14. void setLevel(int level);
  15. ClassType classType() const;
  16. void setClassType(ClassType classType);
  17. void read(const QJsonObject &json);
  18. void write(QJsonObject &json) const;
  19. void print(int indentation = 0) const;
  20. private:
  21. QString mName;
  22. int mLevel;
  23. ClassType mClassType;
  24. };

上述的代码主要关心read和write函数的实现

  1. void Character::read(const QJsonObject &json)
  2. {
  3. if (json.contains("name") && json["name"].isString())
  4. mName = json["name"].toString();
  5. if (json.contains("level") && json["level"].isDouble())
  6. mLevel = json["level"].toInt();
  7. if (json.contains("classType") && json["classType"].isDouble())
  8. mClassType = ClassType(json["classType"].toInt());
  9. }

read()函数,使用QJsonObject获取值填充到Character中。可以使用QJsonObject::operator[]或QJsonObject::value()。如果返回的值不存在则为QJsonValue::Undefined。可以使用QJsonObject::contains()确保其key值存在。

  1. void Character::write(QJsonObject &json) const
  2. {
  3. json["name"] = mName;
  4. json["level"] = mLevel;
  5. json["classType"] = mClassType;
  6. }

write()函数与read()函数相反,使用QObject::operator[]()和QJsonObject::insert()进行赋值,如果key存在,那么值将会被覆盖。

下面是Level类:

  1. class Level
  2. {
  3. public:
  4. Level() = default;
  5. Level(const QString &name);
  6. QString name() const;
  7. QVector<Character> npcs() const;
  8. void setNpcs(const QVector<Character> &npcs);
  9. void read(const QJsonObject &json);
  10. void write(QJsonObject &json) const;
  11. void print(int indentation = 0) const;
  12. private:
  13. QString mName;
  14. QVector<Character> mNpcs;
  15. };

游戏中还需要等级,每一个npc也需要等级,使用QVector存储Character对象,然后再提供上面类似的read()和write()函数。

  1. void Level::read(const QJsonObject &json)
  2. {
  3. if (json.contains("name") && json["name"].isString())
  4. mName = json["name"].toString();
  5. if (json.contains("npcs") && json["npcs"].isArray()) {
  6. QJsonArray npcArray = json["npcs"].toArray();
  7. mNpcs.clear();
  8. mNpcs.reserve(npcArray.size());
  9. for (int npcIndex = 0; npcIndex < npcArray.size(); ++npcIndex) {
  10. QJsonObject npcObject = npcArray[npcIndex].toObject();
  11. Character npc;
  12. npc.read(npcObject);
  13. mNpcs.append(npc);
  14. }
  15. }
  16. }

使用QJsonArray接受json中的数组,调用toObject(0获取Character的Json对象。然后存储到mNpcs中。

  1. void Level::write(QJsonObject &json) const
  2. {
  3. json["name"] = mName;
  4. QJsonArray npcArray;
  5. for (const Character &npc : mNpcs) {
  6. QJsonObject npcObject;
  7. npc.write(npcObject);
  8. npcArray.append(npcObject);
  9. }
  10. json["npcs"] = npcArray;
  11. }

这个是写函数。

上面就是人物和等级相关的类,下面是游戏类。

  1. class Game
  2. {
  3. public:
  4. enum SaveFormat {
  5. Json, Binary
  6. };
  7. Character player() const;
  8. QVector<Level> levels() const;
  9. void newGame();
  10. bool loadGame(SaveFormat saveFormat);
  11. bool saveGame(SaveFormat saveFormat) const;
  12. void read(const QJsonObject &json);
  13. void write(QJsonObject &json) const;
  14. void print(int indentation = 0) const;
  15. private:
  16. Character mPlayer;
  17. QVector<Level> mLevels;
  18. };

首先定义了SaveFormat的枚举,可以选择存储为Json或者二进制。提供了玩家和关卡相关的成员变量,这里官方给出了下面3个函数的说明:newGame(),saveGame(),loadGame(),这里read()和write()函数被saveGame()和loadGame()使用。

  1. void Game::newGame()
  2. {
  3. mPlayer = Character();
  4. mPlayer.setName(QStringLiteral("Hero"));
  5. mPlayer.setClassType(Character::Archer);
  6. mPlayer.setLevel(QRandomGenerator::global()->bounded(15, 21));
  7. mLevels.clear();
  8. mLevels.reserve(2);
  9. Level village(QStringLiteral("Village"));
  10. QVector<Character> villageNpcs;
  11. villageNpcs.reserve(2);
  12. villageNpcs.append(Character(QStringLiteral("Barry the Blacksmith"),
  13. QRandomGenerator::global()->bounded(8, 11),
  14. Character::Warrior));
  15. villageNpcs.append(Character(QStringLiteral("Terry the Trader"),
  16. QRandomGenerator::global()->bounded(6, 8),
  17. Character::Warrior));
  18. village.setNpcs(villageNpcs);
  19. mLevels.append(village);
  20. Level dungeon(QStringLiteral("Dungeon"));
  21. QVector<Character> dungeonNpcs;
  22. dungeonNpcs.reserve(3);
  23. dungeonNpcs.append(Character(QStringLiteral("Eric the Evil"),
  24. QRandomGenerator::global()->bounded(18, 26),
  25. Character::Mage));
  26. dungeonNpcs.append(Character(QStringLiteral("Eric's Left Minion"),
  27. QRandomGenerator::global()->bounded(5, 7),
  28. Character::Warrior));
  29. dungeonNpcs.append(Character(QStringLiteral("Eric's Right Minion"),
  30. QRandomGenerator::global()->bounded(4, 9),
  31. Character::Warrior));
  32. dungeon.setNpcs(dungeonNpcs);
  33. mLevels.append(dungeon);
  34. }

初始化函数设置了玩家目前在哪个关卡和相关属性以及NPC。

  1. void Game::read(const QJsonObject &json)
  2. {
  3. if (json.contains("player") && json["player"].isObject())
  4. mPlayer.read(json["player"].toObject());
  5. if (json.contains("levels") && json["levels"].isArray()) {
  6. QJsonArray levelArray = json["levels"].toArray();
  7. mLevels.clear();
  8. mLevels.reserve(levelArray.size());
  9. for (int levelIndex = 0; levelIndex < levelArray.size(); ++levelIndex) {
  10. QJsonObject levelObject = levelArray[levelIndex].toObject();
  11. Level level;
  12. level.read(levelObject);
  13. mLevels.append(level);
  14. }
  15. }
  16. }

read()函数填充mPlayer相关的数据,然后清空关卡然后重置下。

通过读取QJsonArray读取每个关卡的数据。

  1. void Game::write(QJsonObject &json) const
  2. {
  3. QJsonObject playerObject;
  4. mPlayer.write(playerObject);
  5. json["player"] = playerObject;
  6. QJsonArray levelArray;
  7. for (const Level &level : mLevels) {
  8. QJsonObject levelObject;
  9. level.write(levelObject);
  10. levelArray.append(levelObject);
  11. }
  12. json["levels"] = levelArray;
  13. }

这里的write和上面说的一样。

  1. bool Game::loadGame(Game::SaveFormat saveFormat)
  2. {
  3. QFile loadFile(saveFormat == Json
  4. ? QStringLiteral("save.json")
  5. : QStringLiteral("save.dat"));
  6. if (!loadFile.open(QIODevice::ReadOnly)) {
  7. qWarning("Couldn't open save file.");
  8. return false;
  9. }
  10. QByteArray saveData = loadFile.readAll();
  11. QJsonDocument loadDoc(saveFormat == Json
  12. ? QJsonDocument::fromJson(saveData)
  13. : QJsonDocument::fromBinaryData(saveData));
  14. read(loadDoc.object());
  15. QTextStream(stdout) << "Loaded save for "
  16. << loadDoc["player"]["name"].toString()
  17. << " using "
  18. << (saveFormat != Json ? "binary " : "") << "JSON...\n";
  19. return true;
  20. }

加载游戏的关键是存储的什么格式的文件,是save.json还是save.dat这个文件

QJsonDocument可以通过使用QJsonDocument::fromJson()和QJsonDocument::fromBinaryData()读取数据。成功就返回true。

  1. bool Game::saveGame(Game::SaveFormat saveFormat) const
  2. {
  3. QFile saveFile(saveFormat == Json
  4. ? QStringLiteral("save.json")
  5. : QStringLiteral("save.dat"));
  6. if (!saveFile.open(QIODevice::WriteOnly)) {
  7. qWarning("Couldn't open save file.");
  8. return false;
  9. }
  10. QJsonObject gameObject;
  11. write(gameObject);
  12. QJsonDocument saveDoc(gameObject);
  13. saveFile.write(saveFormat == Json
  14. ? saveDoc.toJson()
  15. : saveDoc.toBinaryData());
  16. return true;
  17. }

存储游戏和loadGame()很像通过官方封装好的QJsonDocument::toJson()和JSonDocument::toBinarayData()存储,一个是读Json,一个是读二进制。

下面是main()函数:

  1. int main(int argc, char *argv[])
  2. {
  3. QCoreApplication app(argc, argv);
  4. QStringList args = QCoreApplication::arguments();
  5. bool newGame = true;
  6. if (args.length() > 1)
  7. newGame = (args[1].toLower() != QStringLiteral("load"));
  8. bool json = true;
  9. if (args.length() > 2)
  10. json = (args[2].toLower() != QStringLiteral("binary"));
  11. Game game;
  12. if (newGame)
  13. game.newGame();
  14. else if (!game.loadGame(json ? Game::Json : Game::Binary))
  15. return 1;
  16. // Game is played; changes are made...
  17. QTextStream(stdout) << "Game ended in the following state:\n";
  18. game.print();
  19. if (!game.saveGame(json ? Game::Json : Game::Binary))
  20. return 1;
  21. return 0;
  22. }

发表评论

表情:
评论列表 (有 0 条评论,245人围观)

还没有评论,来说两句吧...

相关阅读