Sign in to follow this  
Followers 0
Angelic Ice

Grid-based game: Serialising position

19 posts in this topic

Hello forum!

I have a grid, let's say 20x20. Every object (but GUI and alike) are part of this grid.

Since every grid-slot has a fixed size, I can easily determine whether a mouse-click interacts with a grid-slot. But there is one problem with this:

Do I serialise the grid-slot or the actual-position?

Actual position:

+ Easy to deserialise, since no further conversion is required, coordinates are pretty much done.

- It is unclear on where to insert this onto the grid, would require calculation.

Position in the grid:

+ Easy to place on the grid.

- Requires calculation to find the true coordinates.

Serialise grid as well and keep coordinates as values per entity:

+ Contains everything needed.

- Data redundancy, because both values could be converted anyway during runtime.

- Slower serialising and slower deserialising.

 

I assume serialising the grid-position for grid-elements is better?

Not really sure how to progress or what class should be responsible for interpreting/converting/changing grid-positions to real coordinates.

 

Thanks for your time : )

0

Share this post


Link to post
Share on other sites

The game logic evaluates the grid/slot-position : ) But instead of accessing the actual entities, it uses the datastructure-vector/list/... storing them.

Coordinates-values pursue two use-cases at the moment:

1) To help the loader to insert them into the grid-datastructure.

2) To be displayed on the screen, because at some point, the painter-class will iterate over them and render them, wherever they are at the moment. One entity would be the player, in order to allow smooth transition between two tiles, I require the actual coordinates that can be transformed/synced with their animation.

Edited by Angelic Ice
0

Share this post


Link to post
Share on other sites

I'd store the actual positions, and not the grid. The advantage is that you can change the grid without breaking all savegames (or if you prepared for changes in the savegame format, without a savegame change).

2

Share this post


Link to post
Share on other sites

Oh, but how would you identify where to place an entity within the grid-datastructure?

Would you simply calculate that?

E.g. coordinates are x = 64 and y = 64, one grid is 32x32. Simply doing x/32 and y/32 would give me the grid-slots. But that sounds so awkward to do, haha.

Sadly, I cannot rely on the order within the serialised file, as there might be empty grids and these are of course not part of the savegame.

0

Share this post


Link to post
Share on other sites
20 hours ago, Angelic Ice said:

Oh, but how would you identify where to place an entity within the grid-datastructure?

Would you simply calculate that?

E.g. coordinates are x = 64 and y = 64, one grid is 32x32. Simply doing x/32 and y/32 would give me the grid-slots. But that sounds so awkward to do, haha.

Why is that awkward? You claim you are not going to store empty girds. Therefore, apparently, the grids themselves have nothing of interest to store, only the objects they contain are of interest.

So create the grid structure first, and load the objects into them.

 

20 hours ago, Angelic Ice said:

Sadly, I cannot rely on the order within the serialised file, as there might be empty grids and these are of course not part of the savegame.

How is that a problem? You won't get an object for an empty grid, or it wouldn't be empty.

Of course if you don't store empty grids, then after loading you have a bunch of spots without grid, so you can't go there in the game, unless you fill the holes in some way before you need them.

2

Share this post


Link to post
Share on other sites

Yes, not storing empty grids is actually a problem for me. Let's say we have one row of this grid, slot 1 2 3 and 4. Now, 1, 2 and 4 are filled, therefore not empty. Only 1, 2 and 4 are being serialised, because why store empty spots?

Sadly, when we deserialise the file, these contents lack their original grid-position as e.g. x = 32 and y = 0 mean nothing in a grid-world. The first object would go in grid-slot 1, 2nd in 2, but the original 4th positioned object would go in grid-slot 3, because there is nothing left. First read first placed, kind of sorting behaviour. Because the level-loader cannot know, whether a grid will be used, the serialised-file contains no information about what grid-slots to avoid.

I define empty as, no object has been inserted at a given grid-slot. A user could create a 20x20 grid, but use only a few slots within this 20x20 grid. Hence not used grid-slots are empty.

2 hours ago, Alberth said:

How is that a problem? You won't get an object for an empty grid, or it wouldn't be empty.

At the moment, the game cannot know if a grid will be empty. It would be just a NULL'd element within a list/vector. That is the actual problem for me.

