Json decode help [Solved!]

kupo15

Member
I'm having a tough time figuring out json decoding. I've somewhat grasped whats going on but I'm getting an error

___________________________________________
############################################################################################
ERROR in
action number 1
of Create Event
for object object0:

Data structure with index does not exist.
at gml_Object_object0_CreateEvent_1 (line 3) - var size = ds_list_size(list);
############################################################################################

when I show_message list it displays the name of the json file which seems odd to me.

All I'm trying to do is first figure out how to parse a json into something usable like an array like what's in the documentation
https://docs.yoyogames.com/source/dadiospice/002_reference/file handling/json_decode.html

Here is the gmz with two included json files. Texturepacker has an array and hash version of json, I'm not sure what the difference is.

Any help would be appreciated. Thanks

https://www.dropbox.com/s/kj2zl4uhe07p9qy/json.gmz?dl=0

@FrostyCat
 

Tsa05

Member
Wellp, there's line 1: var resultMap = json_decode("array.json");
json_decode() accepts a string, and it looks like you're handing it a filename.
Of course, file names are strings, so...it created a new ds_map and added "array.json" to it. Then it was done.

You need to get a json string into that function. You can use the file_text_read/open/readln functions to get your string out of the file/ once you have that string, you're in good shape.
It looks like you expect the json to contain a list with no key ("Default"); should work, though I do this stuff in GMS2--hopefully the same.
One thing you probably want to look into for crash-proofing is a few is_undefined(variable) statements and a few ds_exists(variable, ds_type_list) statements ;D
 

kupo15

Member
Wellp, there's line 1: var resultMap = json_decode("array.json");
json_decode() accepts a string, and it looks like you're handing it a filename.
Of course, file names are strings, so...it created a new ds_map and added "array.json" to it. Then it was done.

You need to get a json string into that function. You can use the file_text_read/open/readln functions to get your string out of the file/ once you have that string, you're in good shape.
It looks like you expect the json to contain a list with no key ("Default"); should work, though I do this stuff in GMS2--hopefully the same.
One thing you probably want to look into for crash-proofing is a few is_undefined(variable) statements and a few ds_exists(variable, ds_type_list) statements ;D
Haha that's hilarious and lines up with what the result was! Do I have to loop through the entire json to create the string like Jason Elliot described in his tutorial?

Code:
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);
http://jasonleeelliott.com/using-json-data/

I assume I can't just do

var json file_text_open_read("array.json")
var map = json_decode(string(json));

And be on my way from there, can I?
 

Tsa05

Member
Correct, you've got to first get the string out of the file. file_text_open_read() returns an index number that corresponds to the file you want to read, so you've got to pull the string out.

If the file contains just a json string, then you read the line and pass it into json_decode(). I've got files like this:

I just open the file, read the first line (which is one huge json line) and decode it. The result is a whole data structure; a ds_map containing lists and key-value pairings.
To be rigorous, though, yes, you can do a while loop until the end of the file, appending stuff together. That's useful in cases where you might have human-edited files with accidental extra line breaks.
 

kupo15

Member
Correct, you've got to first get the string out of the file. file_text_open_read() returns an index number that corresponds to the file you want to read, so you've got to pull the string out.

If the file contains just a json string, then you read the line and pass it into json_decode(). I've got files like this:

I just open the file, read the first line (which is one huge json line) and decode it. The result is a whole data structure; a ds_map containing lists and key-value pairings.
To be rigorous, though, yes, you can do a while loop until the end of the file, appending stuff together. That's useful in cases where you might have human-edited files with accidental extra line breaks.
Oh cool, so it sounds like I don't have to run a while loop like in that example after all because that tutorial built their own custom json with excel. My json is computer generated.

So unless I'm not understanding, I need to do:?

var json file_text_open_read("array.json") // open the file
var data = file_text_read_string(json) // pull out the json string and store into data
var map = json_decode(string(json)); // decode the json string
***close the json file afterwards
 

Tsa05

Member
Almost.
In your code sample, the json string is pulled out of a file and stored in the variable data.
So, pass data into the decode function to produce the data structures you needed.

Code:
var json = file_text_open_read("array.json") // open the file
var data = file_text_read_string(json) // pull out the json string and store into data
file_text_close(json);

var map = json_decode(data); // decode the json string
 

kupo15

Member
Almost.
In your code sample, the json string is pulled out of a file and stored in the variable data.
So, pass data into the decode function to produce the data structures you needed.

Code:
var json = file_text_open_read("array.json") // open the file
var data = file_text_read_string(json) // pull out the json string and store into data
file_text_close(json);

var map = json_decode(data); // decode the json string
Oops that's what I meant to do haha

Sweet thanks man! I'm looking forward to trying this out when I get home tonight!
 

kupo15

Member
@Tsa05
Code:
var json = file_text_open_read("array.json");
var data = file_text_read_string(json);
file_text_close(json);

var resultmap = json_decode(data);

show_message(resultmap)
Got an error because the decoding failed. The show_message line showed a -1
 

2Dcube

Member
Can you paste what's inside the data variable?
You can do
Code:
clipboard_set_text(data)
at the end of your code to copy it to your clipboard. (Assuming you are running the game on Windows).

EDIT
Ok, I saw the "array.json" file. The data is not on 1 line, so you need to read each line.
Currently, you only read the first line, so the variable data probably holds '{"frames": {', which is not much!
To read each line:
Code:
var data = "";
var json = file_text_open_read("array.json");
while(!file_text_eof(json))
{
   data += file_text_read_string(json);
   file_text_readln();
}
file_text_close(json);

var resultmap = json_decode(data);
 
