💾 JSON Game Data #
In this post, we look at parsing JSON game data using the C++ nlohmann/json library, written in C++11. I am working on a clone of the Arkanoid arcade
from the 1980s, and decided using a JSON file was the best way to store and load
game configuration data. Here, I run through my first impressions of the library.
Although the focus is a game that uses raylib for rendering, the code has wider applications in game code as well as wider C++ applications.
This is a learning in public post, so I would love to hear your opinion, especially if there is something here I might be able to improve on.
🧱 What we’re Building #
I have been working on a C++ clone of the Arkanoid arcade game using raylib for rendering. In the original game, the player has to destroy rows of bricks at the top of the screen. The rows are populated with bricks of different colours, and destroying bricks of some colours earns more point than others. Most bricks take a single hit to destroy, though silver bricks need two hits and gold bricks are indestructible.

Taking those scoring details and maintainability into account, I decided to
put the brick properties into a JSON file loaded on setup. I picked the nlohmann/json library to parse JSON. There are other C++ JSON libraries available , and I settled on this one mostly because it looked popular and has good
CMake integration.
I am loading JSON at initialization, and the JSON config file is small, so speed was not at the top of my wish list. If you are concerned about speed, for your project though, see these benchmarks comparing C and C++ JSON libraries .
In the rest of the post, we take a quick look at eh JSON file, the CMake nlohmann/json config and then, some code snippets to see the library in action. You will find
a link, further down, to the project repo, which has full code examples.
JSON Config File #
I placed the file in an assets directory at the
root of the project:
{ "bricks": [ { "colour": "white", "value": 50 }, { "colour": "orange", "value": 60 }, { "colour": "cyan", "value": 70 }, { "colour": "green", "value": 80 }, { "colour": "red", "value": 90 }, { "colour": "blue", "value": 100 }, { "colour": "magenta", "value": 110 }, { "colour": "yellow", "value": 120 }, { "colour": "silver", "value": 50, "hitsToDestroy": 2 }, { "colour": "gold", "value": 0, "hitsToDestroy": 999 } ], "levelBricks": [ { "level": 1, "rows": ["silver", "red", "blue", "orange", "magenta", "green"] } ]}
nlohmann/json supports importing data with default
values, so although I included hitsToDestroy values
for silver and gold bricks, I omitted the value for the other coloured bricks.
For those I will import with a default value of 1.
⚙️ CMake nlohmann/json Config #
nlohmann/json Config #
The CMake integration was quite straightforward. I use CPM (instead of CMake FetchContent) in the project to manage third-party libraries, and the CPM repo includes an nlohmann/json example .
1 include(cmake/CPM.cmake)2 cpmaddpackage("gh:nlohmann/json#9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03"3 )# v3.11.34 5 add_library(FileSystem_lib STATIC "${PROJECT_SOURCE_DIR}/src/file_system.cpp")6 set_target_properties(FileSystem_lib PROPERTIES CXX_CLANG_TIDY7 "${CLANG_TIDY_COMMAND}")8 target_link_libraries(9 FileSystem_lib10 nlohmann_json::nlohmann_json11 arkanoid_compiler_flags)12 target_compile_definitions(13 FileSystem_lib PUBLIC ASSETS_PATH="${CMAKE_CURRENT_SOURCE_DIR}/assets/")
Observations:
-
You can download the CPM.cmake file from the CPM repo , and place it in a project
cmakedirectory. -
With CPM you can pick a tag from a repo, just using its tag name, though I
prefer to use commit hashes, as above (line
2). -
I included the
ASSETS_PATHdefinition in line12-13to make the path available in source code (see examples, further down).
🖥️ Adding JSON parsing code #
I am used to serde for parsing JSON in Rust , which has quite detailed type checking. nlohmann/json is a little more relaxed in this regard, though I suppose an advantage is that
you can get going quicker, and it might suit workflows with high levels of iteration.
Parsing JSON Game Data #
1 #include "file_system.h"2 3 #include <nlohmann/json.hpp>4 #include <nlohmann/json_fwd.hpp>5 #include <spdlog/spdlog.h>6 7 #include <cstddef>8 #include <fstream>9 #include <map>10 #include <stdexcept>11 #include <string>12 #include <string_view>13 14 15 void parse_level_bricks_file(std::string_view json_path,16 nlohmann::json &json_data)17 {18 std::ifstream json_file{json_path.data()};19 if (!json_file)20 {21 spdlog::error("Unable to open {} for reading.", json_path);22 }23 24 try25 {26 json_data = nlohmann::json::parse(json_file);27 }28 catch (nlohmann::json::parse_error &exception)29 {30 spdlog::error("Error parsing JSON from `{}`: {}.",31 json_path,32 exception.what());33 }34 }
To parse the JSON file, I just called nlohmann::json::parse(), passing in the file std::ifstream. That
function returns an nlohmann::json object with
the JSON data that has convenient access methods (see following section). I used
the spdlog library for logging.
The nlohmann/json parse function throws an exception if it encounters an unexpected token in the JSON or encounters an issue in converting
input bytes to Unicode (the library has full UTF-8 support and partial UTF-16 support).
Using the ASSETS_PATH variable set in the CMakeLists.txt file above, I can create a variable for the path to my JSON data file (assets/level_bricks.json), in C++ source code, thus:
const std::string json_path{ASSETS_PATH "level_bricks.json"};
Reading Parsed Game Data #
There are three common methods for accessing JSON data returned by the nlohmann:json::parse function:
-
data["KEY"]for unchecked access, -
data.at("KEY")for checked access; and -
data.value("KEY", <default_value>)to return the provided default value when the key is absent.
Unchecked Element Access #
The first, like the std::map accessor can be used
to mutate the JSON data. It returns null if the
key is absent.
1 const int level_1_rows{2 static_cast<int>(json_data["levelBricks"][0]["rows"].size())};3 if (level_1_rows == 0)4 {5 spdlog::error("Error in JSON data from `{}`: expected at least one row "6 "of bricks in levelBricks[0].rows.",7 json_path);8 }
I used the [] operator here to access the root
level levelBricks array, then again, to index into
the rows field of the array’s first element.
You also see the size method in use here, which
will return the length of a JSON array, the number of fields in a JSON object or
1, if it is called on a scalar. It returns 0 if called on a null nlohmann::json object, which happens if you access
a field that does not exist in the parsed data.
Checked Element Access #
1 const auto bricks_json_data = json_data["bricks"];2 3 for (const auto &element : bricks_json_data)4 {5 try6 {7 const std::string colour_field{element.at("colour")};8 // TRUNCATED...9 }10 catch (const std::out_of_range & /* exception */)11 {12 spdlog::error(13 "Error in JSON data from `{}`: unknown BrickType: '{}'.",14 json_path,15 std::string(element));16 throw exception;17 }18 }
This time I use checked access, via the at method
(in line 7). This throws an exception if the colour field is missing. The library also provides a C++11 range-based loop access
to elements, as used in line 3.
Value or Default Element Access #
Finally, we have value or default access. For the game, most bricks only need
one hit to be destroyed, and the silver needs two, while the gold brick is
indestructible. Rather than add a value for this field for all bricks, I
decided to set the value to 1, as a default
when it is absent.
1 const auto bricks_json_data = json_data["bricks"];2 3 for (const auto &element : bricks_json_data)4 {5 const int hits_to_destroy{element.value("hitsToDestroy", 1)};6 // TRUNCATED...7 }
The value member on the nlohmann::json object (used in line 5) does exactly what I
am looking for here. Of course, it works similarly for string fields, for
example.
Please enable JavaScript to watch the video 📼
🗳 Poll #
🙌🏽 Parsing JSON Game Data: Wrapping Up #
In this post on parsing JSON game data, we saw how you might use nlohmann/json to load data in a C++ game. More specifically, we saw:
-
how to set up
nlohmann/jsonCMake config; - the main JSON data object access methods; and
- parsing and other useful methods provided by the library.
I hope you found this useful. As promised, you can get the full project code on the Rodney Lab GitHub repo . Do let me know if you found this content useful and would like to see more similar content. Also reach out if there is anything I could improve to provide a better experience for you.
🙏🏽 Parsing JSON Game Data: Feedback #
If you have found this post useful, see links below for further related content on this site. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on X, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.
Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on X (previously Twitter) and also, join the #rodney Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Game Dev as well as Rust and C++ (among other topics). Also, subscribe to the newsletter to keep up-to-date with our latest projects.