Should I just serialise empty grid-slots? Sounds like an immense waste of space, but that would allow me to serialise in order (grid-slot 1 to last) and deserialise in the same pattern.

obj_1 = { .. some values ..}
obj_2 = { .. some values ..}
obj_3 = { empty }
obj_4 = { .. some values ..}

 

2 hours ago, Alberth said:

Why is that awkward? You claim you are not going to store empty girds. Therefore, apparently, the grids themselves have nothing of interest to store, only the objects they contain are of interest.

To re-interpret coordinates to grid-slots just sounded odd to me, but that technique would help me to avoid serialising empty grid-slots. And math is probably faster than meddling with disk-speed, haha.

0

Share this post


Link to post
Share on other sites

Anything with IO is slow as hell anyway. You can add XML encoding of the information to make extra sure it's slow enough :P

Tbh, I am somewhat surprised that the grids themselves have no coordinates, then you could store them at the right spot. The simpler option is however likely just serialize empty grids too, it's empty anyway, so not much additionally content gets written.

You seem to worry much about size and speed for no good reason. Disks are always the limiting factor rather than processor speed, I don't think it's possible to keep a CPU hot from writing a file to disk. Also, disks go for 1TB / 50 euro, and likely even less nowadays. I'd start worrying when you get savegames of around a GB. Until that time, disk-space is a non-issue.

1

Share this post


Link to post
Share on other sites

I decided to go with a vector/list, because I do not need any physics-calculation but "give left/right/... neighbour of grid-slot"-logic.

Oh, I worry about size and speed because this application should run on phones, too. Just trying to provide rather light-weight level-files and keep loading/saving times low, as multiple levels could be downloaded.

Overall, I just got curious if there is already a similar way of handling this case : ) Thanks for your input!

 

0

Share this post


Link to post
Share on other sites
8 hours ago, Angelic Ice said:

I decided to go with a vector/list, because I do not need any physics-calculation but "give left/right/... neighbour of grid-slot"-logic.

That seems like a good idea, and indeed you don't need coordinates in a grid there.

That does not mean however that you cannot have coordinates in a grid in a file, imho. Nobody said there has to be a 1-to-1 mapping between an object in memory and that same object in a file.

I usually design both cases separately (while keeping in mind that making them more equal simplifies matters), and then write a conversion between both formats for loading and saving.

1

Share this post


Link to post
Share on other sites

Oh, interesting!

Well, the issue is, if the player-object wants to move from field 2 to field 3, there will be an animation that transforms the position of the player-object. If a serialisation occurs during this progress, the player will be saved in-between. Even worse: Imagine the playing stopping the game just briefly after saving has been successful, what would happen if the player wants to load this later?

I could of course say, once an object's centre passes the border to a new grid-slot, it will automatically moved to that slot within the vector/list/.. . Therefore, when adding objects from file to field, I would have to calculate, where each centre-point fits in.

This avoids having empty slots in the file, which is ideal, as the average simple calculation is, as you already said, faster than any read/writes. Especially, since empty-objects have to be written to the file and read from it. While calculating allows me to simply map coordinates to slots only during loading, while saving only has to write the actual coordinates.

I really like this approach, thanks a lot for helping me out : )

 

0

Share this post


Link to post
Share on other sites
19 hours ago, Angelic Ice said:

Well, the issue is, if the player-object wants to move from field 2 to field 3, there will be an animation that transforms the position of the player-object. If a serialisation occurs during this progress, the player will be saved in-between.

I don't see any issue, tbh. Conceptually, a save+load is a no-op (well, that's how you code it usually at least). That is, after load, you should get the same situation as that you had if you never saved+loaded. I don't see how save affects player movement.

Right now, you have a way to move the player. You may keep the player in one grid, and at the end of a move assign it to the new grid, or you can continuously update its position, and it changes grid when it passes the border, or whatever.

save+load is a no-op, which means you restore to the exact same situation that you had when you pressed "save". If you move the player to the new grid at the end of the move, then the player is logically in the originating grid until the very end of the move. The destination grid is then empty until the moment that the move ends. That is what you want to happen after load too, since "load" acts asif the movement was never interrupted.

The fact that the display suggests that the player is in a different grid position than it logically is, is a display-trick in that case, fooling the user into believing a smooth move (which is fine). This is however following from your decision how you move your player, not from any save or load.

