├── package.json.meta ├── Editor.meta ├── LICENSE.meta ├── Runtime.meta ├── Runtime ├── EnumOption.cs ├── com.houraiteahouse.options.Runtime.asmdef.meta ├── com.houraiteahouse.options.Runtime.asmdef ├── Option.cs.meta ├── EnumOption.cs.meta ├── OptionType.cs.meta ├── IOptionStorage.cs.meta ├── PlayerPrefsOptionStorage.cs.meta ├── IOptionStorage.cs ├── OptionType.cs ├── PlayerPrefsOptionStorage.cs └── Option.cs ├── README.md.meta ├── Editor ├── com.houraiteahouse.options.Editor.asmdef.meta ├── OptionEditor.cs.meta ├── com.houraiteahouse.options.Editor.asmdef └── OptionEditor.cs ├── package.json ├── LICENSE └── README.md /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff19f3f23e33df74c85f2e4389553831 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1e30a08fab526f4b84fd0d24c07eafe 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8252f6f86b0d17f43a2eba47e17fb1c4 3 | timeCreated: 1497682538 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ae1210642f130f846925952d8a40aa7b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/EnumOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace HouraiTeahouse.Options { 4 | 5 | [Serializable] 6 | public struct EnumOption { 7 | public int Value; 8 | public string DisplayName; 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9ab8d34920e7a054db9bce1ca7fad96d 3 | timeCreated: 1497682538 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/com.houraiteahouse.options.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 30f9b0516ade777458e791a372d377c4 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/com.houraiteahouse.options.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 229b9d0b4aa56ca4e8887815d83cb6f0 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/com.houraiteahouse.options.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.houraiteahouse.options.Runtime", 3 | "references": [ 4 | "com.houraiteahouse.core.Runtime" 5 | ], 6 | "includePlatforms": [], 7 | "excludePlatforms": [] 8 | } 9 | -------------------------------------------------------------------------------- /Runtime/Option.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d103a1f1de483944c800d246f074e1c9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/OptionEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 388fa5422163b344cbe03177d59a4ecc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/EnumOption.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ce12c87beb053040be1d147cc73914c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/OptionType.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c9179072bac22f24c9297bf626bbfcc7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IOptionStorage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 47ac69ba429499c4c83f7fdabcf71169 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/PlayerPrefsOptionStorage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b833789afcc4e03419159bfb6f572ec2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/com.houraiteahouse.options.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.houraiteahouse.options.Editor", 3 | "references": [ 4 | "com.houraiteahouse.options.Runtime" 5 | ], 6 | "optionalUnityReferences": [], 7 | "includePlatforms": [ 8 | "Editor" 9 | ], 10 | "excludePlatforms": [], 11 | "allowUnsafeCode": false 12 | } -------------------------------------------------------------------------------- /Runtime/IOptionStorage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace HouraiTeahouse.Options { 6 | 7 | public interface IOptionsStorage { 8 | bool IsOptionSet(string path); 9 | void SaveOption(string path, float value); 10 | float GetOption(string path); 11 | void SaveChanges(); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Hourai Teahouse", 3 | "name": "com.houraiteahouse.options", 4 | "displayName": "Hourai Options", 5 | "version": "1.0.0", 6 | "description": "Easy options management for Unity games.", 7 | "homepage": "https://github.com/HouraiTeahouse/HouraiOptions", 8 | "bugs": "https://github.com/HouraiTeahouse/HouraiOptions/issues", 9 | "repository": "github:HouraiTeahouse/HouraiOptions", 10 | "publishConfig": { 11 | "registry": "https://upm.houraiteahouse.net" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/OptionType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace HouraiTeahouse.Options { 6 | 7 | /// 8 | /// The enumeration of all supported Option types. 9 | /// 10 | public enum OptionType { 11 | 12 | /// 13 | /// A float value. 14 | /// 15 | Integer, 16 | 17 | /// 18 | /// A int value. 19 | /// 20 | Float, 21 | 22 | /// 23 | /// A bool value. 24 | /// 25 | Boolean, 26 | 27 | /// 28 | /// A enum value. This can support any enum type. 29 | /// 30 | Enum 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /Runtime/PlayerPrefsOptionStorage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace HouraiTeahouse.Options { 6 | 7 | public class PlayerPrefsOptionsStorage : IOptionsStorage { 8 | 9 | public bool IsOptionSet(string path) => PlayerPrefs.HasKey(path); 10 | 11 | public void SaveOption(string path, float value) { 12 | Debug.Log($"[PlayerPrefs] Saved Option \"{path}\": {value}"); 13 | PlayerPrefs.SetFloat(path, value); 14 | } 15 | 16 | public float GetOption(string path) { 17 | var value = PlayerPrefs.GetFloat(path); 18 | Debug.Log($"[PlayerPrefs] Loaded Option \"{path}\": {value}"); 19 | return value; 20 | } 21 | 22 | public void SaveChanges() { 23 | PlayerPrefs.Save(); 24 | Debug.Log($"[PlayerPrefs] Flushed changes to disk."); 25 | } 26 | 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Hourai Teahouse 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 | -------------------------------------------------------------------------------- /Runtime/Option.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEngine.Events; 4 | 5 | namespace HouraiTeahouse.Options { 6 | 7 | [CreateAssetMenu] 8 | public class Option : ScriptableObject { 9 | 10 | static IOptionsStorage _storage; 11 | public static IOptionsStorage Storage { 12 | get { return _storage; } 13 | set { 14 | if (value == null) { 15 | throw new ArgumentException("Option storage cannot be null."); 16 | } 17 | _storage = value; 18 | } 19 | } 20 | 21 | static Option() { 22 | Storage = new PlayerPrefsOptionsStorage(); 23 | } 24 | 25 | public string Path; 26 | public OptionType Type; 27 | 28 | public float MinValue; 29 | public float MaxValue; 30 | public float DefaultValue; 31 | 32 | public string Category; 33 | public string DisplayName; 34 | public int SortOrder; 35 | 36 | public bool IsDebug; 37 | 38 | public string EnumType; 39 | public EnumOption[] EnumOptions; 40 | 41 | float? CurrentRawValue; 42 | bool IsLoaded => CurrentRawValue != null; 43 | 44 | public UnityEvent OnValueChanged; 45 | 46 | public string GetDisplayName() => string.IsNullOrEmpty(DisplayName) ? Path : DisplayName; 47 | 48 | public T Get() { 49 | var rawValue = GetRawValue(); 50 | var readType = typeof(T); 51 | 52 | if (readType == typeof(float)) { 53 | return Cast(rawValue);; 54 | } else if (readType == typeof(int)) { 55 | return Cast((int)rawValue); 56 | } else if (readType == typeof(bool)) { 57 | return Cast(rawValue != 0); 58 | } else if (readType.IsEnum) { 59 | return (T)Enum.Parse(typeof(T), ((int)rawValue).ToString()); 60 | } else { 61 | throw new InvalidOperationException($"Cannot load option with unsupported type: {readType}"); 62 | } 63 | } 64 | 65 | public void Set(T input, bool save = true) { 66 | var oldValue = CurrentRawValue; 67 | var writeType = typeof(T); 68 | if (writeType == typeof(float)) { 69 | CurrentRawValue = Cast(input); 70 | } else if (writeType == typeof(int)) { 71 | CurrentRawValue = (float)Cast(input); 72 | } else if (writeType == typeof(bool)) { 73 | CurrentRawValue = Cast(input) ? 1.0f : 0.0f; 74 | } else if (writeType.IsEnum) { 75 | CurrentRawValue = (float)Cast(input); 76 | } else { 77 | throw new InvalidOperationException($"Cannot save option with unsupported type: {writeType}"); 78 | } 79 | EnforceMinMax(); 80 | if (oldValue != null && CurrentRawValue != oldValue) { 81 | OnValueChanged.Invoke(); 82 | } 83 | if (save) { 84 | Save(); 85 | } 86 | } 87 | 88 | public void Save() { 89 | if (!IsLoaded) { 90 | LoadValue(); 91 | } 92 | Storage.SaveOption(Path, CurrentRawValue.Value); 93 | } 94 | 95 | T Cast(object source) => (T)Convert.ChangeType(source, typeof(T)); 96 | 97 | float? GetRawValue() { 98 | if (!IsLoaded) { 99 | CurrentRawValue = LoadValue(); 100 | } 101 | return CurrentRawValue.Value; 102 | } 103 | 104 | void EnforceMinMax() { 105 | if (!IsLoaded) return; 106 | CurrentRawValue = Mathf.Clamp(CurrentRawValue.Value, MinValue, MaxValue); 107 | } 108 | 109 | float LoadValue() { 110 | if (!Storage.IsOptionSet(Path)) { 111 | Storage.SaveOption(Path, DefaultValue); 112 | return DefaultValue; 113 | } 114 | return Storage.GetOption(Path); 115 | } 116 | 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hourai Options 2 | 3 | A Unity3D library for creating, managing, and persisting user facing game 4 | settings and options in a straightfoward, asset based manner. 5 | 6 | By default, these values for these are stored in PlayerPrefs in an unencrypted 7 | form. The expectation is that these values make no signifigant impact on the 8 | game. 9 | 10 | # Installation 11 | In Unity 2018.3 and later, add the following to your `Packages/manifest.json`: 12 | 13 | ```json 14 | { 15 | "dependencies": { 16 | "com.houraiteahouse.options": "1.0.0" 17 | }, 18 | "scopedRegistries": [ 19 | { 20 | "name": "Hourai Teahouse", 21 | "url": "https://upm.houraiteahouse.net", 22 | "scopes": ["com.houraiteahouse"] 23 | } 24 | ] 25 | } 26 | ``` 27 | 28 | ## Usage 29 | 30 | ### Creating Options 31 | Options can be created like any other asset and edited in the Editor via the 32 | `Create > Option` menu item. 33 | 34 | For ease of general access in code, it's actually advisable to store option 35 | assets under a Resources folder or using Addressables. This makes fetching all 36 | available game options simple via well known Unity APIs. 37 | 38 | ### Reading/Setting Option Values 39 | Option assets can be referred to just like any other asset. Serialize a 40 | reference to a given option in a MonoBehaviour or Scriptable object: 41 | 42 | ```csharp 43 | public class TestScript : MonoBehavior { 44 | 45 | // Serialized! 46 | public Option MyOption; 47 | 48 | public void GetOptionValue() { 49 | // Reading values is simple. Note that invalid type conversions will fail. 50 | var optionValue = MyOption.Read(); 51 | Debug.Log(optionValue); 52 | } 53 | 54 | public void SetOptionValue() { 55 | // Setting values is simple. Note that invalid type conversions will fail. 56 | MyOption.Set= property.arraySize) { 103 | property.InsertArrayElementAtIndex(property.arraySize); 104 | property.GetArrayElementAtIndex(index).stringValue = valueName; 105 | } 106 | var item = property.GetArrayElementAtIndex(index); 107 | item.FindPropertyRelative("Value").intValue = Convert.ToInt32(values.GetValue(index)); 108 | EditorGUILayout.PropertyField(item.FindPropertyRelative("DisplayName"), new GUIContent(valueName)); 109 | } 110 | while (index < property.arraySize) { 111 | property.DeleteArrayElementAtIndex(property.arraySize - 1); 112 | } 113 | } 114 | 115 | void Header(string title) { 116 | EditorGUILayout.Space(); 117 | EditorGUILayout.LabelField(title, EditorStyles.boldLabel); 118 | } 119 | 120 | void EnforceMinMax() { 121 | var defaultProperty = serializedObject.FindProperty(kDefaultValueField); 122 | var min = serializedObject.FindProperty(kMinValueField).floatValue; 123 | var max = serializedObject.FindProperty(kMaxValueField).floatValue; 124 | defaultProperty.floatValue = Mathf.Clamp(defaultProperty.floatValue, min, max); 125 | } 126 | 127 | void SetProperty(string property, float value) => serializedObject.FindProperty(property).floatValue = value; 128 | 129 | void EnumField(string property, Type enumType) { 130 | var serializedProperty = serializedObject.FindProperty(property); 131 | Enum value = (Enum)Enum.ToObject(enumType, (int)serializedProperty.floatValue); 132 | value = EditorGUILayout.EnumPopup(serializedProperty.displayName, value); 133 | serializedProperty.floatValue = (float)Convert.ToInt32(value); 134 | } 135 | 136 | void IntField(string property) { 137 | var serializedProperty = serializedObject.FindProperty(property); 138 | int value = (int)serializedProperty.floatValue; 139 | value = EditorGUILayout.IntField(serializedProperty.displayName, value); 140 | serializedProperty.floatValue = (float)value; 141 | } 142 | 143 | void BoolField(string property) { 144 | var serializedProperty = serializedObject.FindProperty(property); 145 | bool value = serializedProperty.floatValue != 0f; 146 | value = EditorGUILayout.Toggle(serializedProperty.displayName, value); 147 | serializedProperty.floatValue = value ? 1.0f : 0.0f; 148 | } 149 | 150 | } 151 | 152 | } 153 | --------------------------------------------------------------------------------