Yes, game 2 will allow game saves. This is not necessarily an indication that the missions will be intrinsically longer, just a realization that some maps and some players sometimes need to be able to save a map and continue it later. I suppose it may also lead to a ‘gray’ market in saved game files, but that’s a price I’m willing to pay.
So, for the last few days I’ve been working on the save game infrastructure and it’s every bit the pain I remembered it to be. “Why so painful?”, you may say. Well, the state of the terrain, creeper, units, packets, and game metrics all have to be persisted and then reloaded. Sounds straight forward…. for the creeper just record the creeper array and then reload it… and same for the terrain. For most units there are variables that change (like its state, its position, its health, ammo load, etc). These numbers are easily persisted.
But then along comes something like a packet. A packet begins at some location on the map and then remembers the target unit that it is headed for. It has to remember its target unit because the target unit might be moving around while the packet is en route. The target could even be destroyed before the packet reaches it. So the packet needs to know where the target is at any moment. Naturally, I simply stored the object reference of the target unit in each packet during run-time.
This means that when I save a packet in the save game file I have to know what target unit it points to. To solve this problem, each of my units have UIDs (Unique IDentifiers). These UIDs are just integers and no two units have the same integer. So now a packet just needs to save the UID of the target when it is saved in the save game file.
Upon reloading, I have to look up the object reference of the target unit based on its UID. The packet does then when it is being loaded and created from the save game file. Of course this means that the target unit must already exist and have been loaded already! This means load order is important….
So, this solution works as long as cross object referencing is minimal and manageable. If two objects each held a reference to each other, no load order could resolve the issue. In this case, object construction would have to proceed “on demand” and not really in the order the objects are encoded in the save game file. Or, the assignment of the object references would have to be lazy and done at some point after all objects are constructed.
Anyway, technical issues aside, there will be save games… yay!
Save games, yaaaaaaaaay!
*cheers*
Just save the packets destination, rather than both location and destination. So whenever they load the game, the game will immediately remember the locations where packets need to go and will send them out normally.
When a game loads, I have to recreate all of the packets that were in flight when the game was saved. This means I have to put them at some location on the map, and then restore to them some information that lets them know where they were headed. So, I store the x,y of the packet, and the UID of the target unit. During a game load, the packets are loaded last (after) all other units are loaded. So the uid encoded in the packet can easily be matched against an existing unit.
The ability to save games would make those long levels just that little bit easier 🙂
What do you mean by a ‘gray’ market?
People will take the save game files and give them to each other. They will do this as a way to show others their solutions, to share good strategies (and good scores), or work together to achieve the best score, etc. Regarding working together, it allows a small group of people to work on a level in stages and improve their scores.
For instance, let’s say that a mission takes around 15 minutes to complete. The level can be played for 5 minutes and then saved and shared. That starting point can now be played for another 5 minutes by more than one person. Whomever seems to have made the most progress after another 5 minutes gets to become the next starting point for everyone else. Then the cycle repeats.
So there will be things like mission8-5min, mission8-10min, and mission8-15min save game files available on the forums (and everywhere else) and people can choose to pick one and start with it in order to try and get the best score.
Now is this a bad thing, or just another level to the game? I’m going to choose to view it as another aspect of the game that allows people to work together. But, experience has taught me that anything that involves group interaction usually contains some surprises…
If they wanted to show each other their solutions and strategies, wouldn’t they just record the mission and post the video on Youtube?
Working together, though…I could see some serious potential for contests coming out of this….
You could probably fix the save market issue by requiring that it be opened by the same copy of the game that saved it and deleting the stored value when you open it, as well as embedding it into the same files that the game normally keeps data on scores, maps, etc., preventing people from easily save-scumming (Using the save feature to keep trying parts of a level until they do it perfectly). It would still be possible to do the save-market type thing, but it would be much more difficult and time-consuming for all of the people involved. The only question is how much effort it would take for that to happen.
One small edit.
The game end hen saving so you can’t save mre times.
If you really wanted to prevent this, I guess you could include a hash of the activation key in the saved game.
Trading saves should be perfectly legal in any situation. The hash code method is a bad idea. What if I bought two copies (1 per computer), I can’t trade saves b/w machines? That’s not cool.
If you’re going to enforce licensing like that, you should do it like steam/blizzard, where your key is registered to an account, and the client ensures account credentials before it starts. That would also allow players to have online record of their achievements, without which, they just seem like a tack-on for perfectionist.
I understand you’re worried about the online leader boards, but instead of trying to make saves more restrictive, you should restrict leader board scores so that they can only be earned from a continuous playthrough. That way the boards actually reflect skill and endurance, instead of just micromanaging every second until a perfect save is achieved.
[…] Save Games […]
Great! It will definitely increase the replayability when you can skip the energy generation part the second time and concentrate on fighting the creeper to improve your score. 🙂
Regarding the implementation: I’m not sure why you call assigning the references in a later stage ‘lazy’. This practice is very common when you for instance read many related objects from a database (which is no different than from a file). Just cycle through them after loading the basic info of all objects and assign the pointers/references by using the related UIDs. A good (indexed) Find-function will speed up this process a lot, so I hope your data structure provides something like that.
Of course a recursive method (and order of saving your objects) looks kind of neat, but you start getting into trouble when an object is referenced multiple times (one-to-many relationships, not to speak of many-to-many relationships). I would certainly avoid the potential problems this could introduce. Doing that second step will also allow you to make sure all objects are there and every reference is valid. Another advantage will be that all objects will be grouped nicely together in the data file. When you have all your dependencies straightened out (a DB design tool might help here), the save/load order will be evident.
Good luck solving your puzzle!
This is the best idea for long maps.
Thanks for these blog posts! I (and apatently many others) enjoy nerding out by reading them! I find these posts very interesting! Thanks again!
Matthew
AKA: Katanna
UID’s are necessary in saving a game where targets need to be persistent, but there shouldn’t be a reason you can’t easily resolve a circular reference.
You just need to maintain a list of items which have unloaded references, then each time you load an item, check the unloaded references for its UID, and fill it in. By the time you load the last item, all the references will be available(assuming the save file is valid), and the last of the missing references will be filled.
The other option, which would require a more complicated save method, would be to store all the target references in a separate section and apply them after the objects are loaded.
I would be more inclined to take that approach, since I would also want to separate sections for common data, such as positioning, hp, etc., from unit specific data such as special emitter speed. That way the loading process is organized by property type, instead of whole objects
Why don’t you make it so that after you load a saved game the saved game gets destroyed?