Progress Report 1/13/2023
Added 2023-01-14 05:05:10 +0000 UTCCh ch ch... Ah ah ah... Happy Friday the 13th, everyone! Here's what I got done this week:
- Finished the basis for the save system
Only one bullet point this time, but ooooh boy do I have a lot to say about it!
TL;DR: I went through several methods of saving/loading, nearly lost a huge chunk of the project to one small mistake, had an existential crisis, and by the end of the week had a functional save system.
I'll run you through everything chronologically.
I started off by following Godot's official documentation on saving games and a couple of YouTube tutorials I found on the subject. This served as a good start for my understanding of how it worked, but functionally it was a big headache.
Boiled down, it saves the data to a file separate from the game, deletes all nodes (game objects) that contributed to that data, and recreates those nodes with the new data.
My first attempt was applying this to each individual sprite. For testing purposes, I just applied it to the three "Top" sprites. This attempt failed due to the deletion/recreation process causing a loss of reference to these sprites in CustomizerManager. In other words, the CustomizerManager was looking for sprites that were deleted, not the ones that were recreated.
So, I took it to the top instead. I applied the save/load system to the GTSMenu node -- the node that contains all of the sprites, buttons and buttons in the customizer, and it holds the CustomizerManager script. Unsurprisingly, this failed, too. Even after getting the references to CustotomizerManager to work even after recreation, CustomizerManger itself was, for reasons I still don't comprehend, getting references to its previously deleted children (sprites, buttons, etc.) in addition to the newly recreated children. I... stopped asking questions by this point.
It was around this point that I made a nearly project-ending mistake. I had turned some of the sprites into prefabs, which more or less means they're stored elsewhere for easier access and copy-pasting. Well, I did some cleanup in preparation for my next attempt: I deleted the prefabs in the file system, removed failed code, and emptied my computer's trash bin for the next supply of failed attempts. I then saved and quit to try again later.
My mistake was deleting the prefabs in the file system but forgetting to delete them from the in-game scene itself. Godot did not like this. Godot did not like this at all. It refused to load the scene they were in -- that is, the entire main menu, customizer included -- and just gave me a blank project in its place with an error stating the prefab problem. But it wasn't a bit deal; I could just restore the files from the trash bin-... that I had just emptied.
Oh...
Maybe... Maybe I could restore a backup of the project! My last backup was... six months ago...
Oh...
After an existential crisis, I realized I could just create a brand new prefab and give it the same name as the ones from before, giving the in-game objects a reference to grab. And sure enough, it worked! The scene opened, and I quickly banished those problematic objects to Godot Hell, a very real place.
And all was well.
And then I made seven hundred backups of my project.
So, back to the save system, I realized that deleting the nodes and recreating them was not the way to go. Why not just set the variables without deleting anything?
No, seriously, why not? This seems like such a simpler system to work with. What's the catch?
I sought this answer out myself. I began by using the save script to set the variables for every sprite and for CustomizerManager, which controls... well, a lot. A lot. Much to my surprise, this actually worked!... To an extent.
See, what the save script does is it reads each line of the save file and performs code for each of those lines. I had a function calling all sprites and the manager, but I put it in that looping code. I needed each line from the save file to be sent to its respective object, but instead this sent every object the entire save file.
This one stumped me for a while. How do I get the script to read each line of code and send that code to each object without sending each object the rest of the file, too? Well, sometimes the answer to a complicated problem is a simple one.
I added a variable before the looping function and set it to zero. Then, for each line of the save file being read, increase that variable by one. Proceed to use this variable as a reference index for the array of objects. In other words: we're reading line 2, so send it to object 2.
And it worked!
Except for when it didn't!
It was reading and sending the data just fine. The problem now was with organization. Every single new line of code that I added gave me a plethora of new errors to deal with. Every color to save, every sprite's visibility to remember, not to mention the sprites that don't have secondary sprites... It was an uphill battle that I couldn't win. I felt like Sisyphus endlessly rolling that boulder.
I was tired. Utterly drained, and so, so very done with programming. I was ready to put a pin in it and get back to 3D modelling. I miss Slagathor...
So, I went to sleep with intentions to open Blender the next day so that maybe I'd make some progress with something. When I came back from work the next day, I stared at the Godot and Blender icons on my desktop. I weighed my options. I thought long and hard. Finally, I made a decision.
One last attempt.
I tried one of my last ideas: an idea that I called "brute force". I'll just send all data to one object in the customizer. That data object will send data to all relevant objects in the customizer. Those objects will send their data to the data object. The data object will send those objects' data to the save script for saving.
Ladies and gentlemen, sometimes brute force is the best way.
Using this method, I accomplished the same tasks in half the code and a quarter of the time than with the previous method. And it all connects in one, easy-to-debug place! AND it makes sense to do it this way to begin with since the customizer will be saving and loading data separately from the rest of the game!
And with that, I got every part of the "Tops" category -- the selected sprite, the primary and secondary colors, the sprite name, the selection box, and even the lack of a secondary sprite -- all savable and loadable.
And then I went back to sleep because I looked outside and it was already nighttime. :,)
So anyways, that was my week. How was yours? I hope you're doing well! I'm gonna go eat some Taco Bell and think about happy things for a while. I'll see you guys again next week!