Ahoy, i’m having an issue with making sure some playerprefs get saved when the app is stopped, by whatever means. I’m building for android using unity 2019.4.8f1
I have a function that calls Application.Quit(); when the hardware back button is pushed, but there’s no guarantee that people will push that button. They might just close the app from the task switcher, or force quit from OS settings, or maybe they just press the start button and leave the app in the background until whenever, or whatever other way there is to stop the app.
I figured if i just save the playerpref in all possible functions, it would get saved one way or another. I ended up adding a bool to check if saving has happened, so now only one of them will actually save.
But here’s the problem… OnApplicationPause seems to always be the one that happens first, but the playerpref does not get saved. When the app is started again, the last value for that playerpref is not restored.
If i comment out the other ones and only use OnDestroy, the playerpref does indeed get saved. But i don’t know if OnDestroy gets called in every possible quit situation.
So my question is, which function will always get called on android when the app is stopped, regardless of how it gets stopped?
Also, why would OnApplicationPause result in the playerpref not being saved?
That makes sense, and i guess it would be the same situation if the battery dies or the app crashes. I can accept that.
Yes, it gets initialized as false, and the debug statement inside the if condition prints to the log/console. So i know for sure that the if condition inside OnApplicationPause is firing, therefore the other code inside should also fire. But logging the playerpref in start just gives me the default, whereas using OnDestroy and logging in start gives me the last value as it should.
AFAICT, what you’re doing above certainly seems eminently reasonable.
How about isolate one new fresh property that you ONLY set in that OnAppFocus function and see if it changes and/or even gets committed in the first place?
One thing to consider is how long your app has between getting told it’s quitting and the OS yanking its resources. You don’t have forever, so if you’re doing anything that takes a long time it could be an issue.
I would have thought that saving one thing to PlayerPrefs would be fine, though.
There isn’t one.
What I would suggest is splitting your save logic out into its own function, and call that function on every potential path for the application being closed. It can also have a flag or check to see if data has changed since the last time it was called so that if it does get called multiple times in a row it’ll still only save once.
Depending on your game’s requirements, another strategy is to not wait for quitting to save. Instead, do it whenever meaningful stuff happens. Eg: player reaches a checkpoint, save the game. Then it doesn’t matter when they quit, anything significant they did was saved back when they did it. Furthermore, it reduces risk in long play sessions. For instance, what if my phone or the game crashes after 3 hours of play? If you only save on quit then it’s all gone.
Thanks, that was a good idea, and i was kinda expecting a totally new entry to act normally. I was even thinking the original one might mysteriously start working after that and i would just look like i was crazy. But unfortunately the results seem to confirm that something is weird.
I made a new playerpref in onFocus, then printed it to confirm it’s working and the default is being set, and i get 0, so all good there.
Then in onPause, i increment it, store it, and print it, and i get 1, so that seems good
But when it starts again, i get 0 and 1 again. It’s like any playerpref i store in onPause gets destroyed or corrupted somehow after it all executes. And it’s not like playerprefs just isn’t working, there’s plenty of others that i store at different times that persist just fine. As far as i can tell, it’s just in that pause function that it doesn’t work.
Yeah i’m not doing much there. I started with three things that i currently want to store there, but i slimmed it down to just the one for testing when i realized that’s where the problem was. Unfortunately i spent about 12 hours debugging everywhere else it gets stored before i was left with just the final save at quit time.
I’m doing that wherever possible. Things that get changed with just a click get stored right then. But i found that i can’t save directly in OnValueChanged for sliders. I guess it tries to do it too many times, too fast, and sometimes produces unpredictable or even illogical results. So i store those when the menu state changes.
But there’s a few things that can happen when no other events are guaranteed to happen. Like camera views can be directly changed on the fly with the gamepad, without opening the menu. I do store them when the menu is opened, but if nothing significant happens and the user doesn’t open the menu and just quits, then i want to store those few things that haven’t been stored yet. I just want to do it in a way that covers more than quitting via the quit button, because i’m guessing most people don’t bother with that.
That’s actually not really a thing. Unity scripting is single-threaded, one thing at a time, nothing can get ahead of anything else. You’re welcome to write a program that does nothing but write to PlayerPrefs as fast as it can.
I think you have something else going on. Again, isolate it to to a brand-new playerpref, bracket it with tons of Debug.Log() calls in 100% of the places that you change it, then retest.
The OnValueChanged(float) callback from sliders does NOT contain the value from the Slider.
You need to go get the .value field out of the Slider yourself. Is that your problem?
Note how this sample code does it, by using the bare (void) function callback:
Yeah that’s what i thought. It doesn’t block the UI or cause any other issue that i can see, but somehow it doesn’t always save the right value. I only noticed this after more than a few users complained about it and i finally decided to do some testing to confirm it. Sometimes it stores zero, on a playerpref that has a non-zero default, and sometimes it stores a number that’s outside of the range of the slider. But sometimes it’s correct. I’ve debugged the hell out of every place a certain playerpref gets stored and found no issue outside of these two places. I’ve commented out anywhere else it gets changed, leaving only one change at a time, to confirm that these specific places are where it’s failing. At first i was hoping to use the user reports to correlate it to certain OS versions or certain devices, but i’ve discovered that it even happens in the editor. I’m assuming it’s happening to a lot more people than just the ones that email me about it.
I put a function like this in the OnValueChanged event:
I’d already accepted that i can’t do that for whatever reason, which is why i started looking into saving it during other events that don’t fire rapidly, and also on quit. But because of these issues, i’m starting to think there’s just something wrong with my current unity installation of this version. It just seems weird that it could be so specifically borked, while everything else seems to work normally.
I’ve been using unity for almost 5 years now, with the same exact install of this version for at least 6 months, but i’ve never seen anything this weird happen, which is why i decided to bring it up on the forum.
My next update is now overdue since i’ve been spending so much time trying to solve this, so i think i’m just gonna roll with alternate saving methods for now, and then after this update is out, i’ll install the latest 2019 LTS and hope it starts working as expected. Though i’m also starting to wonder if maybe the length of my script, which is nearly 80k lines, could somehow be causing unexpected behavior. Basically i’m grasping at straws for any explanation at this point.
I don’t think saving 1 float to PlayerPrefs is very demanding. I’d probably either save it every time it changes, or if it changes often I’d save it every 5 seconds or so of gameplay. If I’m wrong, then forget it.
Yeah it’s not demanding at all. I timed saving one playerpref, and in this instance it takes less than 1ms to set and save, which is about 1/16th of a frame at 60fps. It can’t be that, but really the whole issue just defies logic. I’ll just do some creative saving to get a fix out, and then update unity and hope things start making sense again.
Yes, i do some file IO with images and text files, and all of that is working without issue on my test devices and in the editor. However i’m kinda nervous about doing IO every frame, which would be the case with storing values in OnValueChanged for sliders.
But playerprefs does work as expected as long as i don’t try to do it at quit time, so i’m not suspicious of the playerprefs class itself. I guess i could try saving to a file at quit time and see what happens. I could check it and use it to set the playerpref in start, and still use playerprefs at all other times.
If that works then i’ll roll with it for now just to get a fix out, and then i’ll try to replicate the issue in a blank project, and maybe reinstall or update unity to see if it starts working normally.
My point is to isolate if the issue is from PlayerPrefs, or from the logic using it. If you replace it with file IO calls and the issue persists then you know to debug or redesign your logic.
For sliders, just save the value when the slider is released.
Yeah i know what you mean. If for whatever reason i just can’t store playerprefs at the moment the app is shutting down, but there’s no issue with writing files, then i have a solution. I’ve already replicated the issue with a test preference that only gets touched at start and quit, so basically if the issue persists in that same scenario with IO, then there’s nothing in my code that will change that, since it’s happening after the last of my code is executed. In one of the snippets i posted above, the last thing that happens is saving and then getting the value and printing it. That showed that it was saved correctly, but then printing it in start showed the default again.
Unfortunately that doesn’t cover every time it can move. If people use VR gaze control to move a slider, the slider handle doesn’t register as being selected or deselected, which i’m guessing is because a raycast isn’t a mouse. I could probably come up with some way to make it work though, maybe using OnPointerExit, but i haven’t tried that yet.