While working on a trading card game one day, I realized that I was going to need a system that would allow me to dynamically create cards from a spreadsheet. Spreadsheets are great for balancing and sorting large amounts of data, such as a deck of cards, but getting them into something more usable is a bit of a challenge. Originally I was going to build a system to pull in XML files, but I discovered that GameMaker is able to encode/decode JSON files which is a really good alternative and has the benefit of being built of arrays (specifically lists and maps). If you are reading this, I am going to assume that, like me, you have struggled to figure out how JSON files actually work and make them useful. Well, now that I have done all the hard work, let us walk through the process of creating cards dynamically through JSON file data!
Understanding JSON
JavaScript Object Notation, JSON, files is a human readable, open standard format document that is easily parsed by computers. It is comprised of two types of data structures that GameMaker users should be familiar with, lists and maps, and looks like the code below. When you see braces { } it indicates a list item that can be accessed. Inside of those braces, you can see a key / value format of a map. The best thing about this system is that these can be nested, so you can have a map in a list in a map in a list just like Facebook does!
[
{
“ID”:1,
“NAME”:”Big Sun”,
“RARITY”:1,
“ATK”:4,
“DEF”:1,
“IMAGE”:”spr_BigSun”,
“FLAVOR”:”Here comes the Sun!”
},
{
“ID”:2,
“NAME”:”Bunny”,
“RARITY”:1,
“ATK”:2,
“DEF”:2,
“IMAGE”:”spr_Bunny”,
“FLAVOR”:”Cute but deadly”
}
]
Of course everything isn’t perfect. Probably the biggest drawback to using JSON files is the creation of the file itself, as most spreadsheet editor do NOT directly export to it. This means that there will be a step or two between making a spreadsheet and having a usable JSON file (XML has the same issue). My currently methodology for making this file is to build the spreadsheet (either in Excel or Google Docs) and save it as a CSV file and then convert it ( this works well ). In the future I will want to find a way to automate this, such as this App Script, but that’s not why we are here. The only really important thing to remember is to have the first row of the spreadsheet be the name for each column. My preference is to have these names written in ALL CAPS, which will make it easier to code later when I use macros (also ALL CAPS).
The Design
For this tutorial we will build a simple system that will open a JSON file, read the data and place it into a ds_grid, and then use the info to create a card made up of several components which we will spawn on command. To start, we will need to use some Macros to help make our code more readable.
ID | 0 |
NAME | 1 |
RARITY | 2 |
DEF | 3 |
ATK | 4 |
IMAGE | 5 |
FLAVOR | 6 |
CARD_HEIGHT | 320 |
CARD_WIDTH | 240 |
CARD_CENTER | 120 |
IMAGESLOT | 100 |
NAMESLOT | 180 |
FLAVORSLOT | 240 |
CIRCLE | 291 |
OFFSET | 30 |
Creating the Card
The first thing we will build is the card. Every Trading Card Game has it’s own take on what information is important to the game, but most have a few common attributes which we will use: Attack (ATK), Defence (DEF), Rarity, Name, Flavor, and of course, the Image. We will also have an ID for each card, which is useful for organizational purposes. Let’s start by creating a card object, obj_Card, and initialize some variables in the Create event.
myID = 0;
myName = “Default”;
myImage = spr_Default;
myRarity = 0;
myDEF = 0;
myATK = 0;
myFlavor = “Bad Text”;
Now that we have the variables, we can draw all of necessary components for the card. We start by drawing the card background, followed by the text, image, the border and the stats. Once we have that, the card is ready to be used!
draw_sprite(spr_CardBack, 0, x, y);
//Text
draw_set_colour(c_white);
draw_set_valign(fa_middle);
draw_set_halign(fa_center);
draw_set_font(fnt_Name);
draw_text(x + CARD_CENTER, y + NAMESLOT, myName);
draw_set_font(fnt_Flavor);
draw_text(x + CARD_CENTER, y + FLAVORSLOT, string(myFlavor));
//Image
draw_sprite(mySprite, 0, x + CARD_CENTER, y + IMAGESLOT);
//Rarity
draw_sprite(spr_Border, myRarity, x, y);
//Stats
draw_set_font(fnt_Stats);
draw_sprite(spr_ATKCircle, 0, x, y + CIRCLE);
draw_text(x + OFFSET, y + CIRCLE, myATK);
draw_sprite(spr_DEFCircle, 0, x + CARD_WIDTH, y + CIRCLE);
draw_text(x + CARD_WIDTH – OFFSET, y + CIRCLE, myDEF);
Importing JSON into GameMaker
There is a JSON file (cardAttributes.json) that can be found in the project’s Included Files that we can use to keep things simple, though we could easily be pulling the file from a server or other location. Once we have a file, we need to load the data into GameMaker and then utilize it.
var theJsonFile = file_text_open_read(“cardAttributes.json”);
var theData = “”;
while (!file_text_eof(theJsonFile))
{
theData += file_text_read_string(theJsonFile);
file_text_readln(theJsonFile);
}
file_text_close(theJsonFile);
The most confusing part I found when loading the text file was understanding how text is read into the program. Text is only read line by line, so if you just try and read the JSON file all you will get is a [. When you try and decode it, you won’t get an error as the ds_map will have an entry, it just doesn’t have everything you want. Therefore, we run a while loop to go through the entire file and read each line into a single string variable. With the text now read, we can decode it!
var theJsonMap = json_decode(theData);
var theList = ds_map_find_value(theJsonMap, “default”);
global.totalCards = ds_list_size(theList);
global.cardData = ds_grid_create(global.totalCards,7);
for (var i = 0; i < global.totalCards; i++)
{
var theEntry = ds_list_find_value(theList, i);
global.cardData[# i, ID] = theEntry[? “ID”];
global.cardData[# i, NAME] = theEntry[? “NAME”];
global.cardData[# i, RARITY] = theEntry[? “RARITY”];
global.cardData[# i, DEF] = theEntry[? “DEF”];
global.cardData[# i, ATK] = theEntry[? “ATK”];
global.cardData[# i, IMAGE] = theEntry[? “IMAGE”];
global.cardData[# i, FLAVOR] = theEntry[? “FLAVOR”];
}
First we decode the file using the json_decode function that returns a ds_map. We then search in the map for “default” which will give us the ds_list of entries. For all intents and purposes, we could probably have grabbed the first entry (or last) as there was likely just one entry in the map, but better to be safe and grab the one we know will have the data. Now that we have the list, we can create a ds_grid that can hold every card and has the seven attributes we need. Both the list and the grid are global variables as we will want to be able to use this info throughout the game.We run a for loop and (using accessors) transfer the map values into the grid. This is where Macros come in handy! We will want this script run at the start of the game, so put it into the Create event of an Overlord object, which should be the only object in the room (for this tutorial).
Spawning A Card
Now all we need to do is spawn the card, which is easily done. Instantiate a Card and then choose a row of the grid to pull the card data from. Since everything is coming from a string, the only real worry is dealing with the image. For this we can use the asset_get_index function which lets you look for an asset in the resource tree from a string. This script can be attached to whatever you want, but for this tutorial, just attach it to a Key Release event for the Spacebar.
var theCard = instance_create(320-CARD_CENTER,32,obj_Card);
var i = floor(random(global.totalCards));
theCard.myID = global.cardData[# i, ID];
theCard.myName = global.cardData[# i, NAME];
theCard.mySprite = asset_get_index(global.cardData[# i, IMAGE]);
theCard.myRarity = global.cardData[# i, RARITY];
theCard.myDEF = global.cardData[# i, DEF];
theCard.myATK = global.cardData[# i, ATK];
theCard.myFlavor = global.cardData[# i, FLAVOR];
theCard.depth = -theCard.id – 100000;
The last line here is setting the depth of the card, which is necessary because GameMaker may not draw the next card on TOP of the previous ones.
Conclusion
Now that everything works, you should have a decent familiarity with how to access JSON files. The formatting may have been a bit confusing at first, but now that you can see that it is just maps with lists with maps and lists, you should be able to create very interesting databases or even navigate Facebook’s data. You also now understand how to read an entire file of text and how to refer to assets via strings!
Love it
I used json to store data in one of my games. Once a system for saving the data is set up, the rest is easy 🙂