summaryrefslogtreecommitdiffstats
path: root/examples/corelib/json/savegame/doc/src/savegame.qdoc
blob: a3eaa50433020b85cab94ea7c21b88f0b304a43a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: http://www.gnu.org/copyleft/fdl.html.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example json/savegame
    \title JSON Save Game Example

    \brief The JSON Save Game example demonstrates how to save and load a
    small game using QJsonDocument, QJsonObject and QJsonArray.

    Many games provide save functionality, so that the player's progress through
    the game can be saved and loaded at a later time. The process of saving a
    game generally involves serializing each game object's member variables
    to a file. Many formats can be used for this purpose, one of which is JSON.
    With QJsonDocument, you also have the ability to serialize a document in a
    binary format, which is great if you don't want the save file to be
    readable, or if you need to keep the file size down.

    In this example, we'll demonstrate how to save and load a simple game to
    and from JSON and binary formats.

    \section1 The Character Class

    The Character class represents a non-player character (NPC) in our game, and
    stores the player's name, level, and class type.

    It provides read() and write() functions to serialise its member variables.

    \snippet json/savegame/character.h 0

    Of particular interest to us are the read and write function
    implementations:

    \snippet json/savegame/character.cpp 0

    In the read() function, we assign Character's members values from the
    QJsonObject argument. You can use either \l QJsonObject::operator[]() or
    QJsonObject::value() to access values within the JSON object; both are
    const functions and return QJsonValue::Undefined if the key is invalid. We
    could check if the keys are valid before attempting to read them with
    QJsonObject::contains(), but we assume that they are.

    \snippet json/savegame/character.cpp 1

    In the write() function, we do the reverse of the read() function; assign
    values from the Character object to the JSON object. As with accessing
    values, there are two ways to set values on a QJsonObject:
    \l QJsonObject::operator[]() and QJsonObject::insert(). Both will override
    any existing value at the given key.

    Next up is the Level class:

    \snippet json/savegame/level.h 0

    We want to have several levels in our game, each with several NPCs, so we
    keep a QList of Character objects. We also provide the familiar read() and
    write() functions.

    \snippet json/savegame/level.cpp 0

    Containers can be written and read to and from JSON using QJsonArray. In our
    case, we construct a QJsonArray from the value associated with the key
    \c "npcs". Then, for each QJsonValue element in the array, we call
    toObject() to get the Character's JSON object. The Character object can then
    read their JSON and be appended to our NPC list.

    \note \l{Container Classes}{Associate containers} can be written by storing
    the key in each value object (if it's not already). With this approach, the
    container is stored as a regular array of objects, but the index of each
    element is used as the key to construct the container when reading it back
    in.

    \snippet json/savegame/level.cpp 1

    Again, the write() function is similar to the read() function, except
    reversed.

    Having established the Character and Level classes, we can move on to
    the Game class:

    \snippet json/savegame/game.h 0

    First of all, we define the \c SaveFormat enum. This will allow us to
    specify the format in which the game should be saved: \c Json or \c Binary.

    Next, we provide accessors for the player and levels. We then expose three
    functions: newGame(), saveGame() and loadGame().

    The read() and write() functions are used by saveGame() and loadGame().

    \snippet json/savegame/game.cpp 0

    To setup a new game, we create the player and populate the levels and their
    NPCs.

    \snippet json/savegame/game.cpp 1

    The first thing we do in the read() function is tell the player to read
    itself. We then clear the levels list so that calling loadGame() on the same
    Game object twice doesn't result in old levels hanging around.

    We then populate the level list by reading each Level from a QJsonArray.

    \snippet json/savegame/game.cpp 2

    We write the game to JSON similarly to how we write Level.

    \snippet json/savegame/game.cpp 3

    When loading a saved game in loadGame(), the first thing we do is open the
    save file based on which format it was saved to; \c "save.json" for JSON,
    and \c "save.dat" for binary. We print a warning and return \c false if the
    file couldn't be opened.

    Since QJsonDocument's \l{QJsonDocument::fromJson()}{fromJson()} and
    \l{QJsonDocument::fromBinaryData()}{fromBinaryData()} functions both take a
    QByteArray, we can read the entire contents of the save file into one,
    regardless of the save format.

    After constructing the QJsonDocument, we instruct the Game object to read
    itself and then return \c true to indicate success.

    \snippet json/savegame/game.cpp 4

    Not surprisingly, saveGame() looks very much like loadGame(). We determine
    the file extension based on the format, print a warning and return \c false
    if the opening of the file fails. We then write the Game object to a
    QJsonDocument, and call either QJsonDocument::toJson() or to
    QJsonDocument::toBinaryData() to save the game, depending on which format
    was specified.

    We are now ready to enter main():

    \snippet json/savegame/main.cpp 0

    Since we're only interested in demonstrating \e serialization of a game with
    JSON, our game is not actually playable. Therefore, we only need
    QCoreApplication and have no event loop. We create our game and assume that
    the player had a great time and made lots of progress, altering the internal
    state of our Character, Level and Game objects.

    \snippet json/savegame/main.cpp 1

    When the player has finished, we save their game. For demonstration
    purposes, we serialize to both JSON and binary. You can examine the contents
    of the files in the same directory as the executable, although the binary
    save file will contain some garbage characters (which is normal).

    To show that the saved files can be loaded again, we call loadGame() for
    each format, returning \c 1 on failure. Assuming everything went well, we
    return \c 0 to indicate success.

    That concludes our example. As you can see, serialization with Qt's JSON
    classes is very simple and convenient. The advantages of using QJsonDocument
    and friends over QDataStream, for example, is that you not only get
    human-readable JSON files, but you also have the option to use a binary
    format if it's required, \e without rewriting any code.

    \sa {JSON Support in Qt}, {Data Storage}
*/