Last edited:

kupo15

Member
Can you paste what's inside the data variable?
You can do
Code:
clipboard_set_text(data)
at the end of your code to copy it to your clipboard. (Assuming you are running the game on Windows).

EDIT
Ok, I saw the "array.json" file. The data is not on 1 line, so you need to read each line.
Currently, you only read the first line, so the variable data probably holds '{"frames": {', which is not much!
To read each line:
Code:
var data = "";
var json = file_text_open_read("array.json");
while(!file_text_eof(json))
{
   data += file_text_read_string(json);
   file_text_readln();
}
file_text_close(json);

var resultmap = json_decode(data);
Oh so I DO have to loop through it all haha man this is so confusing :p I'll give that a shot tonight and thanks for the clipboard function, I'll have to check that out as well
 

klys

Member
Last edited:

klys

Member
Correct, you've got to first get the string out of the file. file_text_open_read() returns an index number that corresponds to the file you want to read, so you've got to pull the string out.

If the file contains just a json string, then you read the line and pass it into json_decode(). I've got files like this:

I just open the file, read the first line (which is one huge json line) and decode it. The result is a whole data structure; a ds_map containing lists and key-value pairings.
To be rigorous, though, yes, you can do a while loop until the end of the file, appending stuff together. That's useful in cases where you might have human-edited files with accidental extra line breaks.

A common pitfall when using json on GML is to saving all the information as it is, but in GML is better to save everything as a string and then when getting the information from the json transforming from string to they respective value type.

For that you can use

Code:
string(number)
doc : https://docs.yoyogames.com/source/dadiospice/002_reference/strings/string.html

Code:
real(string)
doc: https://docs.yoyogames.com/source/dadiospice/002_reference/strings/real.html
 

kupo15

Member
Can you paste what's inside the data variable?
You can do
Code:
clipboard_set_text(data)
at the end of your code to copy it to your clipboard. (Assuming you are running the game on Windows).

EDIT
Ok, I saw the "array.json" file. The data is not on 1 line, so you need to read each line.
Currently, you only read the first line, so the variable data probably holds '{"frames": {', which is not much!
To read each line:
Code:
var data = "";
var json = file_text_open_read("array.json");
while(!file_text_eof(json))
{
   data += file_text_read_string(json);
   file_text_readln();
}
file_text_close(json);

var resultmap = json_decode(data);
Getting some progress here, thanks! Here is what data is before it gets decoded (sorry the format sucks)

