Examples of good saving systems

Welcome to our brand new Clickteam Community Hub! We hope you will enjoy using the new features, which we will be further expanding in the coming months.

A few features including Passport are unavailable initially whilst we monitor stability of the new platform, we hope to bring these online very soon. Small issues will crop up following the import from our old system, including some message formatting, translation accuracy and other things.

Thank you for your patience whilst we've worked on this and we look forward to more exciting community developments soon!

Clickteam.
  • I am trying to create a good and robust saving system. It should track different values of active objects on different levels. For example, is chest no. 2 open or closed on level 5.

    I've created a system using the array object and list, and this works fine (attaching the file). However, even the code I've made is too complicated for me, making it challenging to debug.

    I think my main issue with the array object is keeping track of which "rows" and "columns" to use when writing new data or rewriting old. There are no functions to simply search and replace.

    Anyone know of any good examples? Many of the old files on the forum and click store does not work anymore.
    And is it worth considering INI++ 1.5? Does it support unicode now? I am considering just going back to INIs, at least I understand my own code with that object.

  • i always use array. and you are doing probably the best thing.

    ---but---

    you got to keep track of what you're saving and where.

    making either notes, (i usually will place a string in frame off to the side or make another dummy frame just to write information regarding values) coming back to a project after years, i can refer to my notes and resume.

  • Thanks yeah I may stick to array, but I think I'll stop using the Z-dimension for levels. Instead I'll make a messy 2 dimensional array that holds all the objects from all the levels and use the list object for search.
    This simpler approach seems pretty straight forward to me where I understand the code I write. However, the list object seems to be slowing it down somewhat...

    I only use the list object to look up an array position - where the array Y-position corresponds the the list line number. It is very unfortunate I can't simply look up an array position in the same way, because loading all the positions into the list does take a couple of milliseconds. No deal-breaker, but still not ideal.

  • you could think about smarter ways to save data into an array. you can even make it 1 dimensional, by doing a 1D string array, and saving data an upacking it with string parser.

    for example:

    saves "05,200,190,Gold" in array position 1 of X

    reading this you can unpack it, by getting the string, using parser and with delimiter, values could be:

    1st element= item #05

    2nd element = X position

    3rd element = Y position

    4th element = name

    etc.

    but you have to work this out in advance and plan it before you go too far.

  • What I usually do is mainly use string arrays (so I can store both numbers and strings), then run a foreach loop on the objects I want to save their data, X index is reserved for the ID of each object, while Y is reserved for different sets of data for each object, this will allow storing different data sets for each object without having to later parse this data when loading, which makes the loading process quite a bit faster, and even simpler in most cases (in terms of the Z index, I wouldn't recommend using it much, but you can use it to store different group data, and even for that I would usually go for another array if I don't need to save it in the same file)

    So for example, you have 4 objects and want to save their Count, Name, Xpos, Ypos and Layer pos:

    - Store the count of objects you want to save in an array index like X0, Y0
    - Run a foreach loop on the enemeis you want to save
    - On the foreach loop, store the Name of the object in array index X: Foreachindex, Y: 0
    - On the foreach loop, store the Xpos of the object in array index X: Foreachindex, Y: 1
    - On the foreach loop, store the Ypos of the object in array index X: Foreachindex, Y: 3
    - On the foreach loop, store the Layer pos of the object in array index X: Foreachindex, Y: 4

    As can be seen above, the X index was reserved for the ID of the object, which in this case is just the foreach index (but you can assign that however you like), while Y index is reserved for each unique set of data like Y0 for Name, and Y1 for Xpos..
    In case you want to load the data again, that can depend on how you consider loading as, but let's say you want to create the objects from the data stored:

    - Run a fastloop to load the objects in, run it for the count of objects times stored in the array (index X0, Y0)
    - On the fastloop, create the object by name from the name stored in array index X: fastloop index, Y: 0
    - On the fastloop, position the Xpos of the created object at the data from array index X: fastloop index, Y: 1
    - On the fastloop, position the Ypos of the created object at the data from array index X: fastloop index, Y: 2
    - On the fastloop, Move the object to the layer at array data index X: fastloop index, Y: 3

    This way, the objects are created with their data stored in the array upon the load call

    In case this still a bit hard to understand, I actually made a video tutorial about this before (sorry if the music or editing style is not the best, also, no voice commentary)
    Please login to see this media element.

    Note that you can mix and match both methods together, so you can still store multiple data sets in one array slot, seperated by some kind of delimiter, while also having some stuff stored in different Y indexes , this will enable greater level of flexibility..
    Just try parsing as little as possible when loading, because string manipulation is quite heaviy to process!

    Game/App developer, artist and a community contributor.
    You can support my work on: Please login to see this link.

  • Thanks Linky .

    I want to add only the relevant objects to the array. So for example, if the player has unlocked a chest on map3, that object should be saved as "open".
    Or do you mean that just saving and loading everything is easier to work with?

    This is my setup as of now. I use the list object so I can look up a specific object in the array each time I need to save or load that object. It works fine, but I have to load all the items onto the list at the start of each frame which comes with a small lag if the array is large enough.

    Please login to see this attachment.

  • im not sure if this would be faster, (maybe it might be)- rather than use your [LIST OBJ] to store that information, use a dummy object(eg, active) and use its ALT STRINGS for your list. just a thought if you're comfortable with searching your values the way you do.

  • I had the same problem and was all set to write some madness array based save file monstrosity. I had even planned it all out in Excel. It was going to suck, big time.

    Then I discovered "Save Frame position" and "load frame/application position". If you are using mostly stock objects and extensions this will work great. All I had to do is run a quick routine on load to place my backdrops again .

    Saved me COUNTLESS HOURS, but also may not work for some, probably many, projects. You might consider using it in conjuction with an array or something though. Like use an array to track all your non-clickteam objects, values, and settings. Maybe you can cut your workload down a bunch.

    I really wish Clickteam had a universal "save state" action though!

  • Thanks Linky .

    I want to add only the relevant objects to the array. So for example, if the player has unlocked a chest on map3, that object should be saved as "open".
    Or do you mean that just saving and loading everything is easier to work with?

    You don't have to save everything, just run the foreach loop that saves object's data only the objects that actually need saving, and only save the data sets that need to be saved, that means if you want to save the state of of chests on your game, you would run the saving foreach loop on them, and in the save foreach loop, you would only just store the state of them (that can be a flag or something like that)

    This is my setup as of now. I use the list object so I can look up a specific object in the array each time I need to save or load that object. It works fine, but I have to load all the items onto the list at the start of each frame which comes with a small lag if the array is large enough.

    Please login to see this attachment.

    Sorry but why use a list when you already use an array? lists are much slower than arrays, even when hidden and scrollbar disabled, and the fact that you use Arrays and Lists at the same time can slow things down

    It's actually simpler than what you might think, I already explained it above..
    In case you mean something different, please try explaining in more detail :)

    Game/App developer, artist and a community contributor.
    You can support my work on: Please login to see this link.

  • You don't have to save everything, just run the foreach loop that saves object's data only the objects that actually need saving, and only save the data sets that need to be saved, that means if you want to save the state of of chests on your game, you would run the saving foreach loop on them, and in the save foreach loop, you would only just store the state of them (that can be a flag or something like that)

    Sorry but why use a list when you already use an array? lists are much slower than arrays, even when hidden and scrollbar disabled, and the fact that you use Arrays and Lists at the same time can slow things down

    It's actually simpler than what you might think, I already explained it above..
    In case you mean something different, please try explaining in more detail :)


    I use the list object for two things because I struggle to do the same with the array. These two things are:

    1. Keeping track of where the next available cell/item is in the array. The list object allows me to simply add one line when I want to add a new cell/item. This list line number now corresponds to the new array cell/item number.
    I see from you example that you keep track of the number of cells/items on X0 Y0 Z0. This is great, and I think I now understand how to overcome issue #1

    2. If I want to save a specific object, for example the door locked/unlocked status of level 1, I must first search for that object in the array. The list object has a simple search function which allows me to look up a string that matches the specific object ID, this search functions then returns the list line number - which again corresponds to the array cell/item. I don't know how to resolve this, even with you example.

    I am attaching a quick dummy game with my save system with the array and list object. Note how I can unlock the door on level 1 through a lever on level 2.
    If you can, I would love to get some input on how to improve it! Both on how to not use the list object, and making the code even simpler if I have over complicated things.

    Attaching the file below. It includes a version with and without the "List view" (pro version only), which is great for debugging the array.

  • I am trying to create a good and robust saving system. It should track different values of active objects on different levels. For example, is chest no. 2 open or closed on level 5.

    I've created a system using the array object and list, and this works fine (attaching the file). However, even the code I've made is too complicated for me, making it challenging to debug.

    I think my main issue with the array object is keeping track of which "rows" and "columns" to use when writing new data or rewriting old. There are no functions to simply search and replace.

    Anyone know of any good examples? Many of the old files on the forum and click store does not work anymore.
    And is it worth considering INI++ 1.5? Does it support unicode now? I am considering just going back to INIs, at least I understand my own code with that object.


    I hear you regarding arrays. They are sometimes hard to do without, but they are horrible. They force you to work blind and therefore burden you with a distracting cognitive load as you try and keep track of menial things like what belongs where.

    Consider creating an Excel spreadsheet that mirrors your array, so that you can keep track of where everything belongs that way. I use an array to save my level layouts (my level editor saves levels as an array, then the game engine reads the array file to load the level). I keep track of the key rows and columns in an Excel worksheet that looks like this

    Please login to see this picture.


    The array is relatively simple, works well, and I haven't had to tinker with it for years now. I just leave it be, and the Excel worksheet helps me make sense of it when I need to. But when it came to building the actual player data saving mechanisms - like what you're talking about - I knew that I wanted to steer clear of using more arrays. So for saving actual player data, I only use INI++. Perhaps you have a lot more complex data that needs to be saved than my game does, but for me it works well.

    With an INI, you can of course keep information grouped in a more organised way. And with a good editor like Notepad++ and a little creativity, you can get it to format the data in helpful ways. For example, I get my code to automatically create [.......area name....] entries that segregate levels into areas, and I use notepad++'s User Defined Language feature to automatically style these dividers in a dark color.

    INI++ insists on alphabetising every group, so I get the Fusion code to put an item called zz__________ into every group, which will always wind up as the last item. I then tell notepad to use "zz________=" as a folding cue. The result is that every group in my INI can be neatly folded (ie. opened/closed with the - + buttons on the left):


    Please login to see this picture.

    Even if you had hundreds of items that needed saving, I don' t see why you couldn't have them all in an INI.

    You could have a hundred chests and other objects on one level, and just categorise them logically inside the group (which you can conveniently fold closed whenever you don't need to look into it)


    Please login to see this picture.




    INI++ is still non-unicode, unfortunately. However, LB has started making a Please login to see this link.. Work on that seems to have stalled, but I've heard that the parts that have already been built are working well.

    Please login to see this link.
    My Fusion Tools: Please login to see this link. | Please login to see this link. | Please login to see this link.

  • Thanks Volnaiskra . The regular INI object is pretty straight forward for me, but do you know of any good basic example for INI++ to get started? I don't understand what the level editor example that comes with the extension is trying to do, it doesn't seem to be saving and loading anything. And are there any benefits of using INI++ over regular INI?

  • Thanks Volnaiskra . The regular INI object is pretty straight forward for me, but do you know of any good basic example for INI++ to get started? I don't understand what the level editor example that comes with the extension is trying to do, it doesn't seem to be saving and loading anything. And are there any benefits of using INI++ over regular INI?

    I've not really used the regular INI object, but I imagine that the basics would be pretty similar to INI++. I don't know of any basic INI++ examples off the top of my head, but I'm sure there must be good ones out there. But the internal help file that comes with the extension is pretty good. If you want to use INI++ I'd suggest using INI++'s help file to help you figure out how to do the things you would already do with the INI object. And then explore some of INI++'s deeper features only as you need them.

    I'd not seen the example file before. It seems like it's showing off the Dialog Box feature, where you can view the contents of the INI directly in-game, as well as edit them in real-time:

    Please login to see this picture.

    I've actually never noticed this Dialog Box feature before, and it seems like it could be very handy. Though I personally prefer to view/edit my INI files in Notepad++.


    If you add the following event to the example, then you can save the INI as a text file. It will look identical to what's in the dialog box.

    Please login to see this picture.

    Please login to see this picture.




    The usual way that I use INI++ is to load an existing file, like this:

    Please login to see this picture.

    Please login to see this picture.


    And then when it's time to save it, you just use this action:

    Please login to see this picture.


    As to what the benefits of INI++ are over INI, it depends on what you need. If the features of INI are enough for your needs, then there's no need to use INI++. Have a look at the What's New? section of the INI++ help file to see a list of all the extra features it has. There are plenty that I've never used (eg. the Dialog Box from the example). Though there are some that I use extensively, such as the Searching and Sorting features. If you don't want players being able to easily cheat, then the Encryption feature will likely be important for you.

    Please login to see this link.
    My Fusion Tools: Please login to see this link. | Please login to see this link. | Please login to see this link.

  • Another idea to potentially consider is to use INI to output a human-readable version of the data for your benefit only. For example, you could decide to still use an array for saving your levels, but get the code to simultaneously collate and output the information into a separate INI. This INI wouldn't be used in the game in any way, but would just serve to summarise the most important info into a more easily-digestible form, so that you could see at a glance what's happening in the array. You could then consult this INI when bug-testing.

    I do something like this with my cutscene system. I have a fairly complex cutscene system that lets me 'direct' the action by altering a bunch of different parameters in an INI file. I can do all sorts of things by tweaking parameters inside 'story beats' in this INI file: create/destroy objects, change animations, scale, rotate, move them with easing, immobilise/restore the player, move the camera, add sounds, and so on. The system is easy to use on the INI side, and the engine does the heavy lifting. Here's an example of a short cutscene, with just a single 'story beat' showing:


    Please login to see this picture.


    The problem is that all these 'story beats' with all those potential parameters really add up, and the amount of information quickly becomes enormous. If some jump animation midway through a cutscene needs to be tweaked, I'd find myself struggling to wade through the file to (a) find the relevant cutscene, and (b) find the story beat that needs to be tweaked (is it the jump that happens at [IntroCutscene56] or the one that happens at [IntroCutscene73]?) I quickly realised that I was going to waste hours upon hours making these cutscenes, if I didn't have a better way to make sense of what's going on.

    So I made the code automatically generate a second INI that was only for me (the engine writes it, but never reads it). Whenever my Level Editor saves a level, it also parses any relevant cutscenes and outputs them in a particular format, as shown on the left below:


    Please login to see this picture.


    This is still pretty much gobbledygook, partly because I've made the engine put various strange character combinations like zb||, |=~, and =hidden into it. This is for 2 reasons. Firstly, because INI++ always alphabetises groups, it ensures that the information will be ordered how I want (eg. lines starting with zb|| will all be at the bottom). Secondly, I've used notepad++'s User Defined Language feature to tell it to format certain character combinations in certain ways. For example, character combinations like zb|| and zfooter become invisible, some phrases are made to pop (eg. NPC, Beats), while others (eg. stuff in curly brackets) are made to recede.

    The result, which is shown above on the right, is a summary of my cutscenes which (at least for me) is much more easily readable than it was before. All the metadata is at the top, while at the bottom is a simple short-form script of the cutscene beats, with key info in bright yellow and secondary info in dim biege. It's also calculated and provided some extra information that wasn't previously explicitly there (eg. it tallied up all the cutscene beats and calculated how many exact ticks the entire cutscene will take).

    The original format of Beat #10, with all its many (and mostly unused) parameters, took up 36 lines, as you can see in the first screenshot. But in this version, it's summarised in a single line (line #422)


    You could potentially do something like this with your saving system. As your code saves into an array, it could also save it into a separate INI. With some careful planning, this INI wouldn't just be an overwhelming dump of every single bit of info about the level (which is all your array would be even if you could see it, and I suppose is kind of what your current List Object approach may end up being too), but would have the information more nicely grouped. For example, all chests might appear in a single [Chests] group, and perhaps there'd also be entries that told you the total amount of chests present, as well as how many locked/unlocked chests there are.

    Please login to see this link.
    My Fusion Tools: Please login to see this link. | Please login to see this link. | Please login to see this link.

  • Thanks Volnaiskra !
    Your screenshot seems to show that your INI file is almost 7 000 lines. Is there any lag when saving or loading?

    And what do you select here, the options are a bit overwhelming and I don't quite understand what it means. I just want to save my progress in Appdrive$ + Appdir$ + "/save/save.ini"
    Please login to see this attachment.


    Btw, I think I found a way to use myPlease login to see this link. without the list object. I instead set up a loop that runs down the array and compares each line with the string I am about to add to the array - if it exists, then replace the line - if it is new, then create a new line.
    This is extremely slow and seems to crash the app when I add a couple of 1000 lines to the array. Using the list object for search is in fact way faster and does not crash the app.

    This is the loop code I use:

  • I haven't noticed any significant lag with using INIs. Possibly a fraction of a second when it first loads, but that's ok since it doesn't happen in the middle of gameplay.

    When you load a file in Ini++, the dialog that comes up is indeed intimidating. But don't worry about it - just use the default options, as shown in your screenshot, and hit ok. That's all you need to do.

    Please login to see this link.
    My Fusion Tools: Please login to see this link. | Please login to see this link. | Please login to see this link.

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!