How do I make sense of the logic with local storage?

  • Ok so up front I've used local storage and i used web storage when that was a thing.. it's all basically the same. i understand the ideas behind it.. "exists" "missing" etc.. how to reset..

    but it seems like there is still some fundamental issue where things just don't fire off like they should and for the life of me i can't make sense of it.. what i end up doing is overengineering some constraints.. variables.. triggers.. wait timers.. etc.. to try and get it to behave.

    I can get a single local value to save / recall and work but once i introduce a 2nd one i run into weird behaviors and I just can't understand what's going on.

    here are some shots of my current (convoluted) logic to get something to behave itself.. still not working and there must be a way to get it to work reliably with less mumbo jumbo..

    to explain how it behaves currently.. if i change both of them to be 0 or 1.. it's fine.. if i change only the text one to 1.. then neither go to 1 when i reopen.. but if i change the audio one to 1, then they will both be saves as 1 when i reopen.. i don't get it.

    https://www.dropbox.com/s/609tmk9ytvcs1ak/local%20storage%20example%201.JPG?dl=0

    https://www.dropbox.com/s/3m31rg93l0myg10/local%20storage%20example%202.JPG?dl=0

  • The first screenshot is fine, although there is an easier way to invert a boolean - Set b to (b=0)

    The second screenshot is totally wrong. You can't use "get item" and access LocalStorage.ItemValue in the same event. You should not put local storage triggers as sub-events. "Item exist" and "Item missing" will not be triggered by themselves, you need to execute "Get item" or "Check if item exist" from some other event, for example on start of the layout.

    Also, if you need to store multiple variables, it's much easier to put them into a dictionary and store the entire dictionary as one key. See these demo projects:

    dropbox.com/s/1cz1k5x3teikbv5/DictionaryLocalStorage.capx

    dropbox.com/s/f2d7lc8o4zajgeg/LocalStorageDemo.capx

  • Thank you for this! I definitely am going to try this today and see how far i get. i knew my solution "as is" was over the top.. i was just finding myself going down a rabbit hole of experiments to help control exactly when things were firing off or not. getting strange behaviors and trying to figure out exactly where things were going wrong.

  • Yeah, with Local Storage you can't just experiment and see what's going on. You need to study the documentation and follow certain rules.

  • Hey so I've been looking at the example shared which IS really awesome to see as it does show a local storage saving and such but i'm still struggling to get it to work as I need it too which is slightly different. In the example it works for me so i see it working like that, but in my case i'm dealing with settings.. and i'm needing to check when the app opens to automatically look vs hitting a go buttons which would seem easy to transition but my tests are still coming up short.

    I read the documentation for local storage closer and they too suggest the dictionary and that's great, but my situation just doesn't quite behave like the examples and my attempts to simulate them to line up with the examples are leading to unsuccessful attempts.

    The basic thing i need to understand is unlike your event which has a go button.. i'm doing "on start of layout" and it still feels like i'm not getting a clear idea of order of operation..

    1. you check if "mystorage" exists.. in the "mystorage" missing you just have "" for text.. so when a first time condition happens and "mystorage" is missing.. does it automatically create "mystorage" and instantiate it?

    2. when you confirm "mystorage" exists you get "key1" and "key2" but there is no value in them for the very first time. I need to define a value (i think) to make sure that my logic for the options state in the options screen to be correct. trying to figure out an elegant way to set global variables because my logic currently listens to a "texton" var and "audioon" var that is just 0 or 1..

    i'd like to think i can get value from the key instead, but so far that wasn't working.. but it was hard to know if the key was being set correctly in the first place. i'm just not sure if that's that right thing.

  • Here is how I usually do this, I read LS from Loader layout and after the data is retrieved/missing (Level variable becomes>0), I switch to the main layout. The dictionary is only used for easier storage, all keys from the dictionary are copied over to global variables.

    You can simplify these events further with this new feature:

    youtube.com/watch

  • Nice example.

    One slight variation I am using is replacing the Dictionary object with a JSON object, which I have found to be more versatile for structured data (objects within objects, arrays, etc.)

    An example with a JSON object named PlayerData, change the save and load actions, then use JSON plugin expressions and actions to access the data in the JSON object.

    Load:

    -> PlayerData: Parse JSON string LocalStorage.ItemValue

    Save:

    -> LocalStorage: Set item "PlayerData" to PlayerData.ToCompactString

  • Mikal raised a good point about creating data with the JSON plugin. JSON allows you to create any combination of arrays and dictionaries you like, so for complex data it's much more versatile than the dictionary and array plugins.

    Under some circumstances I would expect the array and dictionary plugins to be faster, as the "path" abstraction we use for accessing JSON data in the plugin adds some overhead. Whether this speed difference matters is down to your circumstances. Your likely to speed up your development process by having more descriptive data structures, so it's a bit of a trade off.

    You can also use combination with the array and dictionary plugins, which can help in some situations. In the procedural terrain generation demo we store the "chunk" data in a JSON object, then convert it to a string and place that into a dictionary. This makes the interactions with the JSON data object simpler, and means we can quickly check if the chunk exists in the dictionary.

  • Nepeo

    Thanks for the information and ideas of mixing dictionary and JSON. As you say for speed of dev purposes, I tend to push to JSON instead of globals, dicts and arrays. It's good to see where it's used in terms of perf impact. I will have to make some pref test projects to compare the speed of access (like full path access to an array buried a few keys deep vs just accessing an array object.) Does setting the current path for JSON plugin help speed this up some, or is that just a shorthand that helps speed up dev?

  • and I will say that i've been reluctant to use functions because i never really understood why i need them since it seems like when something happens doesn't everything fire off inside anyway?

    the only reason i could see using a function is if you see a certain group of events firing off the same way in different places but even then that seems like it's rare because most situations are different anyway.

    Is there some other key reason functions are needed?

    Also I saw you use an action I'd never noticed before.. "wait for previous action to complete".. in my case i was using "wait 0.1 sec" to kinda insure that things fire off in order, but this action looks much faster and controlled.

    Also when you list a series of things for example "on start of layout".. is it always sequential? I've heard it's not with JavaScript. Hence why i was using "wait 0.1" to insure things fired off in order.

  • part12studios Functions help to keep code organized and optimized. My function "StarageSave" in the example above can be called many times from different parts of the game. For example, when player finished a level, made some purchase etc.

    You didn't notice "wait for previous action to complete" before, because it was only added in version r157 :) See that video link I posted.

    Using "Wait 0.1" with asynchronous events is not a good idea, as sometimes the delay may be bigger than 0.1s

    All actions inside one event are executed in sequential order. I'm not sure I understand what's your concern here.

  • Mikal Testing performance is probably a good idea, but be careful your not just looking at a component in isolation. It's better to profile an actual game and see where your most expensive areas are. Making things faster is great, but if you halve the time spent doing something that uses 1% of your CPU time in an area that isn't time sensitive then it's a bit of a waste of effort.

    In terms of understanding it's actual perf. of the JSON plugin there's lots of small optimisations, but you can fall onto the slow path by doing some unexpected things. Setting the current path doesn't affect a huge amount in terms of speed, but it is stored in it's parsed form so that section of the path doesn't need reparsing. Might make some situations faster I guess.

    Using the JSON ForEach condition is faster than accessing each element as it only has to resolve one value ( the array/object ) instead of each child. When parsing paths it will cache the last parent object, the parent being everything other than the last key in the path:

    path = "a.b.c.d"
    parent = [ "a", "b", "c" ]
    child = "d"
    

    So if your only changing the child part of the path it's faster. When accessing arrays with push/pop the full path is cached instead, so the array only needs to be resolved once.

  • cool yea i see your point about saving certain things like general progress at various times and locations in a game. In games i've done in the past i've never really needed that, but i can see now how that would be helpful.

    Yea i noticed that wait for previous action is new soon after i posted that. definitely a much needed thing. i've always wondered about that because "wait till any action is complete" to fire off another action would be super helpful. like imagine being able to loop any action by name to say when THIS is done.. do that.. this doesn't

    As for the the wait 0.1.. i didn't know things don't always fire off that quick. so yea this is even better waiting until the action is complete. exciting (and great timing) to have this.

    as for the sequential thing I thought I'd heard that it didn't somewhere, but glad it's not. I'm pretty sure it was more of a general statement about javascript itself, not construct specifically so yea a myth i'm glad to learn can be dispelled.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • "wait for previous action to complete" is only intended to work with asynchronous actions of certain plugins, like AJAX, Local Storage, Pathfinding. (look for the clock icon on the event sheet). It basically replaces "On completed" event. It won't help you with other things.

    Local Storage actions can easily take longer time on a slow device, or if you are saving/loading a lot of data etc. AJAX Request can take several seconds when you are requesting a file from the internet. Pathfinging Find Path can be quite slow on a large map, and so on. So yeah, never use "Wait 0.1s" with asynchronous events.

  • that's great to know! I'm going to see what i can do with this information this afternoon. thank you so much for the added information and insight. i remember using and struggling a little with webstorage back when that was a thing, but i did get through it.. this seemed similar enough, but the weird thing i was experiencing was this, at it's best:

    i would save text on off to on and sound off.. reload the app and both would be 0.. then i could do things like save text to off and sound on.. and reload the app and both would be 1.. however if both were off.. both would be off.. and if both were on it would be both on.. just weird behavior.. even though each had it's own value name.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)