Code:
{"frames": {"IDLE TRANSITION00000.png":{    "frame": {"x":209,"y":313,"w":224,"h":326},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":200,"y":70,"w":224,"h":326},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00001.png":{    "frame": {"x":1,"y":313,"w":206,"h":315},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":208,"y":79,"w":206,"h":315},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00002.png":{    "frame": {"x":183,"y":1,"w":220,"h":310},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":209,"y":85,"w":220,"h":310},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00003.png":{    "frame": {"x":405,"y":1,"w":208,"h":310},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":212,"y":86,"w":208,"h":310},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00004.png":{    "frame": {"x":1,"y":1,"w":180,"h":308},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":242,"y":88,"w":180,"h":308},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00005.png":{    "frame": {"x":615,"y":1,"w":165,"h":321},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":262,"y":77,"w":165,"h":321},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00006.png":{    "frame": {"x":1,"y":630,"w":195,"h":336},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":262,"y":63,"w":195,"h":336},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00007.png":{    "frame": {"x":381,"y":651,"w":195,"h":339},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":262,"y":58,"w":195,"h":339},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00008.png":{    "frame": {"x":198,"y":641,"w":181,"h":338},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":262,"y":59,"w":181,"h":338},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00009.png":{    "frame": {"x":435,"y":313,"w":173,"h":336},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":262,"y":62,"w":173,"h":336},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00010.png":{    "frame": {"x":610,"y":324,"w":173,"h":337},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":262,"y":60,"w":173,"h":337},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}},"IDLE TRANSITION00011.png":{    "frame": {"x":578,"y":663,"w":177,"h":343},    "rotated": false,    "trimmed": true,    "spriteSourceSize": {"x":262,"y":56,"w":177,"h":343},    "sourceSize": {"w":580,"h":400},    "pivot": {"x":0.5,"y":0.5}}},"meta": {    "app": "http://www.codeandweb.com/texturepacker",    "version": "1.0",    "image": "idletrans.png",    "format": "RGBA8888",    "size": {"w":784,"h":1007},    "scale": "1",    "smartupdate": "$TexturePacker:SmartUpdate:3cb3f1ffb1654e1d6eb6ef7b10d4f858:76dffcfef3e26e1fedc4eff93a6bb5f2:f0f95e7d692a8330104f0d1454fb1ba8$"}}
After decoding it the resultmap creates a map with only two things:
First is "frames"
Last is "meta"

No default or anything. Seems like all the data didn't really get decoded correct? Or does this note from the help file apply to me? I get an error with the list part after decoding
NOTE: When decoding arrays, there is a map with the key "default" ONLY when an array is the top level structure, and ONLY for that top-level array. Internal lists decode directly to ds_lists without being enclosed in a ds_map.
@klys Interesting, are you saying its better to do string(array.json) or what parts?

here is the full test Json file btw
Code:
{
"frames":
{
"IDLE TRANSITION00010.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":337,
"x":262,
"w":173,
"y":60,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":337,
"x":610,
"w":173,
"y":324,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00009.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":336,
"x":262,
"w":173,
"y":62,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":336,
"x":435,
"w":173,
"y":313,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00006.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":336,
"x":262,
"w":195,
"y":63,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":336,
"x":1,
"w":195,
"y":630,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00003.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":310,
"x":212,
"w":208,
"y":86,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":310,
"x":405,
"w":208,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00007.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":339,
"x":262,
"w":195,
"y":58,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":339,
"x":381,
"w":195,
"y":651,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00005.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":321,
"x":262,
"w":165,
"y":77,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":321,
"x":615,
"w":165,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00002.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":310,
"x":209,
"w":220,
"y":85,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":310,
"x":183,
"w":220,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00008.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":338,
"x":262,
"w":181,
"y":59,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":338,
"x":198,
"w":181,
"y":641,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00000.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":326,
"x":200,
"w":224,
"y":70,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":326,
"x":209,
"w":224,
"y":313,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00001.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":315,
"x":208,
"w":206,
"y":79,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":315,
"x":1,
"w":206,
"y":313,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00004.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":308,
"x":242,
"w":180,
"y":88,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":308,
"x":1,
"w":180,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00011.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":343,
"x":262,
"w":177,
"y":56,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":343,
"x":578,
"w":177,
"y":663,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
},
"meta":
{
"smartupdate":"$TexturePacker:SmartUpdate:3cb3f1ffb1654e1d6eb6ef7b10d4f858:76dffcfef3e26e1fedc4eff93a6bb5f2:f0f95e7d692a8330104f0d1454fb1ba8$",
"size":
{
"h":1007,
"w":784,
},
"version":"1.0",
"format":"RGBA8888",
"scale":"1",
"app":"http://www.codeandweb.com/texturepacker",
"image":"idletrans.png",
},
}


EDIT:

I get the feeling there is more work that needs to be done than just using json_decode here. Through more research it seems to me that json_decode is designed to decode simple and clear pairs of values

IE: {"name":"Decebal","surname":"Răzvan","gender":"male","region":"Romania"}

My entire json data is more complicated than that and has what appears to be arrays in there also so somehow I need to figure out how to divide the "data" string into chunks that the json_decode can do
 
Last edited:

kupo15

Member
I think I'm starting to understand it now. This particular JSON file doesn't have "default" as a key. The keys are essentially labels or headers of included data and this one has 2 chunks of data
"Frames" which includes all the data I need
"Meta" which includes other data I don't need so I can safely ignore that

Instead of searching for "default" like most instructions says to do, "Frames" should be storing all that data instead. So it seems like so far json_decode is performing correctly, just need to figure out how to extract the data from "frames" now

here is the full test Json file again
Code:
{
"frames":
{
"IDLE TRANSITION00010.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":337,
"x":262,
"w":173,
"y":60,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":337,
"x":610,
"w":173,
"y":324,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00009.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":336,
"x":262,
"w":173,
"y":62,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":336,
"x":435,
"w":173,
"y":313,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00006.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":336,
"x":262,
"w":195,
"y":63,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":336,
"x":1,
"w":195,
"y":630,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00003.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":310,
"x":212,
"w":208,
"y":86,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":310,
"x":405,
"w":208,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00007.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":339,
"x":262,
"w":195,
"y":58,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":339,
"x":381,
"w":195,
"y":651,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00005.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":321,
"x":262,
"w":165,
"y":77,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":321,
"x":615,
"w":165,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00002.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":310,
"x":209,
"w":220,
"y":85,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":310,
"x":183,
"w":220,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00008.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":338,
"x":262,
"w":181,
"y":59,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":338,
"x":198,
"w":181,
"y":641,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00000.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":326,
"x":200,
"w":224,
"y":70,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":326,
"x":209,
"w":224,
"y":313,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00001.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":315,
"x":208,
"w":206,
"y":79,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":315,
"x":1,
"w":206,
"y":313,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00004.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":308,
"x":242,
"w":180,
"y":88,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":308,
"x":1,
"w":180,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
"IDLE TRANSITION00011.png":
{
"trimmed":True,
"spriteSourceSize":
{
"h":343,
"x":262,
"w":177,
"y":56,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":343,
"x":578,
"w":177,
"y":663,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
},
},
"meta":
{
"smartupdate":"$TexturePacker:SmartUpdate:3cb3f1ffb1654e1d6eb6ef7b10d4f858:76dffcfef3e26e1fedc4eff93a6bb5f2:f0f95e7d692a8330104f0d1454fb1ba8$",
"size":
{
"h":1007,
"w":784,
},
"version":"1.0",
"format":"RGBA8888",
"scale":"1",
"app":"http://www.codeandweb.com/texturepacker",
"image":"idletrans.png",
},
}
 

TheouAegis

Member
Okay I don't know how to read json files at a glance to know when you should use list and when you should use map -- maybe {} is map and [] is list? But with your json, the value of "frames" (and I think nearly everything else) is a map, not a list.

Here is where I'm at so far with what I've been working on today (was on a date all day yesterday):
Code:
var f = file_text_open_read("array.json");
var str = "";
var t = current_second;
while !file_text_eof(f) {
    str += file_text_read_string(f);
    file_text_readln(f);
}
file_text_close(f);
var resultMap = json_decode(str);
var mini = 1;//ds_map_size(resultMap);
var subMap,size;
var curr;
curr[0] = ds_map_find_first(resultMap);
for(var i=0; i<mini; i++)
{
    subMap[i] = ds_map_find_value(resultMap,curr[0]);
    curr[0] = ds_map_find_next(resultMap,curr[0]);
    size = ds_map_size(subMap[i]);
    curr[1] = ds_map_find_first(subMap[i]);
    for(var k=0; k<size; k++)
    {
        subMap[mini+i+k] = ds_map_find_value(subMap[i],curr[1]);
        curr[1] = ds_map_find_next(subMap[i],curr[1]);
        show_debug_message(ds_map_size(subMap[mini+i+k]))
    }
}
When I treated it as a list, I got errors. When I treated frames as a map, I got map sizes just fine.

Edit: updated to show how I got the next iteration (the map of each frame).
 
Last edited:

chamaeleon

Member
EDIT EDIT: I think I'm starting to understand it now. This particular JSON file doesn't have "default" as a key. The keys are essentially labels or headers of included data and this one has 2 chunks of data
"Frames" which includes all the data I need
"Meta" which includes other data I don't need so I can safely ignore that

Instead of searching for "default" like most instructions says to do, "Frames" should be storing all that data instead. So it seems like so far json_decode is performing correctly, just need to figure out how to extract the data from "frames" now
The reason for no default top level key is because the top-level data structure is already a map and not a list. Extracting should be easy using the accessors.
Code:
frames = resultmap[? "frames"]
meta = resultmap[? "meta"]
var framekey = ds_map_find_first(frame); // get key/png filename
while (!is_undefined(framekey))
{
    show_debug_message("PNG: " + framekey);
    var trimmed = frame[? "trimmed"];
    ... do something with framekey and trimmed value ...
    framekey = ds_map_find_next(frame, framekey);
}
 

TheouAegis

Member
Code:
"IDLE TRANSITION00000.png":
{
   "frame": {"x":209,"y":313,"w":224,"h":326},
   "rotated": false,
   "trimmed": true,
   "spriteSourceSize": {"x":200,"y":70,"w":224,"h":326},
   "sourceSize": {"w":580,"h":400},
   "pivot": {"x":0.5,"y":0.5}
},
In my code, this would be subMap[1] (because I ignored "meta" in the main decode. Not sure how this would be read, but I can tell ya each of the ones with {} will need to be broken down further as maps. rotated and trimmed may just be available to read as simple variables.
 

chamaeleon

Member
Code:
"IDLE TRANSITION00000.png":
{
   "frame": {"x":209,"y":313,"w":224,"h":326},
   "rotated": false,
   "trimmed": true,
   "spriteSourceSize": {"x":200,"y":70,"w":224,"h":326},
   "sourceSize": {"w":580,"h":400},
   "pivot": {"x":0.5,"y":0.5}
},
In my code, this would be subMap[1] (because I ignored "meta" in the main decode. Not sure how this would be read, but I can tell ya each of the ones with {} will need to be broken down further as maps. rotated and trimmed may just be available to read as simple variables.
Code:
var frames = decoded[?" frames"];
if (!is_undefined(frames))
{
    var png = ds_map_find_first(frames);
    while (!is_undefined(png))
    {
        var sss = png[? "spriteSourceSize"];
        if (!is_undefined(sss))
        {
            var x = sss[? "x"];
            var y = sss[? "y"];
            if (!is_undefined(x) && !is_undefined(y)) { ... }
        }
        png = ds_map_find_next(frames, png);
    }
}
 

TheouAegis

Member
Hehe saved me the trouble of writing something up for him!


@kupo15 in case you didn't know what all the values are for
frame: x and y are the coordinates inside the image file
frame: w and h are the dimensions of the sprite in the image file
rotated is whether you need to rotate the image 90 degrees counterclockwise or not
trimmed is whether or not the canvas was cropped (in this case, it's true so the canvas is bigger)
spriteSourceSize: x and y are the coordinates in the canvas if trimmed is true
spriteSourceSize: w and h seem redundant to me...
sourceSize: w and h are the dimensions of the canvas
pivot: x and y are supposedly the x and y origin of the actual sprite... but I dunno why they're 0.5 here. That sounds more like a multiplier.
 

klys

Member
Ok, its look that you dont know how to handle a json when is transformed to map and viceversa.

Lets say you have the following map:

Code:
var map = ds_map_create();
map[? "hp"] = string(objPlayer.hp);
map[? "x"] = string(objPlayer.x);
map[? "y"] = string(objPlayer.y);
map[? "name"] = objPlayer.name; // already a string
map[? "map"] = room_get_name(room); // already a string
map[? "inventory"] = json_encode(objInventory.itemslots);
as you can see i transform everything to string, even a json is a string it self.

So now... how i transfor this to json and save then?

Code:
var json = json_encode(map);
var file = file_text_open_write("save");
file_text_write_string(file, json);
file_text_close(file)

this will save a save file with the following json:

Code:
{
"hp":"523",
"x":"230.32",
"y":"845.45",
"mp":"150",
"map":"roomLevelUnderground",
"inventory":"{/"slot-0/":/"-1/"}"
}
that is it! Easy right?

But what we do now to access this information from that file? 'save'

as follow:

Code:
var file = file_text_open_read("save");
var json = file_text_read_string(file);
file_text_close(file);
var map = json_decode(map);
// now that we got the json we need to transform every to their respective value type
map[? "x"] = real(map[? "x"]);
map[? "y"] = real(map[? "y"]);
map[? "map"] = asset_get_index(map[? "map"]);
map[? "inventory"] = json_decode(map[? "inventory"]); // this may give error since we do not transform anything to string as we should

// use the variables

room = map[? "map"];

var player = instance_create(map[? "x"], map[? "y"], objPlayer);
player.name = map[? "name"];
objInventory.itemslots = map[? "inventory"];
as you can see in the process we can use ds_map as arrays using the wildcard '?' in brakets '[]' and the json will be the same as the map
Also you can a json inside a json, just remember to transform every variable to string before adding the json inside the other json.

Another pitfall when using json in GML is to save ds_list as json, or using ds_list in json, please dont do it, ds_list make the json hard to read.

ds_list and ds_map are better no to be mixed when using json, only use ds_maps for json. That is it!
 
Last edited:

FrostyCat

Redemption Seeker
Someone sent me a PM asking me to respond to this topic. And boy is this topic turning into a train wreck.

First of all, I think you've mistakenly loaded the hash JSON in as both the array and hash included files. The so-called "array" file has no arrays in it and looks exactly like the "hash" file. "Hash" is Ruby speak for maps (GML speak), objects (JSON speak) or dictionaries (Python speak). Given that the order of the frames is important, you should use the array format since maps in GML do not preserve ordering. The actual array format would have square brackets in it and would not be identical to the one presented in your current sample.

Second, you need to learn the thought process of addressing nested JSON data, which is actually fairly easy contrary to popular GMC opinion. Here is one of my older posts describing how to do it with an example. For instance, in your hash data file, the height of the frame labelled "IDLE TRANSITION00000.png" is found under the addressing sequence "frames" > "IDLE TRANSITION00000.png" > "frame" > "h". Once you've corrected the array file, use the thought process to address each frame. It should look something like "frames" > 0 > "frame" > "h".

Last of all, much of the advice from klys is unbearably ignorant for anyone with a real understanding of JSON. There's a reason why escaped internal JSON strings are almost never found in the wild. It is against the point of JSON to force saving as a string when the nested data is an object (map) or array (list). While resources should be stored as name strings (as the raw values can shift if reordered later), maps and lists should simply be nested in-place with ds_map_add_map(), ds_map_add_list(), or ds_list_add() with ds_list_mark_as_map() / ds_list_mark_as_list(). If you need to clone a map or list for the purposes of encoding, use ds_map_create() with ds_map_copy() or ds_list_create() with ds_list_copy() for non-nested structures, or encode then decode again for nested structures.
 

chamaeleon

Member
Another pitfall when using json in GML is to save ds_list as json, or using ds_list in json, please dont do it, ds_list make the json hard to read.

ds_list and ds_map are better no to be mixed when using json, only use ds_maps for json. That is it!
I'll beg to differ. If I have a number of the same things I'll quite happily use a JSON array for those. For instance, had I been working with the above content without being constrained in any way how to represent the data of various png files, I'd have put the frame images in an array instead and have the filename be a key in a map per frame image. Although in this particular example the filenames are unique (by definition, given they're filenames), if there was a sequence of things not so easily distinguishable, I'd hate to just use maps and invent sequence numbers as keys or something just to be able to use maps. For properties use maps, for collections of identical types of things use arrays, for collections of different types of things put each collection of identical things in an array that is in a map with keys per type of collection, etc., etc.
 

kupo15

Member
Wow, the wealth of information from everyone is amazing and overwhelming haha

Someone sent me a PM asking me to respond to this topic. And boy is this topic turning into a train wreck.
Understatement haha that's why I originally tagged you in the OP seeing your other posts on the subject.

Another key thing I also didn't understand which after taking the last 30 mins to finally learn about was what the heck accessors really are and their point. Now I believe I get it. It seems like they are an easy way to reference ds to treat them using array notation instead of having a bunch of "ds_map_find_value" lines everywhere. Before your post I was able to extract the first png name from the list through Theous and Chamaeleons exchange.

Second, you need to learn the thought process of addressing nested JSON data, which is actually fairly easy contrary to popular GMC opinion. Here is one of my older posts describing how to do it with an example. For instance, in your hash data file, the height of the frame labelled "IDLE TRANSITION00000.png" is found under the addressing sequence "frames" > "IDLE TRANSITION00000.png" > "frame" > "h". Once you've corrected the array file, use the thought process to address each frame. It should look something like "frames" > 0 > "frame" > "h".
Ok I think I'm getting what you are saying in tandem with what Theo and chamaeleon were posting previously but this quote seems to crack the understanding of the format open for me. So each {} pair is essentially a folder within a folder like what we do with standard windows folder references users/kupo/document etc... {} is essentially the same thing as "/"

So continuing from your example, "spritesourcesize" "sourcesize" "frame" and "pivot" are all subfolders in the main "idel transition0000" folder. If I want to extract from Idle Transition000000:

"trimmed" - frames>idle transition00000>trimmed
"pivot x" - frames>idletrans>pivot>x

If this is correct then I believe I understand the hash format here, which then brings me to the array format...

First of all, I think you've mistakenly loaded the hash JSON in as both the array and hash included files. The so-called "array" file has no arrays in it and looks exactly like the "hash" file. "Hash" is Ruby speak for maps (GML speak), objects (JSON speak) or dictionaries (Python speak). Given that the order of the frames is important, you should use the array format since maps in GML do not preserve ordering. The actual array format would have square brackets in it and would not be identical to the one presented in your current sample.
You are right. I thought I didn't do that and there was no difference between the two exports but just double checked and there is
https://ufile.io/vsu67
Code:
{
"frames":
[

{
"trimmed":True,
"spriteSourceSize":
{
"h":326,
"x":200,
"w":224,
"y":70,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":True,
"frame":
{
"h":326,
"x":363,
"w":224,
"y":351,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00000.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":315,
"x":208,
"w":206,
"y":79,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":True,
"frame":
{
"h":315,
"x":702,
"w":206,
"y":168,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00001.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":310,
"x":209,
"w":220,
"y":85,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":True,
"frame":
{
"h":310,
"x":691,
"w":220,
"y":376,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00002.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":310,
"x":212,
"w":208,
"y":86,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":True,
"frame":
{
"h":310,
"x":362,
"w":208,
"y":577,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00003.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":308,
"x":242,
"w":180,
"y":88,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":True,
"frame":
{
"h":308,
"x":674,
"w":180,
"y":598,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00004.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":321,
"x":262,
"w":165,
"y":77,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":True,
"frame":
{
"h":321,
"x":702,
"w":165,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00005.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":336,
"x":262,
"w":195,
"y":63,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":336,
"x":1,
"w":195,
"y":687,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00006.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":339,
"x":262,
"w":195,
"y":58,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":339,
"x":1,
"w":195,
"y":346,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00007.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":338,
"x":262,
"w":181,
"y":59,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":338,
"x":180,
"w":181,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00008.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":336,
"x":262,
"w":173,
"y":62,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":True,
"frame":
{
"h":336,
"x":363,
"w":173,
"y":176,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00009.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":337,
"x":262,
"w":173,
"y":60,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":True,
"frame":
{
"h":337,
"x":363,
"w":173,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00010.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":343,
"x":262,
"w":177,
"y":56,
},
"sourceSize":
{
"h":400,
"w":580,
},
"rotated":False,
"frame":
{
"h":343,
"x":1,
"w":177,
"y":1,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"IDLE TRANSITION00011.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":163,
"x":0,
"w":137,
"y":4,
},
"sourceSize":
{
"h":167,
"w":142,
},
"rotated":True,
"frame":
{
"h":163,
"x":198,
"w":137,
"y":341,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_blink_0.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":165,
"x":4,
"w":133,
"y":2,
},
"sourceSize":
{
"h":167,
"w":142,
},
"rotated":False,
"frame":
{
"h":165,
"x":886,
"w":133,
"y":780,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_blink_1.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":163,
"x":0,
"w":137,
"y":4,
},
"sourceSize":
{
"h":167,
"w":142,
},
"rotated":True,
"frame":
{
"h":163,
"x":198,
"w":137,
"y":341,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_blink_2.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":169,
"x":67,
"w":105,
"y":1,
},
"sourceSize":
{
"h":170,
"w":217,
},
"rotated":False,
"frame":
{
"h":169,
"x":779,
"w":105,
"y":780,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_corner_land_start_0.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":133,
"x":18,
"w":199,
"y":37,
},
"sourceSize":
{
"h":170,
"w":217,
},
"rotated":True,
"frame":
{
"h":133,
"x":644,
"w":199,
"y":787,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_corner_land_start_1.png",
},

{
"trimmed":False,
"spriteSourceSize":
{
"h":170,
"x":0,
"w":217,
"y":0,
},
"sourceSize":
{
"h":170,
"w":217,
},
"rotated":True,
"frame":
{
"h":170,
"x":472,
"w":217,
"y":787,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_corner_land_start_2.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":162,
"x":0,
"w":181,
"y":0,
},
"sourceSize":
{
"h":235,
"w":181,
},
"rotated":True,
"frame":
{
"h":162,
"x":198,
"w":181,
"y":480,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_hang_tuck_0.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":235,
"x":24,
"w":130,
"y":0,
},
"sourceSize":
{
"h":235,
"w":181,
},
"rotated":False,
"frame":
{
"h":235,
"x":340,
"w":130,
"y":787,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_hang_tuck_1.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":235,
"x":23,
"w":140,
"y":0,
},
"sourceSize":
{
"h":235,
"w":181,
},
"rotated":False,
"frame":
{
"h":235,
"x":198,
"w":140,
"y":787,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_hang_tuck_2.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":131,
"x":0,
"w":110,
"y":74,
},
"sourceSize":
{
"h":205,
"w":136,
},
"rotated":True,
"frame":
{
"h":131,
"x":198,
"w":110,
"y":663,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"spr_creature_hop_forward1_0.png",
},

{
"trimmed":True,
"spriteSourceSize":
{
"h":32,
"x":0,
"w":30,
"y":0,
},
"sourceSize":
{
"h":32,
"w":200,
},
"rotated":False,
"frame":
{
"h":32,
"x":644,
"w":30,
"y":988,
},
"pivot":
{
"x":0.5,
"y":0.5,
},
"filename":"testd.png",
},
],
"meta":
{
"smartupdate":"$TexturePacker:SmartUpdate:dfec2e66578d91f91913276bd8ed7a7d:98412d36799ca1558a76a728ccea1a43:bc9266f9083536fefb4ef11840e31610$",
"size":
{
"h":1024,
"w":1024,
},
"version":"1.0",
"format":"RGBA8888",
"scale":"1",
"app":"http://www.codeandweb.com/texturepacker",
"image":"array.png",
},
}

Thank you for explaining the difference between hash and array, arrays do seem like the better option. Although this creates one more question, how do I extract the data from this format? Does this now contain both lists and maps? Also the filenames are now at the bottom of the chunk of text instead of acting like a label for a folder so how do I separate the different images? I added more files into the above array.json to show what it looks like with multiple animation names in there

Would I go about this by essentially looping through the entire file and placing them into arrays?

array[0,0] = frames>filename
[0,1] = frames>spritesourcesize>h
*repeat for x,w,y
**repeat for sourcesize, rotated, frame,pivot,trimmed

*somehow move onto the next {} and repeat the process
 
Last edited:

chamaeleon

Member
Although this creates one more question, how do I extract the data from this format? Does this now contain both lists and maps? Also the filenames are now at the bottom of the chunk of text so how do I separate the different images? I added more files into the above array.json to show what it looks like with multiple animation names in there
Code:
var decoded = json_decode(json);
var frames = decoded[? "frames"];
if (!is_undefined(frames))
{
    for (var i = 0; i < ds_list_size(frames); i++)
    {
        var frame = frames[| i];
        var filename = frame[? "filename"];
        if (!is_undefined(filename))
            show_debug_message("Frame " + string(i) + ": " + filename);
        else
            show_debug_message("Frame " + string(i) + ": NO FILENAME");
        var sss = frame[? "spriteSourceSize"];
        if (!is_undefined(sss))
        {
            var w = sss[? "w"];
            var h = sss[? "h"];
            if (!is_undefined(w) && !is_undefined(h))
            {
                show_debug_message("  spriteSourceSize width = " + string(w));
                show_debug_message("  spriteSourceSize height = " + string(h));
            }
        }
    }
}
 

kupo15

Member
@chamaeleon Amazing! Thanks for putting that snippet together to help get me started! I'm pretty sure I know how to apply that to the rest of the data I need to pull. My summary of understanding is this

Json Array
1. Extract Json file and store as a string so it can be read
2. Decode the string, creates a ds_map with 2 keys (in my case). Each key's value is a ds_list which holds ds_map data for each image. (in my array file, there are 23 images and the list_size from the "frames" key is 23)
3. Loop through all entries in the list which are all ds_maps and access the data in there like a json hash

I changed the var frame to spritedata because that represents more accurate what that data is holding and to not confuse it with the actual frame data in the json.

This is exciting, thanks so much for everyone's help allowing me to understand jsons. I'll post back here if I run into any future issues, I don't think I will though :D
 

kupo15

Member
Everyone you are simply amazing! I was able to take all the information put forth here and apply it to my test project to create and everything works perfectly! I can't thank everyone enough!

I have one last thing I need to solve and that is how to figure out how to know when there is the start of a new sprite. Currently it just loops through the entire json in one big list but I need to have the array counter reset.

Code:
Current Result Example - Run On List
///animation[sprite,spritedata]
// walk
animation[0,0-14]
animation[1,0-14]
animation[2,0-14]
animation[3,0-14]
// idle
animation[4,0-14]
animation[5,0-14]
animation[6,0-14]
// fall
animation[7,0-14]
animation[8,0-14]

Desired Result from loop - Grouped List
/// animation[sprite,subimage,data]
// walk
animation[0,0,0-14]
animation[0,1,0-14]
animation[0,2,0-14]
animation[0,3,0-14]
// idle
animation[1,0,0-14]
animation[1,1,0-14]
animation[1,2,0-14]
animation[1,3,0-14]
// fall
animation[2,0,0-14]
animation[2,1,0-14]
animation[2,2,0-14]
animation[2,3,0-14]
I know how to create this new list format once I can figure out what I can use as a trigger. 3 Ideas come to mind

1. Manually create a list that says how many subimages their are in each sprite. When the for loop hits that number reset the array (not recommended)
2. Use the original canvas size from the json. When that size is different that indicates the animation has ended (what if by chance the next sprite has the exact dimensions?)
3. Use a partial text filter. I'm going to have to rename my sprites to animation0_0, animation0_1 etc. But I can't compare exact strings. The trigger would be if the next filename != animation0 (previous filename without the _0) that indicates we are onto the next animation

I don't think 1.x has 3 dim arrays unfortunately either...gotta check that
 
Last edited:

chamaeleon

Member
I have one last thing I need to solve and that is how to figure out how to know when there is the start of a new sprite. Currently it just loops through the entire json in one big list but I need to have the array counter reset.
Since it sounds like you have discrete sets of sprites it seems that keeping the discrete sets of sprites in separate arrays in JSON. Perhaps you'd find it easier to have an array of sets, where each entry is a map containing metadata for that set plus the exact set of sprites as an array inside it? This representation would probably imply you'd get the "default" map entry that contains the top-level array from JSON.
 

kupo15

Member
Since it sounds like you have discrete sets of sprites it seems that keeping the discrete sets of sprites in separate arrays in JSON. Perhaps you'd find it easier to have an array of sets, where each entry is a map containing metadata for that set plus the exact set of sprites as an array inside it? This representation would probably imply you'd get the "default" map entry that contains the top-level array from JSON.
So you mean keep the large texture atlas with all the sprites in there but instead of only having one json to that atlas break it up into several jsons all referencing that one atlas? Sounds pretty clever...I need to check if texturepacker can do that if that if I understand you correctly

I think it might be a bit excessive though considering I'm going to have at least 1000 animations. That means at least 1000 json files and hundreds of file_text_open/close at load time. Wouldn't that make load times slower than opening a couple of jsons?
 
Last edited:

chamaeleon

Member
So you mean keep the large texture atlas with all the sprites in there but instead of only having one json to that atlas break it up into several jsons all referencing that one atlas? Sounds pretty clever...I need to check if texturepacker can do that if that if I understand you correctly

I think it might be a bit excessive though considering I'm going to have at least 1000 animations. That means at least 1000 json files and hundreds of file_text_open/close at load time. Wouldn't that make load times slower than opening a couple of jsons?
No, not multiple json files.. I don't know anything about texturepacker and what it may or may not support, but if you have 1000 animations, you could have 1000 entries in the top level array in json, each being a map containing metadata for that particular animation, and one entry in the map being an array of sprites representing the animation sequence. Again, just how I would structure it, if I was given the freedom to do so. If outside constraints like some software package prevents you from doing it, it's a matter of doing the best you can with what it supports.
 

Tsa05

Member
What's being suggested is keeping a list of frames in your json string. Remember that "json" isn't a data structure--json is a formatting technique for describing data in hierarchical relationships. GameMaker turns anything inside {curly braces} into a DS_MAP and anything inside [brackets] into a DS_LIST. Anything else is stored as a map key or a key's value.

So, yea, it looks like your map contains a key called "frames" to which is assigned a map full of maps.
Code:
{
    "frames": {
        "IDLE TRANSITION00010.png": {
            "trimmed": True,
            "spriteSourceSize": {
                "h": 337,
                "x": 262,
                "w": 173,
                "y": 60,
            },
            "sourceSize": {
                "h": 400,
                "w": 580,
            },
            "rotated": False,
            "frame": {
                "h": 337,
                "x": 610,
                "w": 173,
                "y": 324,
            },
            "pivot": {
                "x": 0.5,
                "y": 0.5,
            },
        },
So, the ds_map that results from json_decode() produces a map with only 1 key in it, called "frames." The value of that key is the ID number of a ds_map that contains many keys.
Each key within this "frames" ds_map is the name of a png file. Each key is assigned to another map, containing data about the image.

The formatting here is not exactly ideal, since there's no way to guarantee what order everything is in. This is simply because ds_maps identify everything by name of keys instead of by order. On the other hand, ds_lists do things in order. So, I think that the suggestion above is advocating for something like this:
Code:
{
     "nameOfSpriteSet": {
                                               "x": 610,
                                               "y": 324,
                                               "h": 337,
                                               "w": 580,
                                               "h": 400,
                                               "rotated": False,
                                               "subimages": [ "IDLE TRANSITION00009.png", "IDLE TRANSITION00010.png"
                                                                        ]
There's bits missing, but that's just trying to convey the general idea--I think chamaeleon is suggesting that you store subimages in a list that is associated with the image set. The example above would store a list of image names under the "subimages" key, within the nameOfSpriteSet map.
 

chamaeleon

Member
I think chamaeleon is suggesting that you store subimages in a list that is associated with the image set. The example above would store a list of image names under the "subimages" key, within the nameOfSpriteSet map.
Thank you, that is exactly what I meant, along the lines of your example. I was writing on a phone, and it wasn't exactly conducive to editing a chunk of JSON..
 

kupo15

Member
@chamaeleon @Tsa05

Ok that makes much more sense. So instead of decoding the json only giving me one key "frames" that contains all data within that key, have the json decode to multiple keys
"animation0"
"animation1"
etc....
"meta"

to have a natural separation there. That seems good. TP does have ways where you can custom format your exported data so in theory a json export like that can be done. I unfortunately don't know JS to be able to do that, maybe I can figure it out by copying the json array preset and altering it.

{ "nameOfSpriteSet": { "x": 610, "y": 324, "h": 337, "w": 580, "h": 400, "rotated": False, "subimages": [ "IDLE TRANSITION00009.png", "IDLE TRANSITION00010.png" ]
I don't think this format would be very good for this actually. It looks like you have a list of all the subimages referencing the same data above it. This wouldn't be very optimized because each frame has different amounts of transparency clipped from it so each subimage would need its own set of data.

The more I think about it, I don't think this single string of data without any delineation between sprites "issue" is a problem actually. I already have massive sprite duplication lists that correspond to each animation and its subimages so that I can duplicate frames via code. So I could piggyback this list using the array_length/height functions to figure out where to cut up that list like my 1. option suggested
 

Tsa05

Member
each frame has different amounts of transparency clipped from it so each subimage would need its own set of data
Oh, but that's even easier then! Lists don't have to contain single information per element. They can contain [lists], or even {objects}.
Code:
{
     "nameOfFirstSet": [ {"frame": "image01.png", "x": 330, "y": 660, "cropped": true  .....ALL DATA ABOUT THIS FRAME}, {"frame": "image02.png", "x": 330, "y": 660, "cropped": true  .....ALL DATA ABOUT THIS FRAME} ]
     "nameOfSecondSet": [ {"frame": "image08.png", "x": 330, "y": 660, "cropped": true  .....ALL DATA ABOUT THIS FRAME}, {"frame": "image09.png", "x": 330, "y": 660, "cropped": true  .....ALL DATA ABOUT THIS FRAME} ]
 

kupo15

Member
Oh, but that's even easier then! Lists don't have to contain single information per element. They can contain [lists], or even {objects}.
Code:
{
     "nameOfFirstSet": [ {"frame": "image01.png", "x": 330, "y": 660, "cropped": true  .....ALL DATA ABOUT THIS FRAME}, {"frame": "image02.png", "x": 330, "y": 660, "cropped": true  .....ALL DATA ABOUT THIS FRAME} ]
     "nameOfSecondSet": [ {"frame": "image08.png", "x": 330, "y": 660, "cropped": true  .....ALL DATA ABOUT THIS FRAME}, {"frame": "image09.png", "x": 330, "y": 660, "cropped": true  .....ALL DATA ABOUT THIS FRAME} ]
Cool yeah that looks good!
I just made a post on the forum on the TP forums for help with where to find the current array template so I can modify it. I'll reply back here when I got the information and played around with it a little. Thanks man! :)

EDIT: This is pretty cool for the meantime. I could use this online editor to create the structure I want and build it in GM while I wait to figure out how to create the exporter. This is the format you were talking about, right @Tsa05? The only issue I'm seeing when I download this edited json is the order of the 1st-6th set isn't preserved, are we missing something? I guess the order doesn't have to matter because I could easily use a string search for those keys instead of looping in order

http://jsoneditoronline.org/?id=7880c6b5645c74dd86ca37802860614f

 
Last edited:
Top