Advice on how to save my specific game?

I’ve been trying to make a system to save my game for the past few days, but I can’t seem to figure something out that works.

Think of my game like 2D Minecraft, there is a bunch of “blocks”, spawned right next to each other. Each block is a variant of one specific actor. The blocks are randomly generated so that the map is different each time the player starts their game (it is supposed to be the same afterwards, like a Minecraft world with a random seed, but once you save the game, you’re on that seed forever).

I am having trouble storing all of the data to be saved, specifically the block locations and such.

Here is the approach I have been trying:

  • When the world is randomly generated for the first time, it will spawn a bunch of actors/blocks in a 20x20 square (for now).

  • There isn’t one sequence of spawning, there is multiple. For example: the first set of spawning will be rare blocks, which have a 5% chance of spawning. The second wave of spawning will be another block, maybe 10%, etc.

  • Every time a block is spawned, it stores its coordinates in float variables inside of itself. Then, these coordinates are added to an array of Vectors called “occupied_tiles”, which exists in the Game Instance. The point of this is to have an array full of all of the used tiles, so for different waves of spawning blocks when generating the world, I will search through the array every time I spawn a block to determine if there is already a block at that location, so that I don’t spawn two blocks at the same location, if that makes sense.

  • Along with this, whenever a block is spawned, that specific block and its data is added to an array called “Block_Actors”, in the Game Instance. This will store each specific block and its data in an array, at (theoretically) the same index location as its coordinates in the occupied_tiles array. This is so, whenever I go to save the game, and reload the world, I will have a list of the SPECIFIC blocks and their SPECIFIC locations.

  • So far, this appears to be somewhat working. Here are my current problems:
    … I keep getting crashes(?), I think that my arrays are getting too big. Like I said, I am currently doing a 20x20 block area, which is 400 elements in each array. That’s a lot. I want my total map size to be around 120x120 blocks, so would I need to split this procedure up into like 6 arrays horizontally and 6 arrays vertically, thus equaling 36 arrays of occupied_tiles and Block_Actors? The reason for the crash sometimes says nothing whatsoever, or it’ll say there’s an infinite loop, and just take me to the macro code for a for each loop with break, idk.

… Second problem: whenever the player breaks a block in their game, the data in the Block_Actors array is supposed to be updated to reflect that, for obvious reasons. So if they load their world, break a block, save their world, close the game, and reopen it, that block is to be broken still. I am having trouble updating the specific block in the array, but there’s already a ton of stuff in this post so I’d prefer to just get general input on my method so far.

Tldr: Is this the correct approach for saving my world? It seems very inefficient / scuffed, but I can’t think of anything else. I am pretty new to UE4, and this concept of saving my complex level is pretty much the last thing I need to do in order to get my game on the finishing track.

Thank you for reading my short novel and providing any input :^)

400 elements in an array is not “a lot.”

If you get crashes, it’s more likely that you’re forgetting to resize an array before you try to store into particular offsets in it (if you’re in C++) or something of that nature.

If you’re getting “infinite loop” errors in blueprint, it’s either the case that your algorithm is off and is actually infinite, OR it’s the case that you’re doing something n-squared on top of those arrays. You may wish to consider your implementation for algorithmic complexity. (And this is why most Computer science degrees still teach this concept, despite it sometimes getting a bad rep :slight_smile: )

I don’t quite understand why you need to store the “position” of a block in a separate array, when the actor already has a location, though? Seems like duplicated work. If you want “all blocks of class X in level” then there’s a “get actors of class” function for that. If you only want some of them, then you can add a Set of Block and add each such block to the set when spawned. Your savegame could then just save this set, and it’ll reference all the actors.

Another option to store a grid, is to store a Map from <2D integer coordinate> to <block>. You can then grow this essentially arbitrarily, because the keys on the map don’t need to be contiguous like in an array.

Array of chunks which hold an array of blockdata (i.e Class, Health). The chunk and block index in the given array should already give you everything you need to determine the spawn location when recreating the map from that data. The Block’s data / health should tell your code what block to spawn and what health it is at.

The confusing thing about m infinite loop error is that it is inconsistent.

Here is the general structure of my loops: (it’s written in blueprint but i’ll write it as C++ pseudocode)

This works fine, but if I change it to loop 40 times instead of 20 times, that is when I get the following error:

The block that I am spawning is called a grid_square_actor. This is one block that is actually all of the blocks; it has different variables that determine which specific block it is. For example (it is more complex than this, but yeah) a Stone block would be a grid_square_actor with boolean “is_stone” set to true. A soil block would be a grid_square_actor with boolean “is_soil” set to true. Only one of these block identifier booleans is set to true.

Each block has a different likelihood of being spawned, for example, soil is a 7% chance, while stone is 90%. Whenever I try and change the loop value in the for loops, the infinite loop error I am receiving is only appearing for certain quantities. That is worded weird but i’ll try to explain:

When I try and make the stone loop 28 times or more, I get the infinite loop error. However, when I try and make the soil loop, I can loop it up to 80 or so times before I get this error. It’s weird.

The infinite loop error always says (I think at least) that it’s coming from the “for each loop with break” specifically, and when I click on it to see what note, it brings me into the macro whatever of a for each loop with break. I only used one for each loop with break, to search through the occupied_tiles, so it’s got to be something with that. I just don’t understand where the infinite loop comes from, nor why it varies depending on the amount of times I loop, with different block types.

Here’s my code, if you would like to look. Thank you for all the time I appreciate it

You are iterating n-squared with your two nested for loops.
What is the outer for loop even doing for you? You don’t use its index at all.

In general, you should only need to visit each “thing” (square, block, actor, position, …) once. Almost every algorithm can be re-formulated to do this, and the ones that can’t, can generally be re-formulated to only visit a small additional number of neighbors, which can in turn be accelerated with the right data structure.

So, running a loop over 120 items, 120 times, looks like it will indeed do a lot of work, and there is a timeout limit on the blueprint interpreter, where it will decide that “you’re doing too much work to lock up the main loop, so I’m going to call it an infinite loop and break now.”

That’s what I was thinking, with your last paragraph about “too much work = nope.” The inner loop will spawn x amount of blocks in a straight line, in this example, it’ll spawn 120 soil blocks. If [] is a block, it’ll be like this: [] [] [] [] [] … 120 times. It represents the column.

The outer loop is for the row. It changes the z value to alter the specific row of the blocks being spawned.

The end result is supposed to look like this:

120 times
.
.
.
[] [] [] [] []
[] [] [] [] []
[] [] [] [] []
[] [] [] [] []
[] [] [] [] [] … 120 times

How else would I do it without nested loops? Would you recommend I split this up into multiple different loops, that loop less amount of times?

I’m talking about this guy:

As far as I can see, it changes nothing.
Although reading the nodes is kinda hard, because the resolution is just low enough that the text is all blurry …

In case the second update is the cluster of nodes after the “complete” pin on the second for loop, then perhaps the problem is that you’re looping too much across the array of … whatever it is, a vector array? There’s three total loops in here.

If that still takes too long, you can re-build this as a state machine that makes one row at a time, and inside your Tick() function, you run one iteration of the loop, until you’re done. You may even be able to do this in something like the Game Mode, such that you can prevent the game from fully starting until you’ve spawned all the actors you need.
(Of course, networking that many actors may be another challenge, if that’s the way you’re going – but if you spawn them all using a seed, without replication, that might be OK.)