├── screenshot.png ├── LICENSE.txt ├── README.md └── GlobalStorage.cs /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felladrin/unity3d-globalstorage/HEAD/screenshot.png -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Victor Nogueira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GlobalStorage for Unity 2 | 3 | Unity3D Script to store persistent data in JSON format and watch them live on Inspector. 4 | 5 | ![screenshot](screenshot.png) 6 | 7 | ## Features 8 | 9 | - **Singleton implementation**. Use it from anywhere on your code, from any scene. 10 | - **Categorization of data**. Data are categorized into Numbers, Strings, Booleans and Objects. 11 | - **Self-instantiates on scene**. You don't need to add it to the hierarchy. As soon as it's called, it shows up on the hierarchy autmoatically. Just click on it to see the data stored. 12 | 13 | ## Installation 14 | 15 | [Download the latest unitypackage here](https://github.com/felladrin/unity3d-globalstorage/releases/latest) and import it to your project. 16 | Once imported, you can move and rename the folder to whathever you want. 17 | 18 | ## How to use 19 | 20 | **Save** data to the storage: 21 | ```c# 22 | GlobalStorage.Save("name", "John Doe"); // Saving strings. 23 | GlobalStorage.Save("age", 28); // Saving integers, positive and negative. 24 | GlobalStorage.Save("experience", 473.32); // Faving doubles/floats, positive and negative. 25 | GlobalStorage.Save("isRunning", true); // Saving booleans, true or false. 26 | GlobalStorage.Save("status", playerStatus); // Saving objects. In this case, playerStatus is an instance of PlayerStatus class. 27 | ``` 28 | 29 | **Load** data from the storage: 30 | ```c# 31 | var name = GlobalStorage.Load("name"); // Note that we need to cast 32 | var age = GlobalStorage.Load("age"); // the type of the object 33 | var experience = GlobalStorage.Load("experience"); // being recovered from the 34 | var isRunning = GlobalStorage.Load("isRunning"); // storage. That's how the script 35 | var status = GlobalStorage.Load("status"); // knows how to treat the value. 36 | ``` 37 | 38 | **Delete** data from the storage: 39 | ```c# 40 | GlobalStorage.Delete("name"); 41 | GlobalStorage.Delete("age"); 42 | GlobalStorage.Delete("experience"); 43 | GlobalStorage.Delete("isRunning"); 44 | GlobalStorage.Delete("status"); 45 | ``` 46 | 47 | ## Minimum Requirements 48 | 49 | Unity version 5.3.5f1, released 15 Mar 2016. 50 | 51 | ## License 52 | 53 | The MIT License 54 | 55 | -------------------------------------------------------------------------------- /GlobalStorage.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.IO; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | 7 | /// 8 | /// Stores data in JSON format and display them on Inspector. 9 | /// 10 | public class GlobalStorage : MonoBehaviour 11 | { 12 | public bool DebugMode; 13 | 14 | [SerializeField] private string filePath; 15 | 16 | public string FilePathPath 17 | { 18 | get { return filePath; } 19 | private set { filePath = value; } 20 | } 21 | 22 | [Serializable] 23 | private class GlobalStorageNumber 24 | { 25 | [HideInInspector] public string Key; 26 | public double Value; 27 | 28 | public GlobalStorageNumber(string key, double value) 29 | { 30 | Key = key; 31 | Value = value; 32 | } 33 | } 34 | 35 | [Serializable] 36 | private class GlobalStorageString 37 | { 38 | [HideInInspector] public string Key; 39 | public string Value; 40 | 41 | public GlobalStorageString(string key, string value) 42 | { 43 | Key = key; 44 | Value = value; 45 | } 46 | } 47 | 48 | [Serializable] 49 | private class GlobalStorageBoolean 50 | { 51 | [HideInInspector] public string Key; 52 | public bool Value; 53 | 54 | public GlobalStorageBoolean(string key, bool value) 55 | { 56 | Key = key; 57 | Value = value; 58 | } 59 | } 60 | 61 | [Serializable] 62 | private class GlobalStorageObject 63 | { 64 | [HideInInspector] public string Key; 65 | [TextArea(3, 10)] [SerializeField] private string value; 66 | [SerializeField] private string filePath; 67 | 68 | private string Value 69 | { 70 | get { return value; } 71 | set { this.value = value; } 72 | } 73 | 74 | private string FilePath 75 | { 76 | get { return filePath; } 77 | set { filePath = value; } 78 | } 79 | 80 | public GlobalStorageObject(string key, string value) 81 | { 82 | Key = key; 83 | Value = value; 84 | FilePath = GetSavePath(key); 85 | } 86 | } 87 | 88 | [SerializeField] private List numbers = new List(); 89 | 90 | [SerializeField] private List strings = new List(); 91 | 92 | [SerializeField] private List booleans = new List(); 93 | 94 | [SerializeField] private List objects = new List(); 95 | 96 | #region Singleton definition 97 | 98 | private static GlobalStorage instance; 99 | 100 | private static void SelfInstantiateInCurrentScene() 101 | { 102 | if (GameObject.Find(GetSelfName()) != null) return; 103 | new GameObject {name = GetSelfName()}.AddComponent(); 104 | } 105 | 106 | private void GrantSingleInstance() 107 | { 108 | if (instance == null) 109 | { 110 | instance = this; 111 | } 112 | else if (instance != this) 113 | { 114 | Destroy(gameObject); 115 | } 116 | } 117 | 118 | #endregion 119 | 120 | private void Awake() 121 | { 122 | FilePathPath = GetSavePath(GetSelfName()); 123 | GrantSingleInstance(); 124 | DontDestroyOnLoad(gameObject); 125 | LoadSelf(); 126 | } 127 | 128 | /// 129 | /// Saves data on the storage. 130 | /// 131 | /// The identifier for this item 132 | /// The data to be saved 133 | /// The type of the data 134 | public static void Save(string key, T data) 135 | { 136 | SelfInstantiateInCurrentScene(); 137 | 138 | if (string.IsNullOrEmpty(key)) 139 | return; 140 | 141 | var savePath = GetSavePath(key); 142 | 143 | Delete(key); 144 | 145 | if (TypeCodeIsNumeric(typeof(T))) 146 | { 147 | instance.numbers.Add(new GlobalStorageNumber(key, Convert.ToDouble(data))); 148 | SaveSelf(); 149 | } 150 | else if (TypeCodeIsString(typeof(T))) 151 | { 152 | instance.strings.Add(new GlobalStorageString(key, Convert.ToString(data))); 153 | SaveSelf(); 154 | } 155 | else if (TypeCodeIsBoolean(typeof(T))) 156 | { 157 | instance.booleans.Add(new GlobalStorageBoolean(key, Convert.ToBoolean(data))); 158 | SaveSelf(); 159 | } 160 | else 161 | { 162 | if (data == null) 163 | return; 164 | 165 | try 166 | { 167 | var serializedData = JsonUtility.ToJson(data, true); 168 | if (key.Equals(GetSelfName())) 169 | { 170 | var globalStorage = (GlobalStorage) (object) data; 171 | var objectsBeforeSaving = new List(globalStorage.objects); 172 | globalStorage.objects = null; 173 | serializedData = JsonUtility.ToJson(data, true); 174 | globalStorage.objects = objectsBeforeSaving; 175 | } 176 | else 177 | { 178 | instance.objects.Add(new GlobalStorageObject(key, serializedData)); 179 | } 180 | 181 | File.WriteAllText(savePath, serializedData); 182 | DebugMessage("Data stored to " + savePath); 183 | } 184 | catch (Exception ex) 185 | { 186 | Debug.LogException(ex); 187 | } 188 | } 189 | } 190 | 191 | /// 192 | /// Loads data from the storage. 193 | /// 194 | /// The identifier of the item 195 | /// Type of the data to be loaded 196 | /// The data, already cast to the indicated type 197 | public static T Load(string key) 198 | { 199 | SelfInstantiateInCurrentScene(); 200 | 201 | var data = default(T); 202 | 203 | if (string.IsNullOrEmpty(key)) 204 | return data; 205 | 206 | var savePath = GetSavePath(key); 207 | 208 | try 209 | { 210 | if (TypeCodeIsNumeric(typeof(T))) 211 | { 212 | var globalStorageNumber = instance.numbers.Find(x => x.Key == key); 213 | if (globalStorageNumber != null) 214 | { 215 | data = (T) Convert.ChangeType(globalStorageNumber.Value, typeof(T)); 216 | DebugMessage("Data loaded from Numbers Array"); 217 | } 218 | } 219 | else if (TypeCodeIsString(typeof(T))) 220 | { 221 | var globalStorageString = instance.strings.Find(x => x.Key == key); 222 | if (globalStorageString != null) 223 | { 224 | data = (T) Convert.ChangeType(globalStorageString.Value, typeof(T)); 225 | DebugMessage("Data loaded from Strings Array"); 226 | } 227 | } 228 | else if (TypeCodeIsBoolean(typeof(T))) 229 | { 230 | var globalStorageBoolean = instance.booleans.Find(x => x.Key == key); 231 | if (globalStorageBoolean != null) 232 | { 233 | data = (T) Convert.ChangeType(globalStorageBoolean.Value, typeof(T)); 234 | DebugMessage("Data loaded from Booleans Array"); 235 | } 236 | } 237 | else 238 | { 239 | if (File.Exists(savePath)) 240 | { 241 | var serializedData = File.ReadAllText(savePath); 242 | data = JsonUtility.FromJson(serializedData); 243 | DebugMessage("Data loaded from " + savePath); 244 | } 245 | } 246 | } 247 | catch (Exception ex) 248 | { 249 | Debug.LogException(ex); 250 | } 251 | 252 | return data; 253 | } 254 | 255 | /// 256 | /// Deletes data from the storage. 257 | /// 258 | /// The identifier of the item to be deleted 259 | public static void Delete(string key) 260 | { 261 | SelfInstantiateInCurrentScene(); 262 | 263 | if (string.IsNullOrEmpty(key)) 264 | return; 265 | 266 | try 267 | { 268 | if (instance.numbers.RemoveAll(x => x.Key == key) > 0 || 269 | instance.strings.RemoveAll(x => x.Key == key) > 0 || 270 | instance.booleans.RemoveAll(x => x.Key == key) > 0) 271 | { 272 | SaveSelf(); 273 | } 274 | else 275 | { 276 | var savePath = GetSavePath(key); 277 | instance.objects.RemoveAll(x => x.Key == key); 278 | if (!File.Exists(savePath)) return; 279 | File.Delete(savePath); 280 | } 281 | } 282 | catch (Exception ex) 283 | { 284 | Debug.LogException(ex); 285 | } 286 | } 287 | 288 | private static string GetSavePath(string fileName) 289 | { 290 | return string.IsNullOrEmpty(fileName) 291 | ? null 292 | : Path.Combine(Application.persistentDataPath, fileName + ".json"); 293 | } 294 | 295 | private static bool TypeCodeIsNumeric(Type type) 296 | { 297 | TypeCode[] typeCodes = 298 | { 299 | TypeCode.Decimal, TypeCode.Double, TypeCode.Int16, TypeCode.Int32, TypeCode.Int64, 300 | TypeCode.Single, TypeCode.UInt16, TypeCode.UInt32, TypeCode.UInt64 301 | }; 302 | return Array.IndexOf(typeCodes, Type.GetTypeCode(type)) > -1; 303 | } 304 | 305 | private static bool TypeCodeIsString(Type type) 306 | { 307 | return Type.GetTypeCode(type) == TypeCode.String || Type.GetTypeCode(type) == TypeCode.Char; 308 | } 309 | 310 | private static bool TypeCodeIsBoolean(Type type) 311 | { 312 | return Type.GetTypeCode(type) == TypeCode.Boolean; 313 | } 314 | 315 | private static void SaveSelf() 316 | { 317 | Save(GetSelfName(), instance); 318 | } 319 | 320 | private static void LoadSelf() 321 | { 322 | try 323 | { 324 | var selfName = GetSelfName(); 325 | var savePath = GetSavePath(selfName); 326 | if (!File.Exists(savePath)) return; 327 | var serializedData = File.ReadAllText(savePath); 328 | var debugModeBeforeLoading = instance.DebugMode; 329 | JsonUtility.FromJsonOverwrite(serializedData, instance); 330 | instance.DebugMode = debugModeBeforeLoading; 331 | DebugMessage("Data loaded from " + savePath); 332 | var fileArray = new DirectoryInfo(Application.persistentDataPath).GetFiles("*.json"); 333 | foreach (var fileInfo in fileArray) 334 | { 335 | var fileName = fileInfo.Name.Replace(".json", ""); 336 | if (fileName.Equals(selfName)) continue; 337 | var fileSavePath = GetSavePath(fileName); 338 | var fileSerializedData = File.ReadAllText(fileSavePath); 339 | instance.objects.Add(new GlobalStorageObject(fileName, fileSerializedData)); 340 | DebugMessage("Data loaded from " + fileSavePath); 341 | } 342 | } 343 | catch (Exception ex) 344 | { 345 | Debug.LogException(ex); 346 | } 347 | } 348 | 349 | private static string GetSelfName() 350 | { 351 | var declaringType = MethodBase.GetCurrentMethod().DeclaringType; 352 | return declaringType != null ? declaringType.ToString() : null; 353 | } 354 | 355 | private static void DebugMessage(string message) 356 | { 357 | if (instance.DebugMode) 358 | Debug.Log(message); 359 | } 360 | } 361 | --------------------------------------------------------------------------------