19 hours ago, Angelic Ice said:

Even worse: Imagine the playing stopping the game just briefly after saving has been successful, what would happen if the player wants to load this later?

I have no idea what worries you have here. When you press save, the game-state at that moment is saved, such that you can load it back to the exact same situation later. What happens after a save is not part of the saved game-state, and thus won't happen if you load that state again. It's no different then you're standing next to a gem, you press save, you pick up the gem, and you load the save. The gem is back on the floor and not in your inventory.

19 hours ago, Angelic Ice said:

I could of course say, once an object's centre passes the border to a new grid-slot, it will automatically moved to that slot within the vector/list/.. . Therefore, when adding objects from file to field, I would have to calculate, where each centre-point fits in.

How are you going to restore the exact same game state from that? It seems overly complicated to me.

Of course, technically, there is no need that save+load is a no-op. You could restore to an equivalent state instead, and continue from there. That however complicates matters since you then have multiple ways to express the same thing, which means all code has to deal with each way that may happen. That usually quickly explodes into toooo many options to deal with.

19 hours ago, Angelic Ice said:

This avoids having empty slots in the file, which is ideal,

Huh? All slots are always filled, except during a move?

 

1

Share this post


Link to post
Share on other sites
2 hours ago, Alberth said:

Right now, you have a way to move the player. You may keep the player in one grid, and at the end of a move assign it to the new grid, or you can continuously update its position, and it changes grid when it passes the border, or whatever.

save+load is a no-op, which means you restore to the exact same situation that you had when you pressed "save". If you move the player to the new grid at the end of the move, then the player is logically in the originating grid until the very end of the move. The destination grid is then empty until the moment that the move ends. That is what you want to happen after load too, since "load" acts asif the movement was never interrupted.

 

2 hours ago, Alberth said:

I have no idea what worries you have here. When you press save, the game-state at that moment is saved, such that you can load it back to the exact same situation later. What happens after a save is not part of the saved game-state, and thus won't happen if you load that state again. It's no different then you're standing next to a gem, you press save, you pick up the gem, and you load the save. The gem is back on the floor and not in your inventory.

Maybe this is a part of my problem. I cannot really think of a smart way to serialise actions (player-inputs and resulting game reactions), as my game is mainly done round-based. An input, as a mouse-click to a grid-slot, will run a walk-script, triggering an animation-script and so on, the player has to wait until this has been finished. If the game suddenly needs to be saved, serialising this momentum becomes quite a problem for me.

I barely ever saw a game saving currently processed actions (as jumping, attacking, ..), as to what I experienced, saving is a limited to only certain spots in a level (e.g. save-points) and usually serialises map-data and flags only.  I totally understand the reasoning behind this, players usually opt-in -out of a game's session and probably do not want to save mid-action.

An example for a mid-action: When the player moves from grid-slot 1 to 3, and the user suddenly shuts the phone hence the game tries to save automatically mid-motion/action. How would a move-to-new-slot-task be serialised? One idea would be having a action-task-scheduler (not meant in a multithread-context), that owns currently executing instructions, as

player_moves_to_new_grid_slot, owning values as: running_script and the script's required arguments as goal_slot.

entity_attacks, owning values as: running_script and again the script's required arugments target.

If I would not serialise these task, the transformed player-coordinates would just be loaded in-between grid 1 and 3 but not complete the action.

I mean, I could also have live-coordinates and old-coordinates and simply ignore live-values, therefore dismiss incomplete tasks.

Edited by Angelic Ice
0

Share this post


Link to post
Share on other sites

Oh, you have active code that you want/need to save. Hmm, I have seen that in CorsixTH, where almost literally all Lua data is saved by traversing the links. The function code itself doesn't get saved, only an unique name, so it can be re-connected upon load.

It works, but that's about all you can say about in my opinion. Changing anything is a nightmare, as values of variables get attached to their name, so if you rename a variable and load an old save, it crashes. If you add new variables, it crashes.

If you never want to modify your game, and still be able to load old saves, the above is no concern of course.

 

The other option is to attach a "save" and "load" interface to an action. If you save a running action, you query for the data that you must save (and the name of the action of course). When you load, you load the saved data, and recreate the action from it.

 

I don't know how relevant save at any time is for you, but the simpler option is perhaps only to save at a point where there are no such actions that you must rescue.

1

Share this post


Link to post
Share on other sites

Hm, yes. I would totally fine by not saving the actual movement. As you said, either A1 or A8.

There are still some problems for me though. When the movement happens, the coordinates are being transformed already, saving  during an action would resolve in saving those coordinates.

That might lead to errors: Maybe the character moves over field 3 and 4. But field 4 is a hole hence the character falls into the hole.

If the game would be saved during the walking and then opened again, the character might stand on top of field 4, bypassing its fall-script.

In either case, it sounds as if games contain two kinds of values. One set, that can be serialised and another one that is live and can be transformed?

0

Share this post


Link to post
Share on other sites

I find it helpful to think of a turn-based game from a functional perspective.

You start with game state X, apply player input A using some game logic, and end up with game state Y. X is always known at any given time, but A is something you have to wait for. In other words:

Y = gameLogic(X, A)

If the player saves before providing any input, then all you have is X. In that case, just save X. If the player saves after providing A, then you have X and A. Save X and A, even if game logic is currently running. Once the game logic is complete and you have Y, then Y for turn N immediately becomes X for turn N+1. Rinse and repeat. Note that this applies generally to any transition between valid game states, not just "turns" (however they may be defined by your game).

If you load a game that only has X, then just restore that game state and continue waiting for player input A. If you load a game with both X and A, then you restore the game state and apply input A as you would normally.

As you can see, it doesn't matter when the player saves, because this process is invariant on how much time it takes to perform the game logic and produce Y. You always save X, and sometimes A if it's available.

Edited by Zipster
Clarifications and formatting
1

Share this post


Link to post
Share on other sites

Ah, so you suggest to serialise A as well, if X is continuously transformed towards the final state of Y.

So, X could resemble a tuple of coordinates and A the player-input (e.g. a click on the grid), which translates into a move-order.

I should attach this instruction-set to the owner of X and upon saving, serialise both? So coordinates slowly transform...

Start is x = 10 then x = 11, x = 12, ... goal could be 20. And the instruction-set would be continued, being some script. Probably should read into how other games tackle this, as I want to keep high customisability.

Serialising a spin-animation, followed by a teleport/blink to a grid-field during action, might be difficult.

Thanks for all the inputs : )

0

Share this post


Link to post
Share on other sites
11 hours ago, Angelic Ice said:

Hm, yes. I would totally fine by not saving the actual movement. As you said, either A1 or A8.

There are still some problems for me though. When the movement happens, the coordinates are being transformed already, saving  during an action would resolve in saving those coordinates.

So, don't do that. The real game state is what you need to save, so don't change that. Change the visual state, which you don't need to save. These should be 2 separate pieces of data. You don't need to have a single "coordinates" variable which you use for everything.

 

11 hours ago, Angelic Ice said:

That might lead to errors: Maybe the character moves over field 3 and 4. But field 4 is a hole hence the character falls into the hole.

If the game would be saved during the walking and then opened again, the character might stand on top of field 4, bypassing its fall-script.

I'd say that this is a problem with the way you are handling the logic. If you know that moving from field 1 to field 8 is a single operation, then you can determine right at the start that you will pass through field 4 and that the character is going to fall in. You don't need to animate them over that position to determine that. Your game rules should know that, at the end of the turn, that character has gone, and that the animation is just for effect.

But, if you truly can't handle this without animating the character and seeing exactly where they appear in pixel-space, then maybe your tiles aren't really the true game space. It would be wise to think deeply on this before you proceed because it will have serious implications for the rest of the game. If you do need this finer level of control then your game state may well be at the visual/pixel/world position instead; in which case, you go down the RTS route, storing the command alongside the unit until the unit finishes moving. But I suspect that is not actually what you need.

 

11 hours ago, Angelic Ice said:

In either case, it sounds as if games contain two kinds of values. One set, that can be serialised and another one that is live and can be transformed?

Exactly, but you're thinking about it the wrong way. The data isn't divided up by "what is serialised" vs "what is not serialised", the data is divided up by "what is an essential part of the game state" vs "what is just changing temporarily to make the game look pretty". You always save the first set of data when you're saving the game state, because it's essential for recreating that state when you resume/load the game. You don't need to save the second set of data because its current state has no effect on play.

 

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0