Game Development Asked on November 2, 2021
Our game includes an in-game level editor. When the user saves a custom level, the level is serialized and written to a file:
public void SaveFile<T>(T obj, string path) {
if (!typeof(T).IsSerializable) {
throw new ArgumentException("Tried to save non-serializable type " + typeof(T).Name);
}
FileStream fs = null;
try {
fs = new FileStream(path, FileMode.Create);
DataContractJsonSerializer serializer = GetSerializer();
serializer.WriteObject(fs, obj);
} catch (Exception ex) {
Debug.LogException(ex);
} finally {
if (fs != null) fs.Close();
}
}
Serialized files can be loaded like this:
public T LoadFile<T>(string path) {
if (!typeof(T).IsSerializable) {
throw new ArgumentException("Tried to deserialize non-serializable type " + typeof(T).Name);
}
if (!File.Exists(path)) return default(T);
FileStream fs = null;
T result;
try {
fs = new FileStream(path, FileMode.Open);
DataContractJsonSerializer serializer = GetSerializer();
var result = (T)serializer.ReadObject(fs);
} catch (Exception ex) {
Debug.LogException(ex);
throw ex;
} finally {
if (fs != null) fs.Close();
}
return result;
}
When displaying a "Load" menu, we enumerate the saved files like this:
public List<T> LoadFiles() {
if (!Directory.Exists(path)) {
Debug.Log("No save files found");
return new List<T>();
}
var files = Directory.EnumerateFiles(path);
Debug.Log("Found " + files.Count() + " files");
List<T> result = new List<T>(files.Count());
foreach (string file in files) {
Debug.Log(file);
T obj = LoadFile<T>(file);
if (obj == null) Debug.Log("File at " + file + " loaded null");
if (obj != null) result.Add(obj);
}
return result;
}
I have omitted a lot of code for brevity, but what is shown above should cover the important parts.
In the Editor, everything works perfectly. However, in WebGL builds (where files are saved to/loaded from the browser IndexedDB), there is a strange quirk: the file doesn’t remain in IndexedDB unless the user changes to a different scene after saving.
Scenario 1:
File saved to /idbfs/abc123/CustomLevels//FileName
Scenario 2:
File saved to /idbfs/abc123/CustomLevels//FileName
The browser console log suggests that after Bob refreshes the page in Scenario 1, the file no longer exists. If this was the only scenario that Bob had saved, we see the message "No save files found". If Bob had previously saved other scenarios and used the workaround of returning to the main menu scene, those previously saved files will appear in the console and save list, but the most recent file is missing.
Why would the save file disappear from the IndexedDB if the user leaves the webpage without changing scenes, but remain in the IndexedDB if the user returns to the main menu scene before leaving the webpage?
D'oh! This is covered in the WebGL troubleshooting documentation (which did not come up in my web search results for 'Unity IndexedDB').
Unity does not flush changes to IndexedDB immediately when you save a file. They don't explain when they do a flush, but clearly a scene change is one event that triggers a flush.
To immediately flush the changes, we have to run a little bit of JavaScript. Create a new .jslib
file in Assets/Plugins/
and add the following code:
mergeInto(LibraryManager.library, {
//flush our file changes to IndexedDB
SyncDB: function () {
FS.syncfs(false, function (err) {
if (err) console.log("syncfs error: " + err);
});
}
});
Add a reference to the external JS function in our C# code:
#if UNITY_WEBGL && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void SyncDB();
#endif
Call the external function after saving:
#if UNITY_WEBGL && !UNITY_EDITOR
//flush our changes to IndexedDB
SyncDB();
#endif
Answered by Kevin on November 2, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP