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

程序运行截图如下:

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

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

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

 

Character类

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

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

class Character
  {
      Q_GADGET;

  public:
      enum ClassType {
          Warrior, Mage, Archer
      };
      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);

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

      void print(int indentation = 0) const;
  private:
      QString mName;
      int mLevel;
      ClassType mClassType;
  };

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

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());
  }

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

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

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

下面是Level类:

  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;
  };

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

void Level::read(const QJsonObject &json)
  {
      if (json.contains("name") && json["name"].isString())
          mName = json["name"].toString();

      if (json.contains("npcs") && json["npcs"].isArray()) {
          QJsonArray npcArray = json["npcs"].toArray();
          mNpcs.clear();
          mNpcs.reserve(npcArray.size());
          for (int npcIndex = 0; npcIndex < npcArray.size(); ++npcIndex) {
              QJsonObject npcObject = npcArray[npcIndex].toObject();
              Character npc;
              npc.read(npcObject);
              mNpcs.append(npc);
          }
      }
  }

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

  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;
  }

这个是写函数。

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

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;
  };

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

 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);
  }

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

 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);
          }
      }
  }

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

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

 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;
  }

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

  bool Game::loadGame(Game::SaveFormat saveFormat)
  {
      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));

      read(loadDoc.object());

      QTextStream(stdout) << "Loaded save for "
                          << loadDoc["player"]["name"].toString()
                          << " using "
                          << (saveFormat != Json ? "binary " : "") << "JSON...\n";
      return true;
  }

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

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

 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;
  }

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

下面是main()函数:

 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;
  }

 

相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页