where was i?
when I first created the nullstars section of this site, I was kinda hoping I would have fun game dev things to talk about here. well unfortunately!! that didn’t work out!! because I gave up!!
lmfao. more like took an extended haitus. here’s what happened.
context
around like. febuary of this year. I was working on the game, getting things done, etc. things were working out! I was mostly focusing on getting mechanics and various basic systems and such to feel good before focusing on level design itself. eventually I felt that I was ready to start making more levels, and then something terrible yet thouroughly expected happened.
lag. yeag. this game’s world is being put together using ldtk, and that’s for two reasons:
- gamemaker’s level editor sucks
- ldtk puts every ‘room’ altogether in a big map
going off of what ejow was kinda trying to do, I wanted everything in nullstars to be in one big ass map. the thing about having these short challenges one after another is that having any kind of wait between them is incredibly annoying. pretty much from the start, it was clear that each area would have to have multiple checkpoints to be bearable. so that what ejow did. each ‘level’ had multiple checkpoints inside them, seperating each little challenge. you’d finish the ‘level’ and then reload the room to the next one.
well for nullstars I wanted to do better. let’s make everything one Big Ass Map! what’s the problem with making everything in one big ass map? it takes too much disk space, too much memory, and is very clunky to work with. all of this was a problem in ejow (despite rooms being overall smaller than what I had in mind for nullstars), and seeing that ldtk let you make little rooms and rearrange them wherever you want? it attracted me.
the problem
tldr I decided to load the entire world map at once. every tile, every entity, everything, loaded all at once in the beginning and then sitting around in memory forever.
shockingly, there were several problems with this!
ram
gamemaker’s ram management is a bit… rough?
so can you guess how big a value in gamemaker is? 64 bits? maybe 70 bits for type data?
wrong. 128 bits. every value in gamemaker is 128 fucking bits.
every time you create a variable in gamemaker via like name = 10
, you are taking an extra 128 bits in memory, plus however long the variable name is in utf8 (ofc if the name is unique).
now then. ldtk exports it’s level format as a json file. this json file is comprehensive as fuck, and it’s massive. I only have a little bit in my world right now and it takes up 11mbs. worse yet, it’s dense.
here is some autotile data.
// ...
{ "px": [128,96], "src": [80,64], "f": 0, "t": 133, "d": [397,1298], "a": 1 },
{ "px": [144,96], "src": [80,64], "f": 0, "t": 133, "d": [397,1299], "a": 1 },
{ "px": [160,96], "src": [80,64], "f": 0, "t": 133, "d": [397,1300], "a": 1 },
{ "px": [176,96], "src": [80,64], "f": 0, "t": 133, "d": [397,1301], "a": 1 },
{ "px": [192,96], "src": [80,64], "f": 0, "t": 133, "d": [397,1302], "a": 1 },
{ "px": [208,96], "src": [80,64], "f": 0, "t": 133, "d": [397,1303], "a": 1 },
{ "px": [224,96], "src": [80,64], "f": 0, "t": 133, "d": [397,1304], "a": 1 },
// ...
now imagine you are unpacking this json data in gamemaker. lets unpack a single tile! assuming the best case scenario, each tile takes a minimum of 128 * 13 = 1664 / 8 = 208 bytes. just 5 of these to make a full kilobyte. and there are thousands of them, per tile.
so obviously it’d be in our best interests to parse all of this then throw the json away once we have the data we actually need. in practice, running the game took around >300mbs of ram, most of it will never be used. I think this is unacceptable.
of course, I didn’t care. taking ram doesn’t steal performance, so I could care about reducing ram usage later.
performance
fuck. remember that everything was loaded all the time?
well I was adding these spike bubble things to the map:
and I was adding a lot. so. okay. now I have performance issues. what now
(on an unrelated note, I was also having quite a bit of stuttering because I was abusing the garbage collecter)
good question
my goal at this point was most fundementally: only have instances loaded when they are in a loaded room.
here was the general structure of how world loading worked:
- level
: create event
> load json
> loop through rooms
> loop through layers
> create tile layers
> create entities
everything was in one place, which was hell to navigate, but it was really easy to write. I wanted to get through the boring parts of this project quickly, because I knew I would get really bored really quickly, so I chose to do it the easy way first, then work on making it better later.
in order to support selectively loading entities based on the room, I needed to seperate the rooms from the create event. I decided the best way of doing this was going to be by moving the code responsible for unpacking the room data into a constructor, so that rooms could be discrete objects. the new structure would look like this:
- level
: create event
> load json
> loop through rooms, give room data to some kind of Room() constructor
: step event
> update list of Room()s, doing like room.load() and room.unload()
- Room()
: constructor
> unpack room data
> unpack entity data
: load()
> load entities
: unload()
> do something I think
so rewrite I did. theoretically this shouldn’t have been particularly hard, but apparently it was because I gave the fuck up
a lesson on branches
y’all. create a git branch if you are going to rewrite a fundemental part of your game.
today I decided to go and fix it. 7 months later. turns out the crux of my most frusterating issues was a misformed condition. entities were supposed to be deleted when outside of levels, but I accidentally set them to delete when outside of view.
there were other unfinished parts of the system, but that was the stopping point for me 7 months ago apparently. fixed it in around an hour. the game is playable exactly as it used to be. woo! so now I ask, why was this issue enough for me to avoid the project for 7 months straight? what should that mean for me?
I have no idea. maybe I’ll come up with an answer sometime.
in conclusion?
the main point of this post is to say basically, I’m back on this again. yippee. more updates soon, as they come.