├── 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 |
--------------------------------------------------------------------------------