├── ClassTemplate.cs ├── LICENSE.md ├── README.md ├── application ├── AndroidUtils.cs └── ApplicationChrome.cs ├── camera └── GazeWatcher.cs ├── data ├── Editor │ └── PersistentDataTests.cs ├── ObjectPool.cs ├── PersistentData.cs ├── ValueList.cs └── serialization │ └── json │ ├── Editor │ └── JSONTests.cs │ ├── JSON.cs │ ├── JSONArray.cs │ ├── JSONDictUtils.cs │ ├── JSONEncoder.cs │ ├── JSONLiteral.cs │ ├── JSONNumber.cs │ ├── JSONObject.cs │ ├── JSONParser.cs │ ├── JSONString.cs │ ├── JSONStringifyOptions.cs │ └── JSONValue.cs ├── game ├── Editor │ └── GlobalConfigEditor.cs ├── GameLooper.cs ├── GameManager.cs ├── GlobalConfigGO.cs ├── SceneManager.cs ├── TrackingManager.cs ├── navigation │ ├── NavigatorManager.cs │ └── NavigatorScene.cs └── services │ └── ServiceLocator.cs ├── geometry ├── FlatMesh.cs ├── GeomUtils.cs └── objects │ ├── Circle.cs │ ├── CustomMeshObject.cs │ ├── Editor │ └── RoundedCubeEditor.cs │ ├── LineQuad.cs │ ├── RoundedCube.cs │ └── UVLine.cs ├── localization └── ValueListManager.cs ├── logging └── D.cs ├── net ├── kongregate │ ├── KongregateAPI.cs │ └── README.md └── newgrounds │ ├── API.cs │ ├── GameObjectSurrogate.cs │ ├── README.md │ ├── data │ └── Medal.cs │ └── services │ ├── BasicService.cs │ ├── PostScoreService.cs │ └── UnlockMedalService.cs ├── objects ├── MeshColliderCreator.cs ├── ModelResizer.cs ├── SimpleButton.cs ├── TextMeshSharpener.cs └── layout │ ├── AutoAligner.cs │ ├── AutoLayoutElement.cs │ └── AutoResizer.cs ├── overlays ├── FPSDisplay.cs └── HoloLensStatusDisplay.cs ├── textures └── Texture2DUtils.cs ├── transitions ├── Easing.cs ├── ZTween.cs └── ZTweenSurrogate.cs └── utils ├── ByteUtils.cs ├── Editor └── MathUtilsTests.cs ├── GameObjectUtils.cs ├── JSONUtils.cs ├── MathUtils.cs └── StringUtils.cs /ClassTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /** 6 | * @author zeh fernando 7 | */ 8 | class MyClass { 9 | 10 | // Constants 11 | private const string myString; 12 | 13 | // Static properties 14 | private static MyType myProperty; 15 | 16 | // Properties 17 | private MyType myProperty; 18 | 19 | // Instances 20 | private MyType myProperty; 21 | 22 | #region Static intertface 23 | 24 | 25 | // ================================================================================================================ 26 | // STATIC CONSTRUCTOR --------------------------------------------------------------------------------------------- 27 | 28 | static MyClass() { 29 | // Class constructor 30 | } 31 | 32 | 33 | // ================================================================================================================ 34 | // PUBLIC STATIC INTERFACE ---------------------------------------------------------------------------------------- 35 | 36 | public static void doSomethingPublic() { 37 | } 38 | 39 | 40 | // ================================================================================================================ 41 | // PRIVATE STATIC INTERFACE --------------------------------------------------------------------------------------- 42 | 43 | private static void doSomething() { 44 | } 45 | 46 | #endregion 47 | 48 | 49 | // ================================================================================================================ 50 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 51 | 52 | MyClass(string name = "") { 53 | } 54 | 55 | ~MyClass() { 56 | // Destructor: cleanup 57 | // Base is called automatically afterwards 58 | } 59 | 60 | 61 | // ================================================================================================================ 62 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 63 | 64 | // ================================================================================================================ 65 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 66 | 67 | public string name { 68 | get { return _name; } 69 | } 70 | 71 | public bool cacheData { 72 | get { 73 | return _cacheData; 74 | } 75 | set { 76 | _cacheData = value; 77 | } 78 | } 79 | 80 | // ================================================================================================================ 81 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 82 | 83 | // ================================================================================================================ 84 | // INTERNAL CLASSES ----------------------------------------------------------------------------------------------- 85 | 86 | internal interface IZTweenStep { 87 | void start(); 88 | void update(float t); 89 | void end(); 90 | float getDuration(); 91 | } 92 | 93 | class OtherClass { 94 | // ... 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Zeh Fernando 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Unity tidbits 2 | ============= 3 | 4 | Small C# code to help in Unity development. Currently: 5 | 6 | * [KongregateAPI](net/kongregate): A pure code implementation of the Kongregate statistics API for Unity as a single C# file 7 | -------------------------------------------------------------------------------- /application/AndroidUtils.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_ANDROID && !UNITY_EDITOR 2 | #define USE_ANDROID 3 | #endif 4 | 5 | 6 | /** 7 | * @author zeh fernando 8 | */ 9 | using System; 10 | using UnityEngine; 11 | 12 | class AndroidUtils { 13 | 14 | // Use: 15 | // runOnAndroidUiThread(someMethod) 16 | public static void runOnAndroidUiThread(Action target) { 17 | // Calls an Android activity on the UI Thread (needed for some interactions with Views) 18 | #if USE_ANDROID 19 | using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { 20 | using (var activity = unityPlayer.GetStatic("currentActivity")) { 21 | activity.Call("runOnUiThread", new AndroidJavaRunnable(target)); 22 | // Other examples: 23 | //string a = activity.Call("someNomw", param); 24 | //window.Call("setFlags", flagsValue, -1); // (int)0x7FFFFFFF 25 | //window.Call("setStatusBarColor", unchecked((int)_statusBarColor)); // for an uint 26 | } 27 | } 28 | #else 29 | Debug.Log("[Android] Would call [" + target + "] later on the UI thread"); 30 | #endif 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /camera/GazeWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class GazeWatcher { 6 | 7 | /** 8 | * Given a camera, tracks its position and which object is in its gaze 9 | * 10 | * Usage: 11 | * 12 | * var watcher = new GazeWatcher(Camera.main); 13 | * watcher.position // Vector3 position of the camera 14 | * watcher.direction // Vector3 direction normal of where the camera is looking 15 | */ 16 | 17 | // Handlers 18 | public delegate void GazeWatcherHandler(GazeWatcher gazeWatcher); 19 | 20 | // Properties 21 | private GameLooper _looper; 22 | private RaycastHit _lastHit; 23 | private Camera _camera; 24 | 25 | private Vector3 _position; 26 | private Vector3 _direction; 27 | private bool _isHitting; 28 | private Vector3 _hitPosition; 29 | private Vector3 _hitDirection; 30 | private GameObject _hitObject; 31 | 32 | // Public 33 | public event GazeWatcherHandler onObjectHitStart; 34 | public event GazeWatcherHandler onObjectHitMove; 35 | public event GazeWatcherHandler onObjectHitEnd; 36 | 37 | 38 | // ================================================================================================================ 39 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 40 | 41 | public GazeWatcher(Camera camera) { 42 | _camera = camera; 43 | _looper = new GameLooper(); 44 | 45 | _looper.onUpdate += onUpdate; 46 | } 47 | 48 | 49 | // ================================================================================================================ 50 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 51 | 52 | public Vector3 position { 53 | get { 54 | return _position; 55 | } 56 | } 57 | 58 | public Vector3 direction { 59 | get { 60 | return _direction; 61 | } 62 | } 63 | 64 | public bool isHitting { 65 | get { 66 | return _isHitting; 67 | } 68 | } 69 | 70 | public Vector3 hitPosition { 71 | get { 72 | return _hitPosition; 73 | } 74 | } 75 | 76 | public Vector3 hitDirection { 77 | get { 78 | return _hitDirection; 79 | } 80 | } 81 | 82 | public GameObject hitObject { 83 | get { 84 | return _hitObject; 85 | } 86 | } 87 | 88 | 89 | // ================================================================================================================ 90 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 91 | 92 | private void onUpdate(GameLooper gameLooper) { 93 | // Do a raycast into the world based on camera position and orientation 94 | if (_camera == null) return; 95 | 96 | _position = _camera.transform.position; 97 | _direction = _camera.transform.forward; 98 | 99 | bool didHit = Physics.Raycast(_position, _direction, out _lastHit); 100 | 101 | if ((!didHit || _lastHit.transform.gameObject != _hitObject) && _isHitting) { 102 | // Ended a hit 103 | if (onObjectHitEnd != null) onObjectHitEnd(this); 104 | 105 | _isHitting = false; 106 | _hitPosition = Vector3.zero; 107 | _hitDirection = Vector3.zero; 108 | _hitObject = null; 109 | } 110 | 111 | if (didHit) { 112 | // It is hitting 113 | _hitPosition = _lastHit.point; 114 | _hitDirection = _lastHit.normal; 115 | 116 | if (!_isHitting) { 117 | // Started a new hit 118 | _isHitting = true; 119 | _hitObject = _lastHit.transform.gameObject; 120 | if (onObjectHitStart != null) onObjectHitStart(this); 121 | } 122 | 123 | if (onObjectHitMove != null) onObjectHitMove(this); 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /data/Editor/PersistentDataTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | [TestFixture] 4 | public class PersistentDataTests { 5 | 6 | // ================================================================================================================ 7 | // SETUP ---------------------------------------------------------------------------------------------------------- 8 | 9 | [TestFixtureSetUp] 10 | public void SetUp() { 11 | } 12 | 13 | [TestFixtureTearDown] 14 | public void TearDown() { 15 | } 16 | 17 | // ================================================================================================================ 18 | // TEST INTERFACE ------------------------------------------------------------------------------------------------- 19 | 20 | [Test] 21 | public void Primitives() { 22 | // Saving 23 | var oldData = PersistentData.getInstance(); 24 | oldData.Clear(); 25 | 26 | var b0 = true; 27 | var b1 = false; 28 | var i0 = 2; 29 | var i1 = -1020; 30 | var i2 = 9081720; 31 | var l0 = 21983671920L; 32 | var l1 = -21983671222L; 33 | var f0 = 2.2f; 34 | var f1 = -2.3f; 35 | var d0 = -2.0928; 36 | var d1 = 655352.8762; 37 | var s0 = "aaaaaa"; 38 | var s1 = @"a\naa""a\\aa"; 39 | 40 | oldData.SetBool("b0", b0); 41 | oldData.SetBool("b1", b1); 42 | oldData.SetInt("i0", i0); 43 | oldData.SetInt("i1", i1); 44 | oldData.SetInt("i2", i2); 45 | oldData.SetLong("l0", l0); 46 | oldData.SetLong("l1", l1); 47 | oldData.SetFloat("f0", f0); 48 | oldData.SetFloat("f1", f1); 49 | oldData.SetDouble("d0", d0); 50 | oldData.SetDouble("d1", d1); 51 | oldData.SetString("s0", s0); 52 | oldData.SetString("s1", s1); 53 | oldData.Save(); 54 | 55 | // Loading 56 | var newData = PersistentData.getInstance(); 57 | 58 | // Test 59 | Assert.AreEqual(b0, newData.GetBool("b0")); 60 | Assert.AreEqual(b1, newData.GetBool("b1")); 61 | Assert.AreEqual(i0, newData.GetInt("i0")); 62 | Assert.AreEqual(i1, newData.GetInt("i1")); 63 | Assert.AreEqual(i2, newData.GetInt("i2")); 64 | Assert.AreEqual(l0, newData.GetLong("l0")); 65 | Assert.AreEqual(l1, newData.GetLong("l1")); 66 | Assert.AreEqual(f0, newData.GetFloat("f0")); 67 | Assert.AreEqual(f1, newData.GetFloat("f1")); 68 | Assert.AreEqual(d0, newData.GetDouble("d0")); 69 | Assert.AreEqual(d1, newData.GetDouble("d1")); 70 | Assert.AreEqual(s0, newData.GetString("s0")); 71 | Assert.AreEqual(s1, newData.GetString("s1")); 72 | } 73 | 74 | [Test] 75 | public void Bytes() { 76 | // Saving 77 | var oldData = PersistentData.getInstance(); 78 | oldData.Clear(); 79 | 80 | var oldBytes = new byte[] { 0x41, 0x20, 0x41 }; 81 | oldData.SetBytes("bytes", oldBytes); 82 | oldData.Save(); 83 | 84 | // Loading 85 | var newData = PersistentData.getInstance(); 86 | 87 | // Test 88 | Assert.AreEqual(oldBytes, newData.GetBytes("bytes"), "Simple byte array"); 89 | } 90 | 91 | [Test] 92 | public void Serializable() { 93 | // Saving 94 | var oldData = PersistentData.getInstance(); 95 | oldData.Clear(); 96 | 97 | var oldList = new int[] { 0, 1, 2 }; 98 | oldData.SetObject("intList", oldList); 99 | oldData.Save(); 100 | 101 | // Loading 102 | var newData = PersistentData.getInstance(); 103 | 104 | // Test 105 | Assert.AreEqual(oldList, newData.GetObject("intList"), "Integer list"); 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /data/ObjectPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | class ObjectPool { 5 | 6 | // Instances 7 | private List objects; 8 | private List objectsUsed; 9 | 10 | private int numObjectsFree; 11 | 12 | private Func create; 13 | 14 | // Delegate types 15 | public event Action OnGet; 16 | public event Action OnPut; 17 | public event Action OnClear; 18 | 19 | 20 | // ================================================================================================================ 21 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 22 | 23 | public ObjectPool(Func createFunction = null) { 24 | objects = new List(); 25 | objectsUsed = new List(); 26 | numObjectsFree = 0; 27 | create = createFunction; 28 | } 29 | 30 | 31 | // ================================================================================================================ 32 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 33 | 34 | public T Get() { 35 | // Returns an unused object from the existing pool, or creating a new if none is available 36 | if (numObjectsFree == 0) { 37 | // No objects free, create a new one 38 | if (create != null) { 39 | int pos = addObject(create()); 40 | return getObjectAt(pos); 41 | } else { 42 | return default(T); 43 | } 44 | } else { 45 | // Find first unused object 46 | for (int i = 0; i < objectsUsed.Count; i++) { 47 | if (!objectsUsed[i]) return getObjectAt(i); 48 | } 49 | } 50 | return default(T); 51 | } 52 | 53 | public void Put(T obj) { 54 | // Put an object back in the pool 55 | int index = objects.IndexOf(obj); 56 | if (index > -1) { 57 | // Object is in the pool, just put it back 58 | putObject(index, obj); 59 | } else { 60 | // Object is not in the pool yet, add it 61 | addObject(obj); 62 | } 63 | } 64 | 65 | public void Clear() { 66 | while (objects.Count > 0) clearObjectAt(0); 67 | objects.Clear(); 68 | objectsUsed.Clear(); 69 | numObjectsFree = 0; 70 | } 71 | 72 | public int GetNumObjectsFree() { 73 | return numObjectsFree; 74 | } 75 | 76 | public int GetNumObjects() { 77 | return objects.Count; 78 | } 79 | 80 | 81 | // ================================================================================================================ 82 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 83 | 84 | private int addObject(T obj) { 85 | objects.Add(obj); 86 | objectsUsed.Add(false); 87 | numObjectsFree++; 88 | return objects.Count - 1; 89 | } 90 | 91 | private T getObjectAt(int index) { 92 | if (!objectsUsed[index]) { 93 | objectsUsed[index] = true; 94 | numObjectsFree--; 95 | } 96 | if (OnGet != null) OnGet(objects[index]); 97 | return objects[index]; 98 | } 99 | 100 | private void putObject(int pos, T obj) { 101 | if (objectsUsed[pos]) { 102 | objectsUsed[pos] = false; 103 | numObjectsFree++; 104 | } 105 | if (OnPut != null) OnPut(obj); 106 | } 107 | 108 | private void clearObjectAt(int index) { 109 | if (OnClear != null) OnClear(objects[index]); 110 | objects.RemoveAt(index); 111 | objectsUsed.RemoveAt(index); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /data/ValueList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Runtime.Serialization.Formatters.Binary; 6 | using System.Text; 7 | using UnityEngine; 8 | 9 | class ValueList { 10 | 11 | /* A list of strings and other values, for localization */ 12 | 13 | /* 14 | { 15 | "id1" : "value1", 16 | "group" : { 17 | "id2" : "value2" 18 | } 19 | } 20 | 21 | Use: 22 | getString("id1"); // "value1" 23 | getString("group.id2"); // "value2" 24 | 25 | TODO: 26 | * Proper processed string 27 | * Insert from JSON 28 | * Other native types: bool, number, etc 29 | * Other Unity types: prefab, texture, audio clip 30 | * Actual language filtering 31 | 32 | * For proper language later: 33 | Values.json 34 | { 35 | id: "en", 36 | name: "English", 37 | values: { 38 | name: "App name" 39 | } 40 | } 41 | 42 | */ 43 | 44 | // Constants 45 | private static string ID_HYERARCHY_SEPARATOR = "."; 46 | 47 | // Default values 48 | //private static string VALUE_STRING_DEFAULT = "[null]"; 49 | 50 | // Static properties 51 | private static Dictionary instances; 52 | 53 | // Properties 54 | private string _key; // Instance name 55 | private ValueGroup root; 56 | 57 | 58 | // ================================================================================================================ 59 | // STATIC --------------------------------------------------------------------------------------------------------- 60 | 61 | static ValueList() { 62 | instances = new Dictionary(); 63 | } 64 | 65 | 66 | // ================================================================================================================ 67 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 68 | 69 | public ValueList(string key = "") { 70 | _key = key; 71 | 72 | ValueList.addInstance(this); 73 | root = new ValueGroup(); 74 | } 75 | 76 | 77 | // ================================================================================================================ 78 | // STATIC INTERFACE ----------------------------------------------------------------------------------------------- 79 | 80 | private static void addInstance(ValueList instance) { 81 | if (instances.ContainsKey(instance.key)) instances.Remove(instance.key); 82 | instances.Add(instance.key, instance); 83 | } 84 | 85 | public static ValueList getInstance(string key = "") { 86 | if (instances.ContainsKey(key)) return instances[key]; 87 | 88 | // Doesn't exist, create a new one and return it 89 | return new ValueList(key); 90 | } 91 | 92 | 93 | // ================================================================================================================ 94 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 95 | 96 | /* 97 | public function setValues(__object:Object):void { 98 | // Set the values; __object must be a JSON-like object 99 | values = __object; 100 | // TODO: read string data replacing unix/windows line feed? 101 | } 102 | * */ 103 | 104 | public string GetString(string keyPath) { 105 | return getValueInternal(keyPath); 106 | } 107 | 108 | public T GetValue(string keyPath) { 109 | return getValueInternal(keyPath); 110 | } 111 | 112 | public void SetString(string keyPath, string value) { 113 | setValueInternal(keyPath, new ValueItemString(value)); 114 | } 115 | 116 | public bool HasKey(string keyPath) { 117 | return getValueInternal(keyPath) != null; 118 | } 119 | 120 | public void SetFromJSON(string jsonSource) { 121 | // Parses a JSON file and sets the value data 122 | // { "key" : "value", "group" : { "key": "value" }} 123 | SetFromDictionary(JSON.parseAsDictionary(jsonSource, JSON.FLAG_REMOVE_COMMENTS)); 124 | } 125 | 126 | public void SetFromDictionary(IDictionary dictionary, string parentRoot = "") { 127 | // Set values from a dictionary, using a parent root if any 128 | string thisPath; 129 | foreach (DictionaryEntry entry in dictionary) { 130 | thisPath = (parentRoot.Length > 0 ? parentRoot + ID_HYERARCHY_SEPARATOR : "") + entry.Key; 131 | if (entry.Value is string) { 132 | // Normal string 133 | SetString(thisPath, entry.Value as string); 134 | } else if (entry.Value is IDictionary) { 135 | // Group 136 | SetFromDictionary(entry.Value as Dictionary, thisPath); 137 | } else { 138 | Debug.LogError("Error! Cannot add from dictionary with object [" + entry.Value + "]"); 139 | } 140 | } 141 | } 142 | 143 | /* 144 | public string getProcessedString(string text) { 145 | return getProcessedStringInternal(text); 146 | } 147 | */ 148 | 149 | 150 | // ================================================================================================================ 151 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 152 | 153 | public string key { 154 | get { return _key; } 155 | } 156 | 157 | 158 | // ================================================================================================================ 159 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 160 | 161 | /* 162 | private string getProcessedStringInternal(string text) { 163 | // Returns a string with processed codes 164 | // For example "this is an ${examples/example}!" returns "this is an EXAMPLE!" (where the value of examples.example in strings.json is "EXAMPLE") 165 | 166 | var newString = ""; 167 | 168 | var codes:RegExp = /\$\{(.+?)\}/ig; 169 | var result:Object = codes.exec(__string); 170 | 171 | var lastIndex:Number = 0; 172 | var newIndex:Number; 173 | 174 | while (Boolean(result)) { 175 | newIndex = result["index"]; 176 | 177 | // What came before the tag 178 | newString += __string.substring(lastIndex, newIndex); 179 | 180 | // The tag tex 181 | newString += getString(result[1]); 182 | 183 | lastIndex = codes.lastIndex; 184 | 185 | result = codes.exec(__string); 186 | } 187 | 188 | // End text after last tag 189 | newString += __string.substring(lastIndex, __string.length); 190 | 191 | return newString; 192 | } 193 | * */ 194 | 195 | private T getValueInternal(string keyPath) { 196 | // Get the full path to the value name 197 | var ids = new List(keyPath.Split(ID_HYERARCHY_SEPARATOR.ToCharArray())); 198 | ValueGroup group; 199 | string itemKey; 200 | if (ids.Count > 0) { 201 | // Remove last (because it's the item) 202 | itemKey = ids[ids.Count - 1]; 203 | ids.RemoveAt(ids.Count - 1); 204 | group = getGroupInternal(ids); 205 | } else { 206 | Debug.LogWarning("ValueList :: Value path [" + keyPath + "] is empty!"); 207 | return default(T); 208 | } 209 | 210 | if (group == null) { 211 | return default(T); 212 | } else if (group.hasItem(itemKey)) { 213 | return (T)(group.getItem(itemKey).getValue()); 214 | } else { 215 | Debug.LogWarning("ValueList :: Value path key [" + keyPath + "] doesn't exist!"); 216 | return default(T); 217 | } 218 | } 219 | 220 | private void setValueInternal(string keyPath, ValueItem valueItem) { 221 | var ids = new List(keyPath.Split(ID_HYERARCHY_SEPARATOR.ToCharArray())); 222 | ValueGroup group; 223 | string itemKey; 224 | if (ids.Count > 0) { 225 | // Remove last (because it's the item) 226 | itemKey = ids[ids.Count - 1]; 227 | ids.RemoveAt(ids.Count - 1); 228 | group = getGroupInternal(ids, true); 229 | group.addItem(itemKey, valueItem); 230 | } else { 231 | Debug.LogWarning("ValueList :: Value path [" + keyPath + "] is empty!"); 232 | } 233 | } 234 | 235 | private ValueGroup getGroupInternal(List keyPath, bool canCreate = false) { 236 | var currentGroup = root; 237 | 238 | for (var i = 0; i < keyPath.Count; i++) { 239 | if (currentGroup.hasGroup(keyPath[i])) { 240 | // Found 241 | currentGroup = currentGroup.getGroup(keyPath[i]); 242 | } else if (canCreate) { 243 | // Doesn't exist, but can create 244 | currentGroup.addGroup(keyPath[i], new ValueGroup()); 245 | currentGroup = currentGroup.getGroup(keyPath[i]); 246 | } else { 247 | // Doesn't exist, fail 248 | Debug.LogWarning("ValueList :: Value path [" + string.Join(ID_HYERARCHY_SEPARATOR, keyPath.ToArray()) + "] doesn't exist!"); 249 | return null; 250 | } 251 | } 252 | 253 | return currentGroup; 254 | } 255 | 256 | 257 | // ================================================================================================================ 258 | // AUXILIARY CLASSES ---------------------------------------------------------------------------------------------- 259 | 260 | class ValueGroup { 261 | private Dictionary groups; 262 | private Dictionary items; 263 | 264 | public ValueGroup() { 265 | groups = new Dictionary(); 266 | items = new Dictionary(); 267 | } 268 | 269 | public bool hasItem(string key) { 270 | return items.ContainsKey(key); 271 | } 272 | 273 | public void addItem(string key, ValueItem item) { 274 | items[key] = item; 275 | } 276 | 277 | public ValueItem getItem(string key) { 278 | return items[key]; 279 | } 280 | 281 | public bool hasGroup(string key) { 282 | return groups.ContainsKey(key); 283 | } 284 | 285 | public void addGroup(string key, ValueGroup group) { 286 | groups[key] = group; 287 | } 288 | 289 | public ValueGroup getGroup(string key) { 290 | return groups[key]; 291 | } 292 | } 293 | 294 | abstract class ValueItem { 295 | private object value; 296 | 297 | public ValueItem(object value) { 298 | this.value = value; 299 | } 300 | 301 | public object getValue() { 302 | return value; 303 | } 304 | } 305 | 306 | class ValueItemString:ValueItem { 307 | public ValueItemString(string value):base(value) { 308 | } 309 | 310 | public string getValue() { 311 | return (string)getValue(); 312 | } 313 | } 314 | 315 | } 316 | -------------------------------------------------------------------------------- /data/serialization/json/JSONArray.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | /** 7 | * An array is an ordered collection of values. 8 | * An array begins with [ (left bracket) and ends with ] (right bracket). 9 | * Values are separated by , (comma). 10 | * @author zeh 11 | */ 12 | public class JSONArray:JSONValue { 13 | 14 | // Constants 15 | private const char CHAR_START = '['; 16 | private const char CHAR_END = ']'; 17 | private const char CHAR_ITEM_SEPARATOR = ','; 18 | 19 | // Enums 20 | private enum ParsingState { 21 | Start, 22 | BeforeValue, 23 | AfterValue, 24 | End, 25 | } 26 | 27 | // Properties 28 | private IList value; 29 | 30 | 31 | // ================================================================================================================ 32 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 33 | 34 | public JSONArray() : base() { 35 | } 36 | 37 | public JSONArray(object value) : base(value) { 38 | } 39 | 40 | public JSONArray(StringBuilder input, int position) : base(input, position) { 41 | } 42 | 43 | 44 | // ================================================================================================================ 45 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 46 | 47 | public static bool matchesInput(StringBuilder input, int position) { 48 | return JSONParser.compareStringValue(CHAR_START.ToString(), input, position + JSONParser.countWhitespaceCharacters(input, position)); 49 | } 50 | 51 | public static bool matchesValue(object value) { 52 | return value is IList; 53 | } 54 | 55 | public override object getValue() { 56 | return value; 57 | } 58 | 59 | public override void setValue(object newValue) { 60 | value = (IList)newValue; 61 | } 62 | 63 | 64 | // ================================================================================================================ 65 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 66 | 67 | protected override void parseValueFromInput() { 68 | ParsingState parsingState = ParsingState.Start; 69 | char c; 70 | int i = 0; 71 | JSONValue valueObject; 72 | value = new List(); 73 | 74 | while (i < input.Length && parsingState != ParsingState.End) { 75 | c = input.ToString(inputStart + i, 1)[0]; // TODO: is this efficient? 76 | 77 | switch (parsingState) { 78 | case ParsingState.Start: 79 | if (c == CHAR_START) { 80 | // Starting array 81 | parsingState = ParsingState.BeforeValue; 82 | } else { 83 | Debug.LogError("Invalid character \"" + c + "\" when expecting array start"); 84 | } 85 | break; 86 | 87 | case ParsingState.BeforeValue: 88 | if (!JSONParser.isWhitespace(c)) { 89 | valueObject = JSONParser.parse(input, inputStart + i); 90 | i += valueObject.getInputLength() - 1; 91 | value.Add(valueObject.getValue()); 92 | parsingState = ParsingState.AfterValue; 93 | } 94 | break; 95 | 96 | case ParsingState.AfterValue: 97 | if (!JSONParser.isWhitespace(c)) { 98 | if (c == CHAR_END) { 99 | parsingState = ParsingState.End; 100 | } else if (c == CHAR_ITEM_SEPARATOR) { 101 | parsingState = ParsingState.BeforeValue; 102 | } else { 103 | Debug.LogError("Invalid character \"" + c + "\" when expecting array end or item separator"); 104 | } 105 | } 106 | break; 107 | } 108 | 109 | i++; 110 | } 111 | 112 | inputLength = i; 113 | } 114 | 115 | protected override string stringifyValue(int indentLevel, JSONStringifyOptions options) { 116 | var t = new StringBuilder(); 117 | bool hasItemsInList = false; 118 | 119 | t.Append(CHAR_START); 120 | 121 | foreach (object valueItem in value) { 122 | if (hasItemsInList) t.Append(CHAR_ITEM_SEPARATOR); 123 | 124 | if (options.lineFeedOnArrays) { 125 | t.Append(options.lineFeed + JSONEncoder.getIndents(options.individualIndent, indentLevel + 1)); 126 | } else { 127 | t.Append(options.arrayAfterSeparator); 128 | } 129 | t.Append(JSONEncoder.encode(valueItem, indentLevel + 1, options)); 130 | 131 | hasItemsInList = true; 132 | } 133 | 134 | t.Append(options.lineFeed + JSONEncoder.getIndents(options.individualIndent, indentLevel)); 135 | t.Append(CHAR_END); 136 | 137 | return t.ToString(); 138 | } 139 | } -------------------------------------------------------------------------------- /data/serialization/json/JSONDictUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | /** 4 | * Utilitary functions for dictionary 5 | * @author zeh 6 | */ 7 | public class JSONDictUtils { 8 | public static Dictionary getDict(Dictionary input, string key, Dictionary defaultValue = null) { 9 | if (input.ContainsKey(key)) return (Dictionary)input[key]; 10 | return defaultValue; 11 | } 12 | 13 | public static string getString(Dictionary input, string key, string defaultValue = "") { 14 | if (input.ContainsKey(key)) return (string)input[key]; 15 | return defaultValue; 16 | } 17 | 18 | public static bool getBoolean(Dictionary input, string key, bool defaultValue = false) { 19 | if (input.ContainsKey(key)) return (bool)input[key]; 20 | return defaultValue; 21 | } 22 | 23 | public static List getList(Dictionary input, string key, List defaultValue = null) { 24 | if (input.ContainsKey(key)) return (List)input[key]; 25 | return defaultValue; 26 | } 27 | } -------------------------------------------------------------------------------- /data/serialization/json/JSONEncoder.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | /** 4 | * A class to encode objects into JSON strings 5 | * @author zeh 6 | */ 7 | public class JSONEncoder { 8 | 9 | // ================================================================================================================ 10 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 11 | 12 | /** 13 | * Encodes an object into a string 14 | */ 15 | public static string encode(object value, bool prettyPrint = false) { 16 | if (prettyPrint) { 17 | return encode(value, 0, JSONStringifyOptions.getPretty()); 18 | } else { 19 | return encode(value, 0, JSONStringifyOptions.getCompact()); 20 | } 21 | } 22 | 23 | /** 24 | * Encodes an object into a string, correctly identifying the type 25 | */ 26 | public static string encode(object value, int indentLevel, JSONStringifyOptions options) { 27 | // Decides what it is 28 | if (JSONArray.matchesValue(value)) { 29 | return new JSONArray(value).ToString(indentLevel, options); 30 | } else if (JSONString.matchesValue(value)) { 31 | return new JSONString(value).ToString(indentLevel, options); 32 | } else if (JSONNumber.matchesValue(value)) { 33 | return new JSONNumber(value).ToString(indentLevel, options); 34 | } else if (JSONLiteral.matchesValue(value)) { 35 | return new JSONLiteral(value).ToString(indentLevel, options); 36 | } else { 37 | return new JSONObject(value).ToString(indentLevel, options); 38 | } 39 | } 40 | 41 | /** 42 | * Creates indents by repeating the same string 43 | */ 44 | public static string getIndents(string indent, int times) { 45 | var t = new StringBuilder(); 46 | while (times > 0) { 47 | t.Append(indent); 48 | times--; 49 | } 50 | return t.ToString(); 51 | } 52 | } -------------------------------------------------------------------------------- /data/serialization/json/JSONLiteral.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using UnityEngine; 4 | 5 | /** 6 | * Literals: true, false, or null 7 | * @author zeh 8 | */ 9 | public class JSONLiteral:JSONValue { 10 | 11 | // Constants 12 | private static readonly string LITERAL_TRUE = "true"; 13 | private static readonly string LITERAL_FALSE = "false"; 14 | private static readonly string LITERAL_NULL = "null"; 15 | private static readonly List LITERALS_VALID = new List{ LITERAL_TRUE, LITERAL_FALSE, LITERAL_NULL}; 16 | 17 | // Properties 18 | private object value; 19 | 20 | 21 | // ================================================================================================================ 22 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 23 | 24 | public JSONLiteral() : base() { 25 | } 26 | 27 | public JSONLiteral(object value) : base(value) { 28 | } 29 | 30 | public JSONLiteral(StringBuilder input, int position) : base(input, position) { 31 | } 32 | 33 | 34 | // ================================================================================================================ 35 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 36 | 37 | public static bool matchesInput(StringBuilder input, int position) { 38 | int whitespaceChars = JSONParser.countWhitespaceCharacters(input, position); 39 | foreach (var literal in LITERALS_VALID) { 40 | if (JSONParser.compareStringValue(literal, input, position + whitespaceChars)) return true; 41 | } 42 | return false; 43 | } 44 | 45 | public static bool matchesValue(object value) { 46 | return value == null || value is bool; 47 | } 48 | 49 | public override object getValue() { 50 | return value; 51 | } 52 | 53 | public override void setValue(object newValue) { 54 | value = newValue; 55 | } 56 | 57 | 58 | // ================================================================================================================ 59 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 60 | 61 | protected override void parseValueFromInput() { 62 | if (JSONParser.compareStringValue(LITERAL_TRUE, input, inputStart)) { 63 | inputLength = LITERAL_TRUE.Length; 64 | value = true; 65 | } else if (JSONParser.compareStringValue(LITERAL_FALSE, input, inputStart)) { 66 | inputLength = LITERAL_FALSE.Length; 67 | value = false; 68 | } else if (JSONParser.compareStringValue(LITERAL_NULL, input, inputStart)) { 69 | inputLength = LITERAL_NULL.Length; 70 | value = null; 71 | } else { 72 | Debug.LogError("Invalid literal constant"); 73 | } 74 | } 75 | 76 | protected override string stringifyValue(int indentLevel, JSONStringifyOptions options) { 77 | if (value == null) { 78 | // It's a null 79 | return LITERAL_NULL; 80 | } else if (value is bool) { 81 | // It's a Boolean 82 | return (bool)value ? LITERAL_TRUE : LITERAL_FALSE; 83 | } else { 84 | Debug.LogError("Invalid literal value"); 85 | return null; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /data/serialization/json/JSONNumber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | /** 7 | * A number is very much like a C or Java number, except that the octal and hexadecimal formats are not used. 8 | * @author zeh 9 | */ 10 | public class JSONNumber:JSONValue { 11 | 12 | // Constants 13 | private static readonly char CHAR_MINUS = '-'; 14 | private static readonly char CHAR_PLUS = '+'; 15 | private static readonly char CHAR_PERIOD = '.'; 16 | private static readonly char CHAR_ZERO = '0'; 17 | private static readonly List CHARS_DIGITS1_9 = new List{ '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 18 | private static readonly List CHARS_DIGITS = new List{ '1', '2', '3', '4', '5', '6', '7', '8', '9', CHAR_ZERO }; 19 | private static readonly List CHARS_E = new List{ 'e', 'E' }; 20 | private static readonly List CHARS_START = new List{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' }; // 21 | 22 | // Enums 23 | private enum ParsingState { 24 | Start, 25 | NumberAfterSignal, 26 | NumberDuringInteger, 27 | NumberAfterInteger, 28 | NumberDuringDecimals, 29 | NumberAfterDecimals, 30 | NumberAfterExponent, 31 | NumberDuringExponentInteger, 32 | End 33 | } 34 | 35 | // Properties 36 | private Decimal value; 37 | 38 | 39 | // ================================================================================================================ 40 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 41 | 42 | public JSONNumber() : base() { 43 | } 44 | 45 | public JSONNumber(object value) : base(value) { 46 | } 47 | 48 | public JSONNumber(StringBuilder input, int position) : base(input, position) { 49 | } 50 | 51 | 52 | // ================================================================================================================ 53 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 54 | 55 | public static bool matchesInput(StringBuilder input, int position) { 56 | int whitespaceChars = JSONParser.countWhitespaceCharacters(input, position); 57 | foreach (var startChar in CHARS_START) { 58 | if (JSONParser.compareStringValue(startChar.ToString(), input, position + whitespaceChars)) return true; 59 | } 60 | return false; 61 | } 62 | 63 | public static bool matchesValue(object value) { 64 | return value is sbyte || value is byte || value is short || value is ushort || value is int || value is uint || value is long || value is ulong || value is float || value is double || value is decimal; 65 | } 66 | 67 | public override object getValue() { 68 | return value; 69 | } 70 | 71 | public override void setValue(object newValue) { 72 | value = Convert.ToDecimal(newValue); 73 | } 74 | 75 | 76 | // ================================================================================================================ 77 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 78 | 79 | protected override void parseValueFromInput() { 80 | ParsingState parsingState = ParsingState.Start; 81 | StringBuilder parsingString = new StringBuilder(); 82 | char c; 83 | int i = 0; 84 | 85 | // TODO: maybe calculate each component separately? 86 | 87 | while (i < input.Length && parsingState != ParsingState.End) { 88 | c = input.ToString(inputStart + i, 1)[0]; // TODO: is this efficient? 89 | 90 | switch (parsingState) { 91 | case ParsingState.Start: 92 | if (!JSONParser.isWhitespace(c)) { 93 | if (c == CHAR_MINUS) { 94 | parsingString.Append(CHAR_MINUS); 95 | } else { 96 | i--; 97 | } 98 | parsingState = ParsingState.NumberAfterSignal; 99 | } 100 | break; 101 | case ParsingState.NumberAfterSignal: 102 | if (c == CHAR_ZERO) { 103 | parsingString.Append(c); 104 | parsingState = ParsingState.NumberAfterInteger; 105 | } else if (CHARS_DIGITS1_9.Contains(c)) { 106 | i--; 107 | parsingState = ParsingState.NumberDuringInteger; 108 | } else { 109 | Debug.LogError("Invalid number character"); 110 | } 111 | break; 112 | case ParsingState.NumberDuringInteger: 113 | if (CHARS_DIGITS.Contains(c)) { 114 | parsingString.Append(c); 115 | } else { 116 | i--; 117 | parsingState = ParsingState.NumberAfterInteger; 118 | } 119 | break; 120 | case ParsingState.NumberAfterInteger: 121 | if (c == CHAR_PERIOD) { 122 | parsingString.Append(c); 123 | parsingState = ParsingState.NumberDuringDecimals; 124 | } else { 125 | i--; 126 | parsingState = ParsingState.NumberAfterDecimals; 127 | } 128 | break; 129 | case ParsingState.NumberDuringDecimals: 130 | if (CHARS_DIGITS.Contains(c)) { 131 | parsingString.Append(c); 132 | } else { 133 | i--; 134 | parsingState = ParsingState.NumberAfterDecimals; 135 | } 136 | break; 137 | case ParsingState.NumberAfterDecimals: 138 | if (CHARS_E.Contains(c)) { 139 | parsingString.Append(c); 140 | parsingState = ParsingState.NumberAfterExponent; 141 | } else { 142 | i--; 143 | parsingState = ParsingState.End; 144 | } 145 | break; 146 | case ParsingState.NumberAfterExponent: 147 | if (c == CHAR_MINUS) { 148 | parsingString.Append(CHAR_MINUS); 149 | } else if (c == CHAR_PLUS) { 150 | parsingString.Append(CHAR_PLUS); 151 | } else { 152 | i--; 153 | } 154 | parsingState = ParsingState.NumberDuringExponentInteger; 155 | break; 156 | case ParsingState.NumberDuringExponentInteger: 157 | if (CHARS_DIGITS.Contains(c)) { 158 | parsingString.Append(c); 159 | } else { 160 | i--; 161 | parsingState = ParsingState.End; 162 | } 163 | break; 164 | } 165 | 166 | i++; 167 | } 168 | 169 | inputLength = i; 170 | value = Convert.ToDecimal(parsingString.ToString()); 171 | } 172 | 173 | protected override string stringifyValue(int indentLevel, JSONStringifyOptions options) { 174 | return value.ToString(); 175 | } 176 | } -------------------------------------------------------------------------------- /data/serialization/json/JSONObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using UnityEngine; 4 | 5 | /** 6 | * An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). 7 | * Each name is followed by : (colon) and the name/value pairs are separated by , (comma). 8 | * @author zeh 9 | */ 10 | public class JSONObject:JSONValue { 11 | 12 | // Constants 13 | private const char CHAR_START = '{'; 14 | private const char CHAR_END = '}'; 15 | private const char CHAR_KEY_SEPARATOR = ':'; 16 | private const char CHAR_ITEM_SEPARATOR = ','; 17 | 18 | // Enums 19 | private enum ParsingState { 20 | Start, 21 | BeforeKey, 22 | AfterKey, 23 | BeforeValue, 24 | AfterValue, 25 | End, 26 | } 27 | 28 | // Properties 29 | private Dictionary value; 30 | 31 | 32 | // ================================================================================================================ 33 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 34 | 35 | public JSONObject() : base() { 36 | } 37 | 38 | public JSONObject(object value) : base(value) { 39 | } 40 | 41 | public JSONObject(StringBuilder input, int position) : base(input, position) { 42 | } 43 | 44 | 45 | // ================================================================================================================ 46 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 47 | 48 | public static bool matchesInput(StringBuilder input, int position) { 49 | return JSONParser.compareStringValue(CHAR_START.ToString(), input, position + JSONParser.countWhitespaceCharacters(input, position)); 50 | } 51 | 52 | public static bool matchesValue(object value) { 53 | // TODO: this will always return true? 54 | return !value.GetType().IsPrimitive; 55 | } 56 | 57 | public override object getValue() { 58 | return value; 59 | } 60 | 61 | public override void setValue(object newValue) { 62 | value = (Dictionary)newValue; 63 | } 64 | 65 | 66 | // ================================================================================================================ 67 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 68 | 69 | protected override void parseValueFromInput() { 70 | ParsingState parsingState = ParsingState.Start; 71 | char c; 72 | int i = 0; 73 | string key = "undefined"; 74 | JSONValue valueObject; 75 | value = new Dictionary(); 76 | 77 | while (i < input.Length && parsingState != ParsingState.End) { 78 | c = input.ToString(inputStart + i, 1)[0]; // TODO: is this efficient? 79 | 80 | switch (parsingState) { 81 | case ParsingState.Start: 82 | if (c == CHAR_START) { 83 | // Starting object 84 | parsingState = ParsingState.BeforeKey; 85 | } else { 86 | Debug.LogError("Invalid character \"" + c + "\" when expecting object key start"); 87 | } 88 | break; 89 | 90 | case ParsingState.BeforeKey: 91 | if (!JSONParser.isWhitespace(c)) { 92 | if (JSONString.matchesInput(input, inputStart + i)) { 93 | // Key starting 94 | JSONString keyObject = new JSONString(input, inputStart + i); 95 | key = (string) keyObject.getValue(); 96 | i += keyObject.getInputLength() - 1; 97 | parsingState = ParsingState.AfterKey; 98 | } else if (c == CHAR_END) { 99 | // Premature end 100 | parsingState = ParsingState.End; 101 | } else { 102 | Debug.LogError("Invalid character \"" + c + "\" when expecting object key name"); 103 | } 104 | } 105 | break; 106 | 107 | case ParsingState.AfterKey: 108 | if (!JSONParser.isWhitespace(c)) { 109 | if (c == CHAR_KEY_SEPARATOR) { 110 | parsingState = ParsingState.BeforeValue; 111 | } else { 112 | Debug.LogError("Invalid character \"" + c + "\" when expecting object key separator"); 113 | } 114 | } 115 | break; 116 | 117 | case ParsingState.BeforeValue: 118 | if (!JSONParser.isWhitespace(c)) { 119 | valueObject = JSONParser.parse(input, inputStart + i); 120 | i += valueObject.getInputLength() - 1; 121 | value.Add(key, valueObject.getValue()); 122 | parsingState = ParsingState.AfterValue; 123 | } 124 | break; 125 | 126 | case ParsingState.AfterValue: 127 | if (!JSONParser.isWhitespace(c)) { 128 | if (c == CHAR_END) { 129 | parsingState = ParsingState.End; 130 | } else if (c == CHAR_ITEM_SEPARATOR) { 131 | parsingState = ParsingState.BeforeKey; 132 | } else { 133 | Debug.LogError("Invalid character \"" + c + "\" when expecting object key end"); 134 | } 135 | } 136 | break; 137 | } 138 | 139 | i++; 140 | } 141 | 142 | inputLength = i; 143 | } 144 | 145 | protected override string stringifyValue(int indentLevel, JSONStringifyOptions options) { 146 | var t = new StringBuilder(); 147 | bool hasItemsInList = false; 148 | 149 | t.Append(CHAR_START); 150 | 151 | foreach(KeyValuePair valueItem in value) { 152 | if (hasItemsInList) t.Append(CHAR_ITEM_SEPARATOR); 153 | 154 | t.Append(options.lineFeed + JSONEncoder.getIndents(options.individualIndent, indentLevel + 1)); 155 | t.Append(JSONEncoder.encode(valueItem.Key, 0, options)); 156 | t.Append(options.objectAfterKey + CHAR_KEY_SEPARATOR + options.objectBeforeValue); 157 | t.Append(JSONEncoder.encode(valueItem.Value, indentLevel + 1, options)); 158 | 159 | hasItemsInList = true; 160 | } 161 | 162 | t.Append(options.lineFeed + JSONEncoder.getIndents(options.individualIndent, indentLevel)); 163 | t.Append(CHAR_END); 164 | 165 | return t.ToString(); 166 | } 167 | } -------------------------------------------------------------------------------- /data/serialization/json/JSONParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using UnityEngine; 4 | 5 | /** 6 | * A class to parse Strings and turn them into specific JSON types 7 | * @author zeh 8 | */ 9 | public class JSONParser { 10 | 11 | // Constants 12 | private static readonly List CHARS_WHITESPACE = new List{ 13 | '\u0020', // Space 14 | '\u0009', // Horizontal tab 15 | '\u000A', // Line feed or new line 16 | '\u000D' // Carriage return 17 | }; 18 | 19 | 20 | // ================================================================================================================ 21 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 22 | 23 | /** 24 | * Parses a string into its correct equivalent JSON value 25 | */ 26 | public static JSONValue parse(string input) { 27 | JSONValue jsonValue = parse(new StringBuilder(input), 0); 28 | return jsonValue; 29 | } 30 | 31 | /** 32 | * Parses a StringBuilder into its correct equivalent JSON value, starting at a specific position 33 | */ 34 | public static JSONValue parse(StringBuilder input, int position) { 35 | // Decides what it is 36 | if (JSONObject.matchesInput(input, position)) { 37 | return new JSONObject(input, position); 38 | } else if (JSONArray.matchesInput(input, position)) { 39 | return new JSONArray(input, position); 40 | } else if (JSONString.matchesInput(input, position)) { 41 | return new JSONString(input, position); 42 | } else if (JSONNumber.matchesInput(input, position)) { 43 | return new JSONNumber(input, position); 44 | } else if (JSONLiteral.matchesInput(input, position)) { 45 | return new JSONLiteral(input, position); 46 | } else { 47 | // Invalid 48 | Debug.LogError("No valid type found when parsing " + input + " @ " + position + " (" + input.ToString(position, input.Length - position) + ")"); 49 | return null; 50 | } 51 | } 52 | 53 | /** 54 | * Tests whether a StringBuilder has a specific string at a specific position 55 | */ 56 | public static bool compareStringValue(string valueToSearch, StringBuilder fullText, int position) { 57 | return valueToSearch.Length <= fullText.Length - position && fullText.ToString(position, valueToSearch.Length) == valueToSearch; 58 | } 59 | 60 | /** 61 | * Counts the number of uninterrupted whitespace characters in a StringBuilder instance, starting at a specific position 62 | */ 63 | public static int countWhitespaceCharacters(StringBuilder input, int position) { 64 | int chars = 0; 65 | while (position + chars < input.Length && isWhitespace(input.ToString(position + chars, 1)[0])) { 66 | chars++; 67 | } 68 | return chars; 69 | } 70 | 71 | /** 72 | * Tests whether a specific character is a whitespace character or not 73 | */ 74 | public static bool isWhitespace(char c) { 75 | return CHARS_WHITESPACE.Contains(c); 76 | } 77 | } -------------------------------------------------------------------------------- /data/serialization/json/JSONString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | /** 7 | * A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes. 8 | * A character is represented as a single character string. A string is very much like a C or Java string. 9 | * @author zeh 10 | */ 11 | public class JSONString:JSONValue { 12 | // Constants 13 | private const char CHAR_ESCAPE = '\\'; 14 | private const char CHAR_START = '\"'; 15 | private const char CHAR_END = '\"'; 16 | private const string STRING_ESCAPE_UNICODE = @"\u"; 17 | 18 | private static readonly List CHARS_STRING_NEED_ESCAPE = new List{ 19 | '\"', // Quotation mark 20 | '\\', // Reverse solidus (backslash) 21 | '/', // Solidus (forward slash) 22 | '\b', // Backspace 23 | '\f', // Form feed 24 | '\n', // Line feed 25 | '\r', // Carriage return 26 | '\t' // Tab 27 | }; 28 | 29 | private static readonly List CHARS_STRING_ESCAPED = new List{ 30 | @"\""", // Quotation mark 31 | @"\\", // Reverse solidus (backslash) 32 | @"\/", // Solidus (forward slash) 33 | @"\b", // Backspace 34 | @"\f", // Form feed 35 | @"\n", // Line feed 36 | @"\r", // Carriage return 37 | @"\t" // Tab 38 | }; 39 | 40 | // Enums 41 | private enum ParsingState { 42 | Start, 43 | Middle, 44 | End, 45 | } 46 | 47 | // Properties 48 | private string value; 49 | 50 | 51 | // ================================================================================================================ 52 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 53 | 54 | public JSONString() : base() { 55 | } 56 | 57 | public JSONString(object value) : base(value) { 58 | } 59 | 60 | public JSONString(StringBuilder input, int position) : base(input, position) { 61 | } 62 | 63 | 64 | // ================================================================================================================ 65 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 66 | 67 | public static bool matchesInput(StringBuilder input, int position) { 68 | return JSONParser.compareStringValue(CHAR_START.ToString(), input, position + JSONParser.countWhitespaceCharacters(input, position)); 69 | } 70 | 71 | public static bool matchesValue(object value) { 72 | return value is string || value is char; 73 | } 74 | 75 | public override object getValue() { 76 | return value; 77 | } 78 | 79 | public override void setValue(object newValue) { 80 | value = (string)newValue; 81 | } 82 | 83 | 84 | // ================================================================================================================ 85 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 86 | 87 | protected override void parseValueFromInput() { 88 | ParsingState parsingState = ParsingState.Start; 89 | StringBuilder parsingString = new StringBuilder(); 90 | char c; 91 | int i = 0; 92 | 93 | while (i < input.Length && parsingState != ParsingState.End) { 94 | c = input.ToString(inputStart + i, 1)[0]; // TODO: is this efficient? 95 | 96 | switch (parsingState) { 97 | case ParsingState.Start: 98 | if (c == CHAR_START) { 99 | // Starting string 100 | parsingState = ParsingState.Middle; 101 | } else if (!JSONParser.isWhitespace(c)) { 102 | Debug.LogError("Invalid string starting character \" + c + \""); 103 | } 104 | break; 105 | 106 | case ParsingState.Middle: 107 | if (c == CHAR_END) { 108 | // Ended string 109 | parsingState = ParsingState.End; 110 | } else if (JSONParser.compareStringValue(STRING_ESCAPE_UNICODE, input, inputStart + i)) { 111 | // Unicode encoded character 112 | i++; 113 | if (i < input.Length - 5) { 114 | parsingString.Append((char)(Convert.ToInt16(input.ToString(inputStart+i+1, 4), 16))); 115 | i += 4; 116 | } else { 117 | Debug.LogError("Unicode string started with not enough characters"); 118 | } 119 | } else if (c == CHAR_ESCAPE) { 120 | // Some special character 121 | if (i < input.Length - 1) { 122 | string nc = c.ToString() + input[i+1]; 123 | int charPos = CHARS_STRING_ESCAPED.IndexOf(nc); 124 | if (charPos >= 0) { 125 | parsingString.Append(CHARS_STRING_NEED_ESCAPE[charPos]); 126 | i++; 127 | } else { 128 | Debug.LogError("Escape string without equivalent char"); 129 | } 130 | } 131 | } else { 132 | // Continued string 133 | parsingString.Append(c); 134 | } 135 | break; 136 | } 137 | 138 | i++; 139 | } 140 | 141 | inputLength = i; 142 | value = parsingString.ToString(); 143 | } 144 | 145 | protected override string stringifyValue(int indentLevel, JSONStringifyOptions options) { 146 | // Encodes a string properly, escaping chars that need escaping 147 | var t = new StringBuilder(); 148 | int i; 149 | char c; 150 | int charPos; 151 | string unicodeNumber; 152 | 153 | t.Append(CHAR_START); 154 | 155 | for (i = 0; i < value.Length; i++) { 156 | c = value[i]; 157 | //Debug.Log("CHAR => " + (int)c + " = [" + c + "]"); 158 | charPos = CHARS_STRING_NEED_ESCAPE.IndexOf(c); 159 | if (charPos >= 0) { 160 | // Special char: needs to be escaped 161 | t.Append(CHARS_STRING_ESCAPED[charPos]); 162 | } else if ((int)c < 0x20) { 163 | // Outside the range: needs to be escaped 164 | unicodeNumber = ((int)c).ToString("X"); 165 | t.Append(STRING_ESCAPE_UNICODE + unicodeNumber.PadLeft(4, '0').ToLowerInvariant()); 166 | } else if ((int)c >= 0xc200) { 167 | // Multi byte char 168 | // c2..fd means initial bytes of unicode, fe..ff means the char bytes themselves 169 | unicodeNumber = ((int)c).ToString("X"); 170 | t.Append(STRING_ESCAPE_UNICODE + unicodeNumber.PadLeft(4, '0').ToLowerInvariant()); 171 | } else { 172 | // Normal char 173 | t.Append(c); 174 | } 175 | } 176 | 177 | t.Append(CHAR_END); 178 | 179 | return t.ToString(); 180 | } 181 | } -------------------------------------------------------------------------------- /data/serialization/json/JSONStringifyOptions.cs: -------------------------------------------------------------------------------- 1 | public class JSONStringifyOptions { 2 | 3 | public string individualIndent; 4 | public string lineFeed; 5 | public string arrayAfterSeparator; 6 | public string objectAfterKey; 7 | public string objectBeforeValue; 8 | public bool lineFeedOnArrays; 9 | 10 | 11 | // ================================================================================================================ 12 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 13 | 14 | public JSONStringifyOptions() { 15 | } 16 | 17 | // ================================================================================================================ 18 | // STATIC INTERFACE ----------------------------------------------------------------------------------------------- 19 | 20 | public static JSONStringifyOptions getCompact() { 21 | var o = new JSONStringifyOptions(); 22 | return o; 23 | } 24 | 25 | public static JSONStringifyOptions getPretty() { 26 | var o = new JSONStringifyOptions(); 27 | o.individualIndent = "\t"; 28 | o.lineFeed = "\n"; 29 | o.arrayAfterSeparator = " "; 30 | o.lineFeedOnArrays = true; 31 | o.objectAfterKey = " "; 32 | o.objectBeforeValue = " "; 33 | return o; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /data/serialization/json/JSONValue.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | /** 4 | * A value can be a string in double quotes, or a number, or true or false or null, or an object or an array. 5 | * These structures can be nested. 6 | * @author zeh 7 | */ 8 | public abstract class JSONValue { 9 | 10 | // Properties 11 | protected int inputStart; 12 | protected int inputLength; 13 | protected StringBuilder input; 14 | 15 | 16 | // ================================================================================================================ 17 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 18 | 19 | public JSONValue() { 20 | } 21 | 22 | public JSONValue(object value) { 23 | setValue(value); 24 | } 25 | 26 | public JSONValue(StringBuilder input, int position) { 27 | parseInput(input, position); 28 | } 29 | 30 | 31 | // ================================================================================================================ 32 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 33 | 34 | public void parseInput(StringBuilder parseInput, int parseInputStart) { 35 | input = parseInput; 36 | inputStart = parseInputStart; 37 | parseValueFromInput(); 38 | } 39 | 40 | public string ToString(int indentLevel, JSONStringifyOptions options) { 41 | return stringifyValue(indentLevel, options); 42 | } 43 | 44 | public abstract object getValue(); 45 | 46 | public abstract void setValue(object newValue); 47 | 48 | public int getInputLength() { 49 | return inputLength; 50 | } 51 | 52 | 53 | // ================================================================================================================ 54 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 55 | 56 | protected abstract void parseValueFromInput(); 57 | 58 | protected abstract string stringifyValue(int indentLevel, JSONStringifyOptions options); 59 | } -------------------------------------------------------------------------------- /game/Editor/GlobalConfigEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor; 3 | using UnityEngine; 4 | using System.Collections; 5 | 6 | [CustomEditor(typeof(GlobalConfigGO))] 7 | public class GlobalConfigEditor:Editor { 8 | 9 | // ================================================================================================================ 10 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 11 | 12 | public override void OnInspectorGUI() { 13 | GlobalConfigGO config = (GlobalConfigGO) target; 14 | 15 | var lw = GUILayout.Width(Screen.width * 0.3f); 16 | var cw = GUILayout.Width(Screen.width * 0.21f); 17 | var rw = GUILayout.Width(Screen.width * 0.3f); 18 | var b1 = GUILayout.Width(Screen.width * 0.05f); 19 | var b2 = GUILayout.Width(Screen.width * 0.076f); 20 | var b3 = GUILayout.Width(Screen.width * 0.075f); 21 | 22 | // Header 23 | GUILayout.BeginHorizontal(); 24 | GUILayout.Label("ID", EditorStyles.label, lw); 25 | GUILayout.Label("Type", EditorStyles.label, cw); 26 | GUILayout.Label("Value", EditorStyles.label, rw); 27 | GUILayout.EndHorizontal(); 28 | 29 | // Lists entries 30 | for (var i = 0; i < config.getNumEntries(); i++) { 31 | //config.getEntryAt(i).name = GUILayout.TextField(config.getEntryAt(i).name); 32 | GUILayout.BeginHorizontal(); 33 | 34 | // ID 35 | config.getEntryAt(i).name = GUILayout.TextField(config.getEntryAt(i).name, lw); 36 | 37 | // Type 38 | config.getEntryAt(i).type = (GlobalConfigGO.ConfigEntry.Types)EditorGUILayout.EnumPopup(config.getEntryAt(i).type, EditorStyles.popup, cw); 39 | 40 | // Value 41 | switch (config.getEntryAt(i).type) { 42 | case GlobalConfigGO.ConfigEntry.Types.String: 43 | case GlobalConfigGO.ConfigEntry.Types.Int: 44 | case GlobalConfigGO.ConfigEntry.Types.Float: 45 | config.getEntryAt(i).valueString = GUILayout.TextField(config.getEntryAt(i).valueString, rw); 46 | break; 47 | case GlobalConfigGO.ConfigEntry.Types.Boolean: 48 | config.getEntryAt(i).valueBool = GUILayout.Toggle(config.getEntryAt(i).valueBool, "True", rw); 49 | break; 50 | } 51 | 52 | // Buttons 53 | if (GUILayout.Button("-", b1)) { 54 | } 55 | if (GUILayout.Button("UP", b2)) { 56 | } 57 | if (GUILayout.Button("DOWN", b3)) { 58 | } 59 | 60 | // End 61 | GUILayout.EndHorizontal(); 62 | } 63 | 64 | // Allow adding entries 65 | if (GUILayout.Button("Add entry")) { 66 | config.addEntry(new GlobalConfigGO.ConfigEntry()); 67 | } 68 | 69 | if (GUI.changed) config.save(); 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /game/GameLooper.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | /** 4 | * An object that continuously loops ("ticks") on every rendering frame, dispatching event calls every time 5 | * it does so. 6 | * 7 | * More information: http://zehfernando.com/2013/a-gamelooper-class-for-actionscript-3-projects/ 8 | * Similar to https://github.com/zeh/as3/blob/master/com/zehfernando/models/GameLooper.as 9 | * 10 | * TODO: 11 | * * allow fixedUpdate, lateUpdate 12 | * * minFps, maxFps, minInterval, maxInterval? Might work with fixed update already 13 | */ 14 | 15 | /** 16 | * From GameLooper.as: 17 | * Using GameLooper is similar to creating an ENTER_FRAME event and watching for it, but with these differences: 18 | * . Actual "tick" call rate is flexible: it can execute more than one call per frame, or skip frames as needed 19 | * . It keeps track of relative time, so it gets passed time and frame data (for correct position calculation) 20 | * . Time is flexible, so it can be multiplied/scaled, paused, and resumed 21 | * 22 | * 23 | * How to use: 24 | * 25 | * 1. Create a new instance of GameLooper. This will make the looper's onTicked() signal be fired once per frame 26 | * (same as ENTER_FRAME): 27 | * 28 | * var looper:GameLooper = new GameLooper(); // Create and start 29 | * 30 | * var looper:GameLooper = new GameLooper(true); // Creates and pauses 31 | * 32 | * 2. Create function callbacks to receive the signal (signals are like events, but simpler): 33 | * 34 | * looper.onTicked.add(onTick); 35 | * 36 | * private function onTick(currentTimeSeconds:Number, tickDeltaTimeSeconds:Number, currentTick:int):void { 37 | * var speed:Number = 10; // Speed of the box, in pixels per seconds 38 | * box.x += speed * tickDeltaTimeSeconds; 39 | * } 40 | * 41 | * 42 | * You can also: 43 | * 44 | * 1. Pause/resume the looper to pause/resume the game loop: 45 | * 46 | * looper.pause(); 47 | * looper.resume(); 48 | * 49 | * 50 | * 2. Change the time scale to make time go "faster" (currentTimeSeconds, and tickDeltaTimeSeconds): 51 | * 52 | * looper.timeScale = 2; // 2x original speed (faster motion) 53 | * looper.timeScale = 0.5; // 0.5x original speed (slower motion) 54 | * 55 | * 3. Specify a minimum FPS as a parameter. When the minFPS parameter is used, the looper is always dispatched at 56 | * least that amount of times per second, regardless of the number of frames: 57 | * 58 | * var looper:GameLooper = new GameLooper(false, 8); 59 | * 60 | * In the above example, on a SWF with 4 frames per second, onTicked would be fired twice per frame. On a SWF with 61 | * 6 frames per second, it would be fired once, and then twice every other frame. 62 | * 63 | * 4. Specify a maximum FPS as a parameter. When the maxFPS parameter is used, the looper is not dispatched more 64 | * that number of times per second: 65 | * 66 | * var looper:GameLooper = new GameLooper(false, NaN, 10); 67 | * 68 | * In the above example, on a SWF with 30 frames per second, onTicked would be fired once every 3 frames. 69 | * 70 | */ 71 | 72 | public class GameLooper { 73 | 74 | /** 75 | * Given a camera, tracks its position and which object is in its gaze 76 | */ 77 | 78 | // Handlers 79 | public delegate void GameLooperHandler(GameLooper looper); 80 | 81 | // Properties 82 | private bool _isRunning; 83 | private float _timeScale; 84 | private int _currentTick; // Current absolute frame 85 | private float _currentTime; // Current absolute time, in s 86 | private float _tickDeltaTime; // Time since last tick, in s 87 | private float _lastTimeUpdated; 88 | private bool _useRealTime; // If true, uses real (unscaled) system time first 89 | 90 | private GameObject _surrogateGameObject; 91 | private GameLooperSurrogate _surrogate; 92 | 93 | // Temp stuff to reduce garbage collection 94 | private float _now; 95 | private float _frameDeltaTime; 96 | private float _interval; 97 | 98 | // Public 99 | public event GameLooperHandler onResume; 100 | public event GameLooperHandler onPause; 101 | public event GameLooperHandler onUpdate; 102 | //public event GameLooperHandler FixedUpdate; 103 | //public event GameLooperHandler LateUpdate; 104 | 105 | 106 | // ================================================================================================================ 107 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 108 | 109 | public GameLooper() { 110 | _surrogateGameObject = new GameObject("GameLooper-" + (Time.realtimeSinceStartup)); 111 | _surrogate = _surrogateGameObject.AddComponent(); 112 | _surrogate.setGameLooper(this); 113 | _surrogate.enabled = false; 114 | 115 | _timeScale = 1; 116 | _currentTick = 0; 117 | _currentTime = 0; 118 | _tickDeltaTime = 0; 119 | _useRealTime = false; 120 | _isRunning = false; 121 | 122 | resume(); 123 | } 124 | 125 | ~GameLooper() { 126 | //GameObject.Destroy(_surrogateGameObject); 127 | _surrogateGameObject = null; 128 | _surrogate = null; 129 | onResume = null; 130 | onPause = null; 131 | onUpdate = null; 132 | } 133 | 134 | 135 | // ================================================================================================================ 136 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 137 | 138 | public void resume() { 139 | if (!_isRunning) { 140 | _isRunning = true; 141 | _lastTimeUpdated = getTime(); 142 | _frameDeltaTime = 0; 143 | _tickDeltaTime = 0; 144 | _surrogate.enabled = true; 145 | 146 | if (onResume != null) onResume(this); 147 | } 148 | } 149 | 150 | public void pause() { 151 | if (_isRunning) { 152 | _isRunning = false; 153 | _surrogate.enabled = false; 154 | 155 | if (onPause != null) onPause(this); 156 | } 157 | } 158 | 159 | public float deltaTime { 160 | get { 161 | return _frameDeltaTime; 162 | } 163 | } 164 | 165 | public float time { 166 | get { 167 | return _currentTime; 168 | } 169 | } 170 | 171 | public int frameCount { 172 | get { 173 | return _currentTick; 174 | } 175 | } 176 | 177 | public float timeScale { 178 | get { 179 | return _timeScale; 180 | } 181 | set { 182 | _timeScale = value; 183 | } 184 | } 185 | 186 | 187 | // ================================================================================================================ 188 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 189 | 190 | private float getTime() { 191 | return _useRealTime ? Time.realtimeSinceStartup : Time.time; 192 | } 193 | 194 | private void updateTime() { 195 | _now = getTime(); 196 | _frameDeltaTime = _now - _lastTimeUpdated; 197 | _tickDeltaTime = _frameDeltaTime * _timeScale; 198 | 199 | _currentTick++; 200 | _currentTime += _tickDeltaTime; 201 | 202 | _lastTimeUpdated = _now; 203 | } 204 | 205 | private void dispatchUpdate() { 206 | updateTime(); 207 | 208 | if (onUpdate != null) onUpdate(this); 209 | } 210 | 211 | /* 212 | // TODO: reactivate this later, once the time relationship is figured 213 | private void dispatchFixedUpdate() { 214 | updateTime(); 215 | 216 | if (FixedUpdate != null) FixedUpdate(this); 217 | } 218 | 219 | private void dispatchLateUpdate() { 220 | updateTime(); 221 | 222 | if (LateUpdate != null) LateUpdate(this); 223 | } 224 | */ 225 | 226 | 227 | // ================================================================================================================ 228 | // AUX CLASSES ---------------------------------------------------------------------------------------------------- 229 | 230 | class GameLooperSurrogate:MonoBehaviour { 231 | 232 | // Properies 233 | private GameLooper _gameLooper; 234 | 235 | 236 | // ================================================================================================================ 237 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 238 | 239 | void Update() { 240 | if (_gameLooper != null) _gameLooper.dispatchUpdate(); 241 | } 242 | 243 | /* 244 | void FixedUpdate() { 245 | if (_gameLooper != null) _gameLooper.dispatchFixedUpdate(); 246 | } 247 | 248 | void LateUpdate() { 249 | if (_gameLooper != null) _gameLooper.dispatchLateUpdate(); 250 | } 251 | */ 252 | 253 | 254 | // ================================================================================================================ 255 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 256 | 257 | public void setGameLooper(GameLooper gameLooper) { 258 | _gameLooper = gameLooper; 259 | } 260 | } 261 | } -------------------------------------------------------------------------------- /game/GameManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | class GameManager:MonoBehaviour { 5 | 6 | // Properties 7 | private GameLooper _looper; 8 | private SceneManager _sceneManager; 9 | 10 | private static GameManager instance; 11 | 12 | 13 | // ================================================================================================================ 14 | // EXTENDED INTERFACE --------------------------------------------------------------------------------------------- 15 | 16 | void Awake() { 17 | DontDestroyOnLoad(gameObject); 18 | 19 | instance = this; 20 | _looper = new GameLooper(); 21 | _sceneManager = new SceneManager(); 22 | } 23 | 24 | void Start() { 25 | } 26 | 27 | void Update() { 28 | } 29 | 30 | 31 | // ================================================================================================================ 32 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 33 | 34 | public GameManager getInstance() { 35 | return instance; 36 | } 37 | 38 | 39 | // ================================================================================================================ 40 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 41 | 42 | public GameLooper looper { 43 | get { 44 | return _looper; 45 | } 46 | } 47 | 48 | public SceneManager sceneManager { 49 | get { 50 | return _sceneManager; 51 | } 52 | } 53 | 54 | 55 | // ================================================================================================================ 56 | // EXTENDABLE INTERFACE ------------------------------------------------------------------------------------------- 57 | 58 | // ================================================================================================================ 59 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /game/GlobalConfigGO.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | 4 | [ExecuteInEditMode] 5 | public class GlobalConfigGO:MonoBehaviour { 6 | 7 | // Serializable properties 8 | [SerializeField] 9 | private string[] entryNames; 10 | 11 | [SerializeField] 12 | private int[] entryTypes; 13 | 14 | [SerializeField] 15 | private string[] entryValuesString; 16 | 17 | [SerializeField] 18 | private bool[] entryValuesBool; 19 | 20 | // Properties 21 | private List entries = new List(); 22 | 23 | private bool hasStarted; 24 | private static GlobalConfigGO instance; 25 | 26 | 27 | // ================================================================================================================ 28 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 29 | 30 | void Awake() { 31 | instance = this; 32 | entries = new List(); 33 | hasStarted = true; 34 | load(); 35 | } 36 | 37 | void Update() { 38 | if (Application.isEditor && !Application.isPlaying) { 39 | load(); 40 | } 41 | } 42 | 43 | 44 | // ================================================================================================================ 45 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 46 | 47 | public static GlobalConfigGO getInstance() { 48 | return instance; 49 | } 50 | 51 | public void addEntry(ConfigEntry entry) { 52 | entries.Add(entry); 53 | } 54 | 55 | public ConfigEntry getEntryAt(int index) { 56 | return entries[index]; 57 | } 58 | 59 | public int getNumEntries() { 60 | return entries.Count; 61 | } 62 | 63 | public void load() { 64 | //Debug.Log("loading serialized data, it has " + entryNames.Length + " entries"); 65 | // Load from serialized properties 66 | entries.Clear(); 67 | ConfigEntry entry; 68 | for (int i = 0; i < entryNames.Length; i++) { 69 | entry = new ConfigEntry(); 70 | entry.name = entryNames[i]; 71 | entry.type = (ConfigEntry.Types)entryTypes[i]; 72 | entry.valueString = entryValuesString[i]; 73 | entry.valueBool = entryValuesBool[i]; 74 | entries.Add(entry); 75 | } 76 | } 77 | 78 | public void save() { 79 | // Save to serialized properties 80 | if (hasStarted) { 81 | entryNames = new string[entries.Count]; 82 | entryTypes = new int[entries.Count]; 83 | entryValuesString = new string[entries.Count]; 84 | entryValuesBool = new bool[entries.Count]; 85 | for (int i = 0; i < entries.Count; i++) { 86 | entryNames[i] = entries[i].name; 87 | entryTypes[i] = (int)entries[i].type; 88 | entryValuesString[i] = entries[i].valueString; 89 | entryValuesBool[i] = entries[i].valueBool; 90 | } 91 | //Debug.Log("saving all serialized data, it has " + entries.Count + " entries; " + entryNames.Length + " recorded"); 92 | } 93 | } 94 | 95 | public string getString(string key, string defaultValue = "") { 96 | foreach (var entry in entries) { 97 | if (entry.name == key) return entry.valueString; 98 | } 99 | return defaultValue; 100 | } 101 | 102 | public bool getBool(string key, bool defaultValue = false) { 103 | foreach (var entry in entries) { 104 | if (entry.name == key) return entry.valueBool; 105 | } 106 | return defaultValue; 107 | } 108 | 109 | public float getFloat(string key, float defaultValue = 0.0f) { 110 | foreach (var entry in entries) { 111 | if (entry.name == key) return float.Parse(entry.valueString); 112 | } 113 | return defaultValue; 114 | } 115 | 116 | public int getInteger(string key, int defaultValue = 0) { 117 | foreach (var entry in entries) { 118 | if (entry.name == key) return int.Parse(entry.valueString); 119 | } 120 | return defaultValue; 121 | } 122 | 123 | 124 | // ================================================================================================================ 125 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 126 | 127 | 128 | // ================================================================================================================ 129 | // AUXILIARY CLASSES 130 | 131 | public class ConfigEntry { 132 | 133 | public enum Types : int { 134 | String = 0, 135 | Int = 1, 136 | Float = 2, 137 | Boolean = 3 138 | } 139 | 140 | public string name = ""; 141 | public Types type = Types.String; 142 | 143 | public string valueString = ""; 144 | public bool valueBool = false; 145 | 146 | public ConfigEntry() { 147 | 148 | } 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /game/SceneManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class SceneManager { 5 | 6 | // Properties 7 | public string[] levelNames; 8 | private int _currentLevel; 9 | 10 | 11 | // ================================================================================================================ 12 | // EXTENDED INTERFACE --------------------------------------------------------------------------------------------- 13 | 14 | public void Start() { 15 | // keep this object alive 16 | _currentLevel = -1; 17 | } 18 | 19 | 20 | // ================================================================================================================ 21 | // PRIVATE INTERFACE ---------------------------------------------------------------------------------------------- 22 | 23 | private void loadLevel(string __sceneName) { 24 | Application.LoadLevel(__sceneName); 25 | } 26 | 27 | private void loadLevel(int __index) { 28 | loadLevel(levelNames[__index]); 29 | } 30 | 31 | 32 | // ================================================================================================================ 33 | // ACCESSOR INTERFACE ----------------------------------------------------------------------------------------------- 34 | 35 | public int currentLevel { 36 | get { 37 | return _currentLevel; 38 | } 39 | } 40 | 41 | 42 | // ================================================================================================================ 43 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 44 | 45 | public void loadNextLevel() { 46 | int nextLevel = (_currentLevel + 1) % levelNames.Length; 47 | loadLevel(nextLevel); 48 | _currentLevel = nextLevel; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /game/navigation/NavigatorManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | public class NavigatorManager:MonoBehaviour { 6 | 7 | // Parameters 8 | public NavigatorScene startingScene; 9 | 10 | // Properties 11 | private bool isNavigating; 12 | private NavigatorScene currentScene; 13 | 14 | 15 | // ================================================================================================================ 16 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 17 | 18 | void Start() { 19 | isNavigating = false; 20 | if (startingScene != null) navigateTo(startingScene, null, true); 21 | } 22 | 23 | void Update() { 24 | 25 | } 26 | 27 | 28 | // ================================================================================================================ 29 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 30 | 31 | public void navigateTo(string gameObjectName) { 32 | GameObject gameObject = GameObject.Find(gameObjectName); 33 | if (gameObject != null && gameObject.GetComponent() != null) { 34 | navigateTo(gameObject.GetComponent()); 35 | } else { 36 | Debug.LogError("Game object with name [" + gameObjectName + "] and a NavigatorScene component not found!"); 37 | } 38 | } 39 | 40 | public void navigateTo(NavigatorScene newScene, Dictionary newBundle = null, bool immediate = false) { 41 | // Navigates to a gameobject with the camera 42 | //Debug.Log("navigating to " + gameObject + " @ " + gameObject.transform.position); 43 | 44 | if (!isNavigating && newScene != null && newScene.cameraTarget != null && newScene != currentScene) { 45 | //* .call(() => logDone("over")) 46 | 47 | // Initializations 48 | newScene.initialize(newBundle); 49 | 50 | if (immediate) { 51 | // Immediately show it 52 | if (currentScene != null) currentScene.onStartedHiding(); 53 | newScene.onStartedShowing(); 54 | Camera.main.gameObject.transform.position = newScene.cameraTarget.transform.position; 55 | if (currentScene != null) currentScene.onFinishedHiding(); 56 | newScene.onFinishedShowing(); 57 | } else { 58 | // Animate to it 59 | // Create tween 60 | var tween = ZTween.use(Camera.main.gameObject); 61 | 62 | // Call start functions 63 | if (currentScene != null) tween.call(currentScene.onStartedHiding); 64 | tween.call(newScene.onStartedShowing); 65 | 66 | // Animate 67 | tween.moveTo(newScene.cameraTarget.transform.position, 0.4f, Easing.backInOut); 68 | 69 | // Call ending functions 70 | if (currentScene != null) tween.call(currentScene.onFinishedHiding); 71 | tween.call(newScene.onFinishedShowing); 72 | 73 | // Play the tween 74 | tween.play();//.wait(1).call(Func).set("visible", false).play(); 75 | } 76 | 77 | currentScene = newScene; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /game/navigation/NavigatorScene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | public class NavigatorScene:MonoBehaviour { 5 | 6 | // Parameters 7 | public GameObject cameraTarget; 8 | public bool preferFullscreen; 9 | public string trackingId; 10 | 11 | // For tracking 12 | public delegate void NavigatorSceneEvent(); 13 | 14 | public event NavigatorSceneEvent OnStartedShowing; 15 | public event NavigatorSceneEvent OnFinishedShowing; 16 | public event NavigatorSceneEvent OnPause; 17 | public event NavigatorSceneEvent OnResume; 18 | public event NavigatorSceneEvent OnStartedHiding; 19 | public event NavigatorSceneEvent OnFinishedHiding; 20 | 21 | // Properties 22 | private bool isActivated = true; 23 | private bool disableRendering = false; 24 | 25 | private Dictionary bundle; 26 | 27 | 28 | // ================================================================================================================ 29 | // EXTENDED INTERFACE --------------------------------------------------------------------------------------------- 30 | 31 | void Start() { 32 | deActivate(); 33 | } 34 | 35 | void Update() { 36 | if (Input.GetKeyDown(KeyCode.Escape) && isActivated) { 37 | // Look for all "back" buttons... 38 | var buttons = gameObject.GetComponentsInChildren(); 39 | var acted = false; 40 | foreach (var button in buttons) { 41 | if (button.isBackEquivalent) { 42 | acted = true; 43 | button.performActionPublic(); 44 | break; 45 | } 46 | } 47 | if (!acted) { 48 | Debug.Log("No back, will quit!"); 49 | Application.Quit(); 50 | } 51 | } 52 | } 53 | 54 | 55 | // ================================================================================================================ 56 | // PUBLIC INTERFACE ------------------------------------------------------------------------------------------------ 57 | 58 | public string getBundleParameterAsString(string key, string defaultValue = "") { 59 | object value; 60 | return bundle != null && bundle.TryGetValue(key, out value) ? (string)value : defaultValue; 61 | } 62 | 63 | public int getBundleParameterAsInt(string key, int defaultValue = 0) { 64 | object value; 65 | return bundle != null && bundle.TryGetValue(key, out value) ? (int)value : defaultValue; 66 | } 67 | 68 | public T getBundleParameterAs(string key, T defaultValue = default(T)) { 69 | object value; 70 | return bundle != null && bundle.TryGetValue(key, out value) ? (T)value : defaultValue; 71 | } 72 | 73 | 74 | // ================================================================================================================ 75 | // EVENT INTERFACE ------------------------------------------------------------------------------------------------ 76 | 77 | public virtual void initialize(Dictionary newBundle) { 78 | bundle = newBundle; 79 | } 80 | 81 | public virtual void onStartedShowing() { 82 | // Changes screen 83 | ApplicationChrome.statusBarState = ApplicationChrome.navigationBarState = ApplicationChrome.States.Visible; 84 | ApplicationChrome.dimmed = preferFullscreen; 85 | 86 | activate(); 87 | 88 | // Track page 89 | //Debug.Log("Trying to track ::: [" + "navigation:screen:" + trackingId + "]"); 90 | TrackingManager.getInstance().trackScreen(trackingId); 91 | 92 | if (OnStartedShowing != null) OnStartedShowing(); 93 | } 94 | 95 | public virtual void onFinishedShowing() { 96 | if (OnFinishedShowing != null) OnFinishedShowing(); 97 | } 98 | 99 | public virtual void onPause() { 100 | if (OnPause != null) OnPause(); 101 | } 102 | 103 | public virtual void onResume() { 104 | if (OnResume != null) OnResume(); 105 | } 106 | 107 | public virtual void onStartedHiding() { 108 | if (OnStartedHiding != null) OnStartedHiding(); 109 | } 110 | 111 | public virtual void onFinishedHiding() { 112 | deActivate(); 113 | if (OnFinishedHiding != null) OnFinishedHiding(); 114 | } 115 | 116 | 117 | // ================================================================================================================ 118 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 119 | 120 | private void deActivate() { 121 | //gameObject.SetActive(false); 122 | //renderer.enabled = false; 123 | //gameObject.SetActive(false); 124 | if (isActivated) { 125 | // Disable all rendering 126 | if (disableRendering) { 127 | var renderers = GetComponentsInChildren(); 128 | foreach (var renderer in renderers) { 129 | renderer.enabled = false; 130 | } 131 | } 132 | 133 | isActivated = false; 134 | } 135 | } 136 | 137 | private void activate() { 138 | //gameObject.SetActive(true); 139 | //renderer.enabled = true; 140 | //gameObject.SetActive(true); 141 | if (!isActivated) { 142 | // Disable all rendering 143 | if (disableRendering) { 144 | var renderers = GetComponentsInChildren(); 145 | foreach (var renderer in renderers) { 146 | renderer.enabled = true; 147 | } 148 | } 149 | 150 | isActivated = true; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /game/services/ServiceLocator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | 4 | public class ServiceLocator:MonoBehaviour { 5 | 6 | // Properties 7 | public List _services; 8 | 9 | private static ServiceLocator instance; 10 | 11 | 12 | // ================================================================================================================ 13 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 14 | 15 | void Awake() { 16 | instance = this; 17 | } 18 | 19 | 20 | // ================================================================================================================ 21 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 22 | 23 | public static T get() where T : MonoBehaviour { 24 | List services = instance._services; 25 | for (int i = 0; i < services.Count; i++) { 26 | if (services[i] is T) return (T)services[i]; 27 | } 28 | Debug.LogError("Tried locating a service of type " + typeof(T) + " that could not be found!"); 29 | return default(T); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /geometry/FlatMesh.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | public class FlatMesh { 6 | 7 | /* 8 | * Given a mesh (vertices, normals, triangles, and colors), calculates a new mesh with: 9 | * . Flat shaded triangles (splitting edges) 10 | * . Fake SSAO/Ambient Occlusion (with vertex colors) 11 | */ 12 | 13 | // Properties 14 | private Vector3[] vertices; 15 | private Vector3[] normals; 16 | private int[] triangles; 17 | private Color32[] colors; 18 | 19 | private Vector3[] newVertices; 20 | private int[] newTriangles; 21 | private Vector2[] newUvs; 22 | private Color32[] newColors; 23 | 24 | private bool isMeshDirty; // If true, the mesh needs to be recalculated 25 | 26 | 27 | // ================================================================================================================ 28 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 29 | 30 | public FlatMesh(Vector3[] __vertices, Vector3[] __normals, int[] __triangles, Color32[] __colors) { 31 | D.Log("FlatMesh initialized; " + __vertices.Length + " vertices, " + (__triangles.Length/3) + " triangles, " + __colors.Length + " colors"); 32 | 33 | vertices = __vertices; 34 | normals = __normals; 35 | triangles = __triangles; 36 | colors = __colors; 37 | 38 | isMeshDirty = true; 39 | } 40 | 41 | 42 | // ================================================================================================================ 43 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 44 | 45 | public Vector3[] flatVertices { 46 | get { 47 | if (isMeshDirty) recalculateFlatMesh(); 48 | return newVertices; 49 | } 50 | } 51 | 52 | public int[] flatTriangles { 53 | get { 54 | if (isMeshDirty) recalculateFlatMesh(); 55 | return newTriangles; 56 | } 57 | } 58 | 59 | public Vector2[] flatUvs { 60 | get { 61 | if (isMeshDirty) recalculateFlatMesh(); 62 | return newUvs; 63 | } 64 | } 65 | 66 | public Color32[] flatColors { 67 | get { 68 | if (isMeshDirty) recalculateFlatMesh(); 69 | return newColors; 70 | } 71 | } 72 | 73 | 74 | // ================================================================================================================ 75 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 76 | 77 | private void recalculateFlatMesh() { 78 | // Creates the new mesh 79 | int numTriangles = triangles.Length / 3; 80 | int numVertices = vertices.Length; 81 | 82 | int i, j, t; 83 | Vector3 v; 84 | //Vector3 n; 85 | Color32 c; 86 | 87 | // Calculates ambient occlusion 88 | 89 | float ti = Time.realtimeSinceStartup; 90 | 91 | // Finds darkness of every vertice 92 | float[] darkness = new float[numVertices]; // 0 (normal color) to 1 (black) 93 | 94 | // Creates ray samples 95 | int numSamples = 20; 96 | Vector3[] rotations = new Vector3[numSamples]; 97 | Vector3 o; 98 | float radius = 400f; 99 | 100 | for (i = 0; i < numSamples; i++) { 101 | rotations[i] = UnityEngine.Random.onUnitSphere * radius; 102 | } 103 | 104 | List myRots; 105 | 106 | Vector3 v1xv2, v2xv3, v3xv1; 107 | Vector3 v1, v2, v3; 108 | 109 | for (i = 0; i < numVertices; i++) { 110 | // For every vertice... 111 | o = vertices[i] + normals[i]; 112 | 113 | myRots = new List(rotations); 114 | 115 | for (t = 0; t < numTriangles * 3; t += 3) { 116 | // ...check every triangle... 117 | 118 | v1 = vertices[triangles[t]] - o; 119 | v2 = vertices[triangles[t+1]] - o; 120 | v3 = vertices[triangles[t+2]] - o; 121 | 122 | v1xv2 = Vector3.Cross(v1, v2); 123 | v2xv3 = Vector3.Cross(v2, v3); 124 | v3xv1 = Vector3.Cross(v3, v1); 125 | 126 | for (j = 0; j < myRots.Count; j++) { 127 | // ...against every ray 128 | if (GeomUtils.rayIntersectsTriangleSF_precalculated(o, myRots[j], v1xv2, v2xv3, v3xv1)) { 129 | myRots.RemoveAt(j); 130 | j--; 131 | } 132 | } 133 | } 134 | 135 | darkness[i] = Math.Abs(((float)(rotations.Length - myRots.Count) / numSamples - 0.5f) * 2f); 136 | //if (i % 100 == 0 || hits >= numSamples) { 137 | //if (i % 100 == 0) D.Log ("@" + i + " : " + o + " -> " + Vector3.back + " >> hits = " + hits + ", " + darkness[i]); 138 | } 139 | 140 | // Applies darkness to the vertex colors 141 | for (i = 0; i < numVertices; i++) { 142 | colors[i].r = (byte)(colors[i].r * (1-darkness[i])); 143 | colors[i].g = (byte)(colors[i].g * (1-darkness[i])); 144 | colors[i].b = (byte)(colors[i].b * (1-darkness[i])); 145 | //colors[i].r = (byte)Math.Max(0, colors[i].r - Math.Floor(darkness[i] * 255f)); 146 | //colors[i].g = (byte)Math.Max(0, colors[i].g - Math.Floor(darkness[i] * 255f)); 147 | //colors[i].b = (byte)Math.Max(0, colors[i].b - Math.Floor(darkness[i] * 255f)); 148 | } 149 | 150 | D.Log("Took " + (Time.realtimeSinceStartup-ti) + "s to generate vertices ambient occlusion."); 151 | 152 | // Creates new properties 153 | newVertices = new Vector3[numTriangles * 3]; 154 | newTriangles = new int[numTriangles * 3]; 155 | newUvs = new Vector2[numTriangles * 3]; 156 | newColors = new Color32[numTriangles * 3]; 157 | 158 | // Splits edges 159 | for (i = 0; i < triangles.Length; i++) { 160 | v = vertices[triangles[i]]; 161 | c = colors[triangles[i]]; 162 | newVertices[i] = new Vector3(v.x, v.y, v.z); 163 | newTriangles[i] = i; 164 | newColors[i] = new Color32(c.r, c.g, c.b, c.a); 165 | } 166 | 167 | // Creates individual UVs 168 | float uvNudge = 17f/256f; // Offset so the triangle fits 169 | for (i = 0; i < triangles.Length; i += 3) { 170 | newUvs[i+0] = new Vector2(0, 0 + uvNudge); 171 | newUvs[i+1] = new Vector2(0.5f, 1 - uvNudge); 172 | newUvs[i+2] = new Vector2(1, 0 + uvNudge); 173 | } 174 | 175 | isMeshDirty = false; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /geometry/objects/Circle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | #if UNITY_EDITOR 6 | using UnityEditor; 7 | #endif 8 | 9 | #if UNITY_EDITOR 10 | [ExecuteInEditMode()] 11 | #endif 12 | 13 | public class Circle:CustomMeshObject { 14 | 15 | /* 16 | A simple circle. 17 | */ 18 | 19 | // Properties 20 | [SerializeField] private float _radius = 100; 21 | [SerializeField] private int _segments = 50; 22 | [SerializeField] private Color _color = new Color(0.5f, 0.5f, 0.5f); 23 | 24 | 25 | // ================================================================================================================ 26 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 27 | 28 | void Awake() { 29 | } 30 | 31 | 32 | // ================================================================================================================ 33 | // EDITOR INTERFACE ----------------------------------------------------------------------------------------------- 34 | 35 | #if UNITY_EDITOR 36 | 37 | [MenuItem("GameObject/Create Other/Circle")] 38 | public static void createNew() { 39 | var newObject = createNewGameObjectWithScript("Circle"); 40 | Undo.RegisterCreatedObjectUndo(newObject, "Create Circle"); 41 | Selection.objects = new GameObject[] { newObject }; 42 | } 43 | 44 | #endif 45 | 46 | 47 | // ================================================================================================================ 48 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 49 | 50 | public static GameObject createNewGameObjectWithScript(string name) { 51 | var gameObject = createNewGameObject(name); 52 | gameObject.AddComponent(); 53 | return gameObject; 54 | } 55 | 56 | 57 | // ================================================================================================================ 58 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 59 | 60 | public float radius { 61 | get { return _radius; } 62 | set { 63 | _radius = value; 64 | requestMeshGeneration(); 65 | } 66 | } 67 | 68 | public int segments { 69 | get { return _segments; } 70 | set { 71 | _segments = value; 72 | requestMeshGeneration(); 73 | } 74 | } 75 | 76 | public Color color { 77 | get { return _color; } 78 | set { 79 | _color = value; 80 | requestMeshGeneration(); 81 | } 82 | } 83 | 84 | 85 | // ================================================================================================================ 86 | // EXTENDED INTERFACE --------------------------------------------------------------------------------------------- 87 | 88 | protected override void updateMesh(Mesh mesh) { 89 | generateMesh(mesh); 90 | } 91 | 92 | 93 | // ================================================================================================================ 94 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 95 | 96 | private void generateMesh(Mesh mesh) { 97 | // Generate the entire mesh 98 | if (mesh != null) { 99 | int segs = Math.Max(4, _segments); 100 | float rad = Math.Max(0, _radius); 101 | if (rad == 0) segs = 0; 102 | if (segs == 0) rad = 0; 103 | int numVerticesTotal = segs + 1; 104 | int numTrianglesTotal = segs; 105 | 106 | Vector3[] vertices = new Vector3[numVerticesTotal]; 107 | int[] triangles = new int[numTrianglesTotal * 3]; 108 | Color[] colors = new Color[numVerticesTotal]; 109 | 110 | // First vertex 111 | vertices[0].x = 0; 112 | vertices[0].y = 0; 113 | vertices[0].z = 0; 114 | 115 | // All other vertices 116 | for (int i = 0; i < segs; i++) { 117 | double f = (double)i / (double)segs; 118 | vertices[i+1].x = (float)Math.Sin(f * 2.0 * Math.PI) * _radius; 119 | vertices[i+1].y = (float)Math.Cos(f * 2.0 * Math.PI) * _radius; 120 | vertices[i+1].z = 0; 121 | } 122 | 123 | // Triangles 124 | int trianglesPosition = 0; 125 | for (int i = 0; i < numTrianglesTotal; i++) { 126 | triangles[trianglesPosition++] = 0; 127 | triangles[trianglesPosition++] = i + 1; 128 | if (i + 2 < numVerticesTotal) { 129 | triangles[trianglesPosition++] = i + 2; 130 | } else { 131 | triangles[trianglesPosition++] = (i + 2) % numTrianglesTotal; 132 | } 133 | } 134 | 135 | // Colors 136 | for (int i = 0; i < numVerticesTotal; i++) { 137 | colors[i] = _color; 138 | } 139 | 140 | // Create mesh 141 | mesh.Clear(); 142 | mesh.vertices = vertices; 143 | mesh.colors = colors; 144 | mesh.SetTriangles(triangles, 0); 145 | mesh.RecalculateNormals(); 146 | mesh.RecalculateBounds(); 147 | mesh.Optimize(); 148 | 149 | updateUVs(mesh); 150 | } 151 | } 152 | 153 | private void updateUVs(Mesh mesh) { 154 | // Update the uvs (uses the same locations) 155 | Vector3[] vertices = mesh.vertices; 156 | Vector2[] uvs = new Vector2[vertices.Length]; 157 | 158 | for (int i = 0; i < uvs.Length; i++) { 159 | uvs[i] = new Vector2(vertices[i].x, vertices[i].y); 160 | } 161 | 162 | mesh.uv = uvs; 163 | } 164 | } -------------------------------------------------------------------------------- /geometry/objects/CustomMeshObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using UnityEngine; 6 | #if UNITY_EDITOR 7 | using UnityEditor; 8 | #endif 9 | 10 | #if UNITY_EDITOR 11 | [ExecuteInEditMode()] 12 | #endif 13 | 14 | public class CustomMeshObject:MonoBehaviour { 15 | 16 | /* 17 | A basic class for custom meshes (avoids most issues with editor errors). Meant to be extended. 18 | */ 19 | 20 | // Properties 21 | private MeshFilter meshFilter; 22 | private Mesh mesh; 23 | 24 | private bool isDirty = true; 25 | 26 | 27 | // ================================================================================================================ 28 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 29 | 30 | void Update() { 31 | if (Application.isEditor && !Application.isPlaying) { 32 | // Always update while editing 33 | generateMesh(); 34 | } 35 | } 36 | 37 | void LateUpdate() { 38 | if (isDirty) { 39 | generateMesh(); 40 | } 41 | } 42 | 43 | 44 | // ================================================================================================================ 45 | // EDITOR INTERFACE ----------------------------------------------------------------------------------------------- 46 | 47 | #if UNITY_EDITOR 48 | 49 | /* 50 | // Example: 51 | [MenuItem("GameObject/Create Other/Rounded Cube")] 52 | public static void createNew() { 53 | newObject = createNewGameObjectFromEditor("Rounded Cube"); 54 | newObject.AddComponent(); 55 | 56 | // Make this action undoable 57 | Undo.RegisterCreatedObjectUndo(newObject, "Create " + name); 58 | 59 | // Select the new object 60 | Selection.objects = new GameObject[] { newObject }; 61 | 62 | return newObject; 63 | } 64 | */ 65 | 66 | #endif 67 | 68 | 69 | 70 | // ================================================================================================================ 71 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 72 | 73 | public static GameObject createNewGameObject(string name) { 74 | // Create the actual object 75 | GameObject newObject = new GameObject(name); 76 | 77 | // Create mesh filter (with no mesh yet) 78 | newObject.AddComponent(); 79 | 80 | // Create mesh renderer 81 | newObject.AddComponent(); 82 | 83 | // Debug.Log("Creating, class =======> " + MethodBase.GetCurrentMethod().DeclaringType); 84 | 85 | /* 86 | // Create mesh collider 87 | newObject.AddComponent(); 88 | */ 89 | 90 | return newObject; 91 | } 92 | 93 | 94 | // ================================================================================================================ 95 | // EXTENDABLE INTERFACE ------------------------------------------------------------------------------------------- 96 | 97 | // EXTEND with override 98 | protected virtual void updateMesh(Mesh mesh) { 99 | mesh.Clear(); 100 | /* 101 | mesh.vertices = vertices; 102 | mesh.colors = colors; 103 | mesh.SetTriangles(triangles, 0); 104 | mesh.RecalculateNormals(); 105 | mesh.RecalculateBounds(); 106 | mesh.Optimize(); 107 | mesh.uv = uvs; 108 | */ 109 | } 110 | 111 | 112 | // ================================================================================================================ 113 | // INTERNAL INTERFACE FOR EXTENDED -------------------------------------------------------------------------------- 114 | 115 | protected void requestMeshGeneration() { 116 | isDirty = true; 117 | } 118 | 119 | 120 | // ================================================================================================================ 121 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 122 | 123 | private void generateMesh() { 124 | // Generate the entire mesh 125 | if (meshFilter == null) meshFilter = gameObject.GetComponent(); 126 | if ((mesh == null && meshFilter != null) || (meshFilter != null && meshFilter.sharedMesh == null)) { 127 | if (meshFilter.sharedMesh == null) { 128 | // Need to create a mesh first 129 | Mesh newMesh = new Mesh(); 130 | newMesh.name = name + " mesh"; 131 | meshFilter.mesh = newMesh; 132 | } 133 | 134 | // Create own mesh reference 135 | if (Application.isPlaying) { 136 | mesh = meshFilter.mesh; 137 | } else { 138 | // Avoid stupid warnings about leaks by making a deep copy 139 | // The old one will still be garbage collected later when the scene is saved 140 | Mesh meshCopy = (Mesh) Mesh.Instantiate(meshFilter.sharedMesh); 141 | meshCopy.name = meshFilter.sharedMesh.name; 142 | mesh = meshFilter.mesh = meshCopy; 143 | } 144 | } 145 | 146 | isDirty = false; 147 | updateMesh(mesh); 148 | } 149 | } -------------------------------------------------------------------------------- /geometry/objects/Editor/RoundedCubeEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor; 3 | using UnityEngine; 4 | using System.Collections; 5 | 6 | [CustomEditor(typeof(RoundedCube))] 7 | public class RoundedCubeEditor:Editor { 8 | 9 | // ================================================================================================================ 10 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 11 | 12 | public override void OnInspectorGUI() { 13 | RoundedCube roundedCube = (RoundedCube) target; 14 | 15 | roundedCube.width = EditorGUILayout.FloatField("Width", roundedCube.width); 16 | roundedCube.height = EditorGUILayout.FloatField("Height", roundedCube.height); 17 | roundedCube.depth = EditorGUILayout.FloatField("Depth", roundedCube.depth); 18 | roundedCube.cornerRadius = EditorGUILayout.FloatField("Corner radius", roundedCube.cornerRadius); 19 | roundedCube.offsetTopX = EditorGUILayout.FloatField("Top Offset (X)", roundedCube.offsetTopX); 20 | roundedCube.offsetTopY = EditorGUILayout.FloatField("Top Offset (Y)", roundedCube.offsetTopY); 21 | roundedCube.cornerSegments = EditorGUILayout.IntSlider("Corner segments", roundedCube.cornerSegments, 0, 32); 22 | roundedCube.color = EditorGUILayout.ColorField("Color", roundedCube.color); 23 | 24 | if (GUI.changed) { 25 | EditorUtility.SetDirty(roundedCube); 26 | } 27 | 28 | /* 29 | serializedObject.Update(); 30 | 31 | // Totals 32 | //GUILayout.LabelField("Triangles", 10.ToString()); 33 | 34 | //GUILayout.BeginVertical("Terrain", "box"); 35 | //GUILayout.Space(20); 36 | //GUILayout.EndVertical(); 37 | 38 | // Terrain info 39 | //terrainTarget.title = EditorGUILayout.TextField("Terrain title", terrainTarget.title); 40 | 41 | // Other 42 | terrainTarget.setTileTypeList((TiledTerrainTileTypes)EditorGUILayout.ObjectField("List of tile types", terrainTarget.getTileTypeList(), typeof(TiledTerrainTileTypes), true)); 43 | 44 | //((TiledTerrain)target).ping();//lookAtPoint = EditorGUILayout.Vector3Field ("Look At Point", target.lookAtPoint); 45 | serializedObject.ApplyModifiedProperties(); 46 | */ 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /geometry/objects/LineQuad.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | #if UNITY_EDITOR 6 | using UnityEditor; 7 | #endif 8 | 9 | #if UNITY_EDITOR 10 | [ExecuteInEditMode()] 11 | #endif 12 | 13 | public class LineQuad:CustomMeshObject { 14 | 15 | /* 16 | A line with an origin point and correct tiled mapping 17 | */ 18 | 19 | // Constants 20 | public static Color COLOR_DEFAULT = new Color(1, 0.5f, 0.5f, 1); 21 | public static float THICKNESS_DEFAULT = 0.1f; 22 | 23 | // Properties 24 | private float _length; 25 | private float _thickness; 26 | private Vector2 _start; 27 | private Vector2 _end; 28 | private Color _color; 29 | 30 | 31 | // ================================================================================================================ 32 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 33 | 34 | void Awake() { 35 | _thickness = THICKNESS_DEFAULT; 36 | _color = COLOR_DEFAULT; 37 | _start = new Vector2(0, 0); 38 | _end = new Vector2(0, 0); 39 | } 40 | 41 | 42 | // ================================================================================================================ 43 | // EDITOR INTERFACE ----------------------------------------------------------------------------------------------- 44 | 45 | #if UNITY_EDITOR 46 | 47 | [MenuItem("GameObject/Create Other/Line Quad")] 48 | public static void createNew() { 49 | var newObject = createNewGameObjectWithScript("Line Quad"); 50 | Undo.RegisterCreatedObjectUndo(newObject, "Create Line Quad"); 51 | Selection.objects = new GameObject[] { newObject }; 52 | } 53 | 54 | #endif 55 | 56 | 57 | 58 | // ================================================================================================================ 59 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 60 | 61 | public static GameObject createNewGameObjectWithScript(string name) { 62 | var gameObject = createNewGameObject(name); 63 | gameObject.AddComponent(); 64 | return gameObject; 65 | } 66 | 67 | 68 | // ================================================================================================================ 69 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 70 | 71 | public float length { 72 | get { 73 | return _length; 74 | } 75 | } 76 | 77 | // TODO: allow setting the length! 78 | 79 | public float thickness { 80 | get { return _thickness; } 81 | set { 82 | _thickness = value; 83 | requestMeshGeneration(); 84 | } 85 | } 86 | 87 | public Vector2 start { 88 | get { return _start; } 89 | set { 90 | _start = value; 91 | updatePositions(); 92 | } 93 | } 94 | 95 | public Vector2 end { 96 | get { return _end; } 97 | set { 98 | _end = value; 99 | updatePositions(); 100 | } 101 | } 102 | 103 | public Color color { 104 | get { return _color; } 105 | set { 106 | _color = value; 107 | updateColor(); 108 | } 109 | } 110 | 111 | 112 | // ================================================================================================================ 113 | // EXTENDED INTERFACE --------------------------------------------------------------------------------------------- 114 | 115 | protected override void updateMesh(Mesh mesh) { 116 | generateMesh(mesh); 117 | } 118 | 119 | 120 | // ================================================================================================================ 121 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 122 | 123 | private void updatePositions() { 124 | // Generates the whole line 125 | 126 | _length = Vector2.Distance(_start, _end); 127 | transform.localPosition = new Vector3(_start.x, _start.y, 0); 128 | transform.localRotation = Quaternion.Euler(0, 0, Mathf.Atan2(_end.y - _start.y, _end.x - _start.x) * Mathf.Rad2Deg); 129 | 130 | requestMeshGeneration(); 131 | } 132 | 133 | private void generateMesh(Mesh mesh) { 134 | // Create vertices 135 | Vector3[] vertices = new Vector3[] { 136 | new Vector3(0, -_thickness / 2, 0), 137 | new Vector3(_length, -_thickness / 2, 0), 138 | new Vector3(0, _thickness / 2, 0), 139 | new Vector3(_length, _thickness / 2, 0) 140 | }; 141 | 142 | // Create triangles 143 | int[] triangles = new int[] {0, 2, 1, 1, 2, 3}; 144 | 145 | // Create mesh 146 | mesh.Clear(); 147 | mesh.vertices = vertices; 148 | mesh.SetTriangles(triangles, 0); 149 | mesh.RecalculateNormals(); 150 | mesh.RecalculateBounds(); 151 | mesh.Optimize(); 152 | 153 | updateUVs(mesh); 154 | updateColor(); 155 | } 156 | 157 | private void updateUVs(Mesh mesh) { 158 | // Update the uvs 159 | float uvLength = _length / _thickness / 2f; 160 | Vector2[] uvs = new Vector2[] { 161 | new Vector2(0, 0), 162 | new Vector2(uvLength, 0), 163 | new Vector2(0, 1), 164 | new Vector2(uvLength, 1) 165 | }; 166 | mesh.uv = uvs; 167 | } 168 | 169 | private void updateColor() { 170 | GetComponent().materials[0].color = _color; 171 | } 172 | } -------------------------------------------------------------------------------- /geometry/objects/UVLine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | #if UNITY_EDITOR 6 | using UnityEditor; 7 | #endif 8 | 9 | #if UNITY_EDITOR 10 | [ExecuteInEditMode()] 11 | #endif 12 | 13 | public class UVLine:MonoBehaviour { 14 | 15 | /* 16 | A line with an origin point: set start and end vectors. Uv mapping is adjusted 17 | */ 18 | 19 | // Constants 20 | public static Color COLOR_DRAGGING = new Color(0, 1, 1, 0.4f); 21 | public static Color COLOR_NORMAL = new Color(0, 1, 1, 1); 22 | public static Color COLOR_INVALID = new Color(1, 0.5f, 0.5f, 0.5f); 23 | public static float THICKNESS_DEFAULT = 16; 24 | 25 | // Properties 26 | private float _length; 27 | private float _thickness; 28 | private MeshFilter meshFilter; 29 | private Mesh mesh; 30 | private Vector2 _start; 31 | private Vector2 _end; 32 | private Color _color; 33 | 34 | 35 | // ================================================================================================================ 36 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 37 | 38 | void Awake() { 39 | _thickness = THICKNESS_DEFAULT; 40 | _color = COLOR_NORMAL; 41 | _start = new Vector2(0, 0); 42 | _end = new Vector2(0, 0); 43 | } 44 | 45 | void Start() { 46 | generate(); 47 | } 48 | 49 | void Update() { 50 | if (Application.isEditor && !Application.isPlaying) { 51 | // Always update while editing 52 | generate(); 53 | } 54 | } 55 | 56 | 57 | // ================================================================================================================ 58 | // EDITOR INTERFACE ----------------------------------------------------------------------------------------------- 59 | 60 | #if UNITY_EDITOR 61 | 62 | [MenuItem("GameObject/Create Other/Line")] 63 | static void CreateNew() { 64 | // Create the actual object 65 | GameObject newObject = new GameObject("Line"); 66 | 67 | Mesh newMesh = new Mesh(); 68 | newMesh.name = "Line mesh"; 69 | 70 | MeshFilter newMeshFilter = newObject.AddComponent(); 71 | newMeshFilter.mesh = newMesh; 72 | 73 | // Create mesh collider 74 | newObject.AddComponent(); 75 | 76 | // Create mesh renderer 77 | newObject.AddComponent(); 78 | 79 | // Attach script 80 | UVLine newLineScript = newObject.AddComponent(); 81 | newLineScript.start = new Vector2(0, 0); 82 | newLineScript.end = new Vector2(10, 0); 83 | 84 | // Make this action undoable 85 | Undo.RegisterCreatedObjectUndo(newObject, "Create Line"); 86 | 87 | // Select the new object 88 | Selection.objects = new GameObject[] { newObject }; 89 | //Selection.activeTransform = gameObject.transform; 90 | } 91 | 92 | #endif 93 | 94 | 95 | 96 | // ================================================================================================================ 97 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 98 | 99 | // ================================================================================================================ 100 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 101 | 102 | public float length { 103 | get { 104 | return _length; 105 | } 106 | } 107 | 108 | public float thickness { 109 | get { 110 | return _thickness; 111 | } 112 | set { 113 | _thickness = value; 114 | generateMesh(); 115 | } 116 | } 117 | 118 | public Vector2 start { 119 | get { 120 | return _start; 121 | } 122 | set { 123 | _start = value; 124 | generate(); 125 | } 126 | } 127 | 128 | public Vector2 end { 129 | get { 130 | return _end; 131 | } 132 | set { 133 | _end = value; 134 | generate(); 135 | } 136 | } 137 | 138 | public Color color { 139 | get { 140 | return _color; 141 | } 142 | set { 143 | _color = value; 144 | updateColor(); 145 | } 146 | } 147 | 148 | 149 | // ================================================================================================================ 150 | // EXTENDABLE INTERFACE ------------------------------------------------------------------------------------------- 151 | 152 | 153 | 154 | // ================================================================================================================ 155 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 156 | 157 | private void generate() { 158 | // Generates the whole line 159 | 160 | _length = Vector2.Distance(_start, _end); 161 | transform.position = new Vector3(_start.x, 0, _start.y); 162 | transform.rotation = Quaternion.Euler(270, -Mathf.Atan2(_end.y - _start.y, _end.x - _start.x) * Mathf.Rad2Deg, 0); 163 | 164 | generateMesh(); 165 | } 166 | 167 | private void generateMesh() { 168 | if (meshFilter == null) meshFilter = gameObject.GetComponent(); 169 | if (mesh == null && meshFilter != null) mesh = Application.isPlaying ? meshFilter.mesh : meshFilter.sharedMesh; 170 | 171 | //if (mesh == null) mesh = (Application.isEditor && !Application.isPlaying) ? meshFilter.sharedMesh : meshFilter.mesh; 172 | 173 | if (mesh != null) { 174 | // Create vertices 175 | Vector3[] vertices = new Vector3[] { 176 | new Vector3(0, -_thickness / 2, 0), 177 | new Vector3(_length, -_thickness / 2, 0), 178 | new Vector3(0, _thickness / 2, 0), 179 | new Vector3(_length, _thickness / 2, 0) 180 | }; 181 | 182 | // Create triangles 183 | int[] triangles = new int[] {0, 1, 2, 2, 1, 3}; 184 | 185 | // Create mesh 186 | mesh.Clear(); 187 | mesh.vertices = vertices; 188 | mesh.SetTriangles(triangles, 0); 189 | mesh.RecalculateNormals(); 190 | mesh.RecalculateBounds(); 191 | mesh.Optimize(); 192 | 193 | updateUVs(); 194 | updateColor(); 195 | 196 | //Debug.Log("Generated mesh, length = " + _length); 197 | } 198 | } 199 | 200 | private void updateUVs(float __uvOffsetX = 0) { 201 | // Update the uvs according to size and time 202 | float uvLength = _length / _thickness / 2f; 203 | Vector2[] uvs = new Vector2[] { 204 | new Vector2(__uvOffsetX, 0), 205 | new Vector2(__uvOffsetX + uvLength, 0), 206 | new Vector2(__uvOffsetX, 1), 207 | new Vector2(__uvOffsetX + uvLength, 1) 208 | }; 209 | mesh.uv = uvs; 210 | } 211 | 212 | private void updateColor() { 213 | GetComponent().materials[0].color = _color; 214 | } 215 | } -------------------------------------------------------------------------------- /localization/ValueListManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | 5 | #if UNITY_EDITOR 6 | [ExecuteInEditMode()] 7 | #endif 8 | 9 | public class ValueListManager:MonoBehaviour { 10 | 11 | // Static properties 12 | private static Dictionary instances; 13 | 14 | // Parameters 15 | public string key; 16 | 17 | public ValueListWithQualifiers[] valueListsWithQualifiers; 18 | 19 | // Properties 20 | private List valueLists; // Valid value lists, already filtered and ordered 21 | 22 | // TODO: add tester interface. You can enter an ID, and it shows the selected value, and where it's coming from. Also add a simulator? 23 | // TODO: add a "comparer", so two language xmls can be compared and the missing ids can be listed. 24 | // TODO: create a better inspector? http://answers.unity3d.com/questions/26207/how-can-i-recreate-the-array-inspector-element-for.html 25 | 26 | // ================================================================================================================ 27 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 28 | 29 | void Awake() { 30 | // Load all value lists, creating a pre-filtered list 31 | valueLists = new List(); 32 | 33 | for (var i = 0; i < valueListsWithQualifiers.Length; i++) { 34 | // Filter 35 | if (doQualifiersApply(valueListsWithQualifiers[i])) { 36 | // Is valid, load 37 | var valueList = new ValueList("values_" + key + "_" + i); 38 | valueList.SetFromJSON(File.ReadAllText(valueListsWithQualifiers[i].fileName)); 39 | valueLists.Add(valueList); 40 | } 41 | 42 | } 43 | 44 | // Add to the list of instances that can be retrieved later 45 | ValueListManager.addInstance(this); 46 | 47 | Debug.Log("Value lists read; current language is " + ValueListManager.GetInstance().GetString("generic.language")); 48 | } 49 | 50 | 51 | // ================================================================================================================ 52 | // STATIC INTERFACE ----------------------------------------------------------------------------------------------- 53 | 54 | static ValueListManager() { 55 | instances = new Dictionary(); 56 | } 57 | 58 | private static void addInstance(ValueListManager instance) { 59 | if (instances.ContainsKey(instance.key)) instances.Remove(instance.key); 60 | instances.Add(instance.key, instance); 61 | } 62 | 63 | public static ValueListManager GetInstance(string key = "") { 64 | if (instances.ContainsKey(key)) return instances[key]; 65 | Debug.LogError("Error! Tried reading a ValueListManager with key [" + key + "] that doesn't exist."); 66 | return null; 67 | } 68 | 69 | 70 | // ================================================================================================================ 71 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 72 | 73 | public string GetString(string keyPath) { 74 | return GetValue(keyPath); 75 | } 76 | 77 | public T GetValue(string keyPath) { 78 | List lists = getValidValueLists(); 79 | if (lists != null) { 80 | for (int i = 0; i < lists.Count; i++) { 81 | if (lists[i].HasKey(keyPath)) return lists[i].GetValue(keyPath); 82 | } 83 | } 84 | 85 | Debug.LogWarning("Trying reading object of value [" + keyPath + "] that was not found on value lists."); 86 | return default(T); 87 | } 88 | 89 | 90 | // ================================================================================================================ 91 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 92 | 93 | private ValueList getFirstValidValueList() { 94 | for (var i = 0; i < valueListsWithQualifiers.Length; i++) { 95 | if (doQualifiersApply(valueListsWithQualifiers[i])) { 96 | return valueLists[i]; 97 | } 98 | } 99 | 100 | return null; 101 | } 102 | 103 | private List getValidValueLists() { 104 | // Create a list of valueLists that qualify given their selectors 105 | 106 | // TODO: filter by other qualifiers that are not platform- or device-specific? 107 | return valueLists; 108 | } 109 | 110 | private bool doQualifiersApply(ValueListWithQualifiers valueListWithQualifiers) { 111 | // Checks whether a value list's qualifiers are valid 112 | 113 | if (!valueListWithQualifiers.enabled) return false; 114 | 115 | bool isValid = true; 116 | 117 | // Check language 118 | if (isValid && valueListWithQualifiers.languageFilterEnabled) { 119 | isValid = valueListWithQualifiers.language == Application.systemLanguage; 120 | } 121 | 122 | // Check OS 123 | if (isValid && valueListWithQualifiers.platformFilterEnabled) { 124 | isValid = valueListWithQualifiers.platform == Application.platform; 125 | } 126 | 127 | return isValid; 128 | } 129 | 130 | } 131 | 132 | [System.Serializable] 133 | public class ValueListWithQualifiers { 134 | 135 | public string fileName; 136 | public bool enabled; 137 | 138 | public bool languageFilterEnabled; 139 | public SystemLanguage language; 140 | 141 | public bool platformFilterEnabled; 142 | public RuntimePlatform platform; 143 | 144 | /* 145 | * http://developer.android.com/guide/topics/resources/providing-resources.html 146 | * Qualifiers: 147 | * . Country (mcc, mnc) 148 | * . Language/locale (en, en-rUS) ("r" is to differentiate the region portion) 149 | * . Width/height/smallest width 150 | * . Layout direction 151 | * . Screen size (small, normal, large, xlarge) 152 | * . Aspect ratio (long, not long) 153 | * . Round or not 154 | * . Screen orientation 155 | * . UI mode (car, desktop, appliance, etc) 156 | * . Screen pixel density (ldpi, mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi, nodpi, tvdpi) 157 | * . Touchscreen type (notouch, finger) 158 | * . Keyboard availability (keysexposed, keyshidden, keyssoft) 159 | */ 160 | 161 | } -------------------------------------------------------------------------------- /logging/D.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public class D { 5 | 6 | public static void Log(object __message) { 7 | if (!Debug.isDebugBuild) return; 8 | 9 | // Builds parameters 10 | System.Diagnostics.StackTrace t = new System.Diagnostics.StackTrace(); 11 | string method = t.ToString().Split('\n')[1]; 12 | method = method.Substring(method.IndexOf("at ") + 3); 13 | 14 | string currentFrame = Time.frameCount.ToString("0000000"); 15 | string currentTime = (Time.realtimeSinceStartup * 1000).ToString("00000"); 16 | 17 | // Finally, composes line 18 | string fullMessage = "[" + currentFrame + "@" + currentTime + "] " + method + " :: " + __message; 19 | //string fullMessage = "[" + currentFrame + ":" + currentTime + " | " + method + ": " + __message; 20 | 21 | // Writes to Unity console log and to normal log 22 | Debug.Log(fullMessage + "\n"); 23 | System.Console.WriteLine(fullMessage); 24 | 25 | //Debug.Log("Flat Mesh Initialized (d)\n"); // UnityEngine.Debug.Log 26 | //System.Console.WriteLine("Flat Mesh Initialized"); 27 | // Debug.isDebugBuild 28 | // http://docs.unity3d.com/Documentation/ScriptReference/Application.RegisterLogCallback.html 29 | 30 | 31 | // Port LoggingFramework: 32 | // http://forum.unity3d.com/threads/38720-Debug.Log-and-needless-spam 33 | 34 | } 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /net/kongregate/KongregateAPI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public class KongregateAPI:MonoBehaviour { 5 | 6 | // Properties 7 | private bool _isConnected; 8 | private int _userId; 9 | private string _userName; 10 | private string _gameAuthToken; 11 | 12 | 13 | // ================================================================================================================ 14 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 15 | 16 | void Start() { 17 | _isConnected = false; 18 | _userId = 0; 19 | _userName = "Guest"; 20 | _gameAuthToken = ""; 21 | } 22 | 23 | void Awake() { 24 | // Instructs the game object to survive level changes 25 | DontDestroyOnLoad(this); 26 | 27 | // Begin the API loading process if available 28 | Application.ExternalEval( 29 | "if (typeof(kongregateUnitySupport) != 'undefined') {" + 30 | " kongregateUnitySupport.initAPI('" + gameObject.name + "', 'OnKongregateAPILoaded');" + 31 | "}" 32 | ); 33 | } 34 | 35 | 36 | // ================================================================================================================ 37 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 38 | 39 | public static KongregateAPI Create() { 40 | // Create a game object with a reference to the API 41 | GameObject newGameObject = new GameObject("KongregateAPIObject-" + (Time.realtimeSinceStartup)); 42 | KongregateAPI instance = newGameObject.AddComponent(); 43 | return instance; 44 | } 45 | 46 | public void OnKongregateAPILoaded(string __userInfoString) { 47 | // Is connected 48 | _isConnected = true; 49 | 50 | // Splits the user info parameter 51 | string[] userParams = __userInfoString.Split('|'); 52 | _userId = int.Parse(userParams[0]); 53 | _userName = userParams[1]; 54 | _gameAuthToken = userParams[2]; 55 | } 56 | 57 | public void SubmitStats(string __name, int __value) { 58 | Application.ExternalCall("kongregate.stats.submit", __name, __value); 59 | } 60 | 61 | public bool isConnected { 62 | get { return _isConnected; } 63 | } 64 | 65 | public int userId { 66 | get { return _userId; } 67 | } 68 | 69 | public string userName { 70 | get { return _userName; } 71 | } 72 | 73 | public string gameAuthToken { 74 | get { return _gameAuthToken; } 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /net/kongregate/README.md: -------------------------------------------------------------------------------- 1 | Kongregate API 2 | ============== 3 | 4 | A pure code implementation of the Kongregate statistics API for Unity as a single C# file. The statistics API is used for counting items (such as level completions, enemies killed, high score, etc) on Kongregate games. Games implementing the API also [get a bigger share of ad revenue](http://www.kongregate.com/pages/help#ad-revenue-share-q-3) from the website, where eligible. 5 | 6 | While the Kongregate API is relatively simple to implement, most of the implementation examples out there use JavaScript or require you to manually create GameObjects. This implementation minimizes the amount of work needed for setup and use. 7 | 8 | Usage 9 | ----- 10 | 11 | 1. Copy the **KongregateAPI.cs** file to your Unity project's "Scripts" folder 12 | 2. Anywhere in your code (e.g. your `Main` game class), create an instance of the KongregateAPI: 13 |
KongregateAPI kongregate = KongregateAPI.Create();
14 | 3. Whenever you need to submit statistics, do: 15 |
kongregate.SubmitStats(name, value);
16 | For example: 17 |
kongregate.SubmitStats("high-score", 1000);
18 | kongregate.SubmitStats("tanks-destroyed", 1);
19 | 20 | 21 | Full Interface 22 | -------------- 23 | 24 | * `Create()` (Static): creates a `KongregateAPI` instance and returns it (automatically creating a `GameObject` for itself so it can receive events). 25 | * `SubmitStats(string name, int value)`: submit statistics to the Kongregate API. 26 | * bool `isConnected`: returns whether the user is properly connected to the Kongregate site API. Read-only. 27 | * int `userId`: id of the current user. Read-only. 28 | * string `userName`: name of the current user. Read-only. 29 | * string `gameAuthToken`: game authorization token. Read-only. 30 | 31 | Debugging 32 | --------- 33 | 34 | In general, you don't need to do anything special for your code to work on Kongregate. Just implement the above code and you're good. 35 | 36 | Notice, however, that: 37 | 38 | * This won't work when testing your game inside Unity; the Kongregate API depends on JavaScript located on their website. 39 | * Testing on the pre-publish screen won't work either (the `kongregate` object is not instantiated). The API only works after your game is properly published. 40 | * If you want to make sure your game is working with the API, go to its published page, add `?debug_level=4` to the URL of the game, and reload the page. This makes the JavaScript console (F12 on Chrome) output what the API is receiving from your game. 41 | 42 | Caveats 43 | ------- 44 | 45 | * This implementation doesn't support [callbacks](http://developers.kongregate.com/docs/api-overview/unity-api). 46 | 47 | More information 48 | ---------------- 49 | 50 | * [Kongregate Statistics API](http://www.kongregate.com/developer_center/docs/en/statistics-api) 51 | -------------------------------------------------------------------------------- /net/newgrounds/API.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using System.Collections; 4 | using Newgrounds.Services; 5 | 6 | namespace Newgrounds { 7 | 8 | public class API:MonoBehaviour { 9 | 10 | // Properties 11 | private static string _apiId; 12 | private static string _encryptionKey; 13 | 14 | private static bool _isConnected; 15 | 16 | private static GameObjectSurrogate _surrogate; 17 | 18 | // Passed user data 19 | private static string _connectionUserName; 20 | private static string _connectionUserId; 21 | private static string _connectionPublisherId; 22 | private static string _connectionUserpageFormat; 23 | private static string _connectionSessionId; 24 | private static string _connectionSaveGroupId; 25 | private static string _connectionSaveFileId; 26 | 27 | 28 | // ================================================================================================================ 29 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 30 | 31 | public static void Connect(string __apiId, string __encryptionKey = null, string __version = "") { 32 | _apiId = __apiId; 33 | _encryptionKey = __encryptionKey; 34 | 35 | // This is an odd combination: the static class creates a GameObject that has an instance of the class. 36 | // The instance just forwards everything to the static class. 37 | GameObject newGameObject = new GameObject("NewgroundsAPISurrogate-" + (int)(Time.realtimeSinceStartup * 1000f)); 38 | _surrogate = newGameObject.AddComponent(); 39 | 40 | Debug.Log("Attempting to connect to newgrounds API"); 41 | 42 | if (Application.isEditor) { 43 | // Running in editor: use fake data 44 | // REPLACE THIS WITH THE URL OF YOUR GAME WHEN TESTING! It needs the session id! 45 | setContainerURLStatic("http://uploads.ungrounded.net/alternate/999999/999999_alternate_9999.zip/?NewgroundsAPI_PublisherID=9&NewgroundsAPI_SandboxID=Abc999&NewgroundsAPI_SessionID=Abc999&NewgroundsAPI_UserName=john&NewgroundsAPI_UserID=999999&ng_username=john"); 46 | } else { 47 | // Need the container URL first (for user parameters) 48 | Application.ExternalEval( 49 | "document.getElementById('unityPlayer').children[0].SendMessage('" + newGameObject.name + "', 'setContainerURL', document.location.toString());" 50 | ); 51 | } 52 | } 53 | 54 | internal static void setContainerURLStatic(string __url) { 55 | // Now that the container is known, continue connecting 56 | // Example URL passed: http://uploads.ungrounded.net/alternate/999999/999999_alternate_9999.zip/?NewgroundsAPI_PublisherID=9&NewgroundsAPI_SandboxID=Abc999&NewgroundsAPI_SessionID=Abc999&NewgroundsAPI_UserName=john&NewgroundsAPI_UserID=999999&ng_username=john 57 | 58 | Debug.Log("Container URL is " + __url); 59 | 60 | // Dirty querystring parsing (no access to C#'s System.Web) 61 | string[] pairs = __url.Substring(__url.IndexOf("?") + 1).Split('&'); 62 | string[] pair; 63 | for (int i = 0; i < pairs.Length; i++) { 64 | pair = pairs[i].Split('='); 65 | switch (pair[0]) { 66 | case "NewgroundsAPI_UserName": 67 | _connectionUserName = pair[1]; 68 | break; 69 | case "NewgroundsAPI_UserID": 70 | _connectionUserId = pair[1]; 71 | break; 72 | case "NewgroundsAPI_PublisherID": 73 | _connectionPublisherId = pair[1]; 74 | break; 75 | case "NewgroundsAPI_UserpageFormat": 76 | _connectionUserpageFormat = pair[1]; 77 | break; 78 | case "NewgroundsAPI_SessionID": 79 | _connectionSessionId = pair[1]; 80 | break; 81 | case "NewgroundsAPI_SaveGroupID": 82 | _connectionSaveGroupId = pair[1]; 83 | break; 84 | case "NewgroundsAPI_SaveFileID": 85 | _connectionSaveFileId = pair[1]; 86 | break; 87 | } 88 | } 89 | 90 | Debug.Log("connectionUserName => " + _connectionUserName); // 'zehfernando' 91 | Debug.Log("connectionUserId => " + _connectionUserId); // int 92 | Debug.Log("connectionPublisherId => " + _connectionPublisherId); // 1 93 | Debug.Log("connectionUserpageFormat => " + _connectionUserpageFormat); // Empty 94 | Debug.Log("connectionSessionId => " + _connectionSessionId); // Long hash 95 | Debug.Log("connectionSaveGroupId => " + _connectionSaveGroupId); // Empty 96 | Debug.Log("connectionSaveFileId => " + _connectionSaveFileId); // Empty 97 | 98 | _isConnected = true; 99 | } 100 | 101 | public static void UnlockMedal(string __medalName) { 102 | 103 | } 104 | 105 | public static void PostScore(string __scoreBoardName, int __numericScore, string __tag = null) { 106 | PostScoreService service = new PostScoreService(eResult); 107 | service.setData(__scoreBoardName, __numericScore, __tag); 108 | service.execute(); 109 | } 110 | 111 | private static void eResult(BasicService __service = null) { 112 | if (__service.resultSuccess) { 113 | Debug.Log("==============> success posting " + __service); 114 | } else { 115 | Debug.Log("==============> error posting: " + __service.resultErrorMessage); 116 | } 117 | } 118 | 119 | 120 | // ================================================================================================================ 121 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 122 | 123 | public static string apiId { 124 | get { return _apiId; } 125 | } 126 | 127 | public static string encryptionKey { 128 | get { return _encryptionKey; } 129 | } 130 | 131 | public static bool isConnected { 132 | get { return _isConnected; } 133 | } 134 | 135 | public static GameObjectSurrogate surrogate { 136 | get { return _surrogate; } 137 | } 138 | 139 | public static string connectionUserName { 140 | get { return _connectionUserName; } 141 | } 142 | 143 | public static string connectionUserId { 144 | get { return _connectionUserId; } 145 | } 146 | 147 | public static string connectionPublisherId { 148 | get { return _connectionPublisherId; } 149 | } 150 | 151 | public static string connectionUserpageFormat { 152 | get { return _connectionUserpageFormat; } 153 | } 154 | 155 | public static string connectionSessionId { 156 | get { return _connectionSessionId; } 157 | } 158 | 159 | public static string connectionSaveGroupId { 160 | get { return _connectionSaveGroupId; } 161 | } 162 | 163 | public static string connectionSaveFileId { 164 | get { return _connectionSaveFileId; } 165 | } 166 | 167 | /* 168 | Return Object 169 | command_id = getMedals 170 | medals - An array of medal objects (if the game uses them) with the following properties: 171 | medal_id - The numeric id of the medal 172 | medal_name - The name of the medal 173 | medal_value - The point value of the medal 174 | medal_difficulty - 1=easy, 2=moderate, 3=challenging, 4=difficult, 5=brutal 175 | medal_unlocked - true/false (if publisher_id and user_id were passed) 176 | medal_icon - The URL of the icon for this medal 177 | secret - 1 if this is a secret medal 178 | medal_description - The description of this medal 179 | success = 1 180 | {"command_id":"getMedals","medals": 181 | [ 182 | {"medal_id":-32827,"medal_name":"Completed Level 1 (Ambush)","medal_value":0,"medal_difficulty":1,"medal_unlocked":false,"medal_icon":"http:\/\/apifiles.ngfiles.com\/medals\/36000\/36957\/32827_completedlevel1ambush.jpg","secret":0,"medal_description":"Completed Level 1 (Ambush)"} 183 | ],"success":1} 184 | 185 | UnityEngine.Debug:Log(Object) 186 | Debug.Log(Object) (at Assets/Scripts/Libraries/logging/D.cs:25) 187 | c__Iterator9:MoveNext() (at Assets/Scripts/Libraries/net/newgrounds/NewgroundsAPI.cs:186) 188 | 189 | */ 190 | } 191 | } -------------------------------------------------------------------------------- /net/newgrounds/GameObjectSurrogate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using System.Collections; 4 | using SimpleJSON; 5 | 6 | namespace Newgrounds { 7 | 8 | public class GameObjectSurrogate:MonoBehaviour { 9 | 10 | // A script to serve as a GameObject surrogate/proxy 11 | 12 | // ================================================================================================================ 13 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 14 | 15 | void Awake() { 16 | // Actual initialization 17 | // Instructs the game object to survive level changes 18 | DontDestroyOnLoad(this); 19 | } 20 | 21 | public void doPost(WWW __wwww, Action __doneCallback) { 22 | StartCoroutine(doPostWithYield(__wwww, __doneCallback)); 23 | } 24 | 25 | public void setContainerURL(string __url) { 26 | // Receives the current URL with user data and passes everything to the static class 27 | // This is a private method, but JavaScript don't care; it is still called by SendMessage() 28 | API.setContainerURLStatic(__url); 29 | } 30 | 31 | 32 | // ================================================================================================================ 33 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 34 | 35 | private IEnumerator doPostWithYield(WWW _www, Action __doneCallback) { 36 | yield return _www; 37 | 38 | Debug.Log("Service sent; response: " + _www.text); 39 | 40 | //JSONNode medals = JSON.Parse(_w.text); 41 | 42 | //Debug.Log("There are " + medals["medals"].Count + " medals loaded"); 43 | //Debug.Log("The name of the first medal is: [" + medals["medals"][0]["medal_name"] + "]"); 44 | 45 | if (__doneCallback != null) __doneCallback(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /net/newgrounds/README.md: -------------------------------------------------------------------------------- 1 | Newgrounds API 2 | ============== 3 | 4 | An *initial* implementation of the Newgrounds RESTful API in C# for Unity with no JavaScript bridges. It tries to mimic the syntax of the [AS3 version of the API](http://www.newgrounds.com/wiki/creator-resources/flash-api/connecting-to-the-api), although only a handful of methods are available. 5 | 6 | This is currently used in my [Escape Drill](http://www.newgrounds.com/portal/view/638876) game for Ludum Dare #29, but missing a lot of features. 7 | 8 | Usage 9 | ----- 10 | 11 | 1. Copy the contents of the `newgrounds` folder to your Unity project's "Scripts" folder 12 | 2. Anywhere in your code (e.g. your `Main` game class), connect to the API: 13 |
Newgrounds.API.Connect(apiId, encryptionKey);
14 | 3. Whenever you need to submit statistics, do: 15 |
Newgrounds.API.PostScore(name, value);
16 | For example: 17 |
Newgrounds.API.PostScore("High Score (level 1)", 1000);
18 | Newgrounds.API.PostScore("Tanks Destroyed", 10);
19 | 20 | 21 | Full Interface 22 | -------------- 23 | 24 | * `Newgrounds.API.Connect(string apiId, string encriptionKey)` (Static): Connects to the API. You need to do this before anything is posted. You can find the API id and encryption key values in the [API Tools section of your project](http://www.newgrounds.com/projects). 25 | * `Newgrounds.API.PostScore(string name, int value)` (Static): submit a score to the Newgrounds API. The `name` is not an id, but rather how the score is represented in the website. 26 | * string `connectionUserName`: current user name. Read-only. 27 | * int `connectionUserId`: id of the current user. Read-only. 28 | * string `connectionSessionId`: a long hash with the id of the current session. Read-only. 29 | 30 | Debugging 31 | --------- 32 | 33 | The API will not work inside the editor by default. This is because it needs some kind of session ID that is passed by the website when the game is ran. However, the API does detect when you're running inside the editor and attempt to use a placeholder URL. To replace this placeholder URL with a real URL, go to your game's page, find the URL of the iframe used to host it, and replace the `setContainerURLStatic()` call in line 45 of API.cs with this url. It will look like this: 34 | 35 | http://uploads.ungrounded.net/alternate/999999/999999_alternate_9999.zip/?NewgroundsAPI_PublisherID=9&NewgroundsAPI_SandboxID=Abc999&NewgroundsAPI_SessionID=Abc999&NewgroundsAPI_UserName=john&NewgroundsAPI_UserID=999999&ng_username=john 36 | 37 | Caveats 38 | ------- 39 | 40 | * A lot is missing. Actually it only posts high scores right now. 41 | 42 | More information 43 | ---------------- 44 | 45 | * [Newgrounds gateway API](http://www.newgrounds.com/wiki/creator-resources/newgrounds-apis/developer-gateway) 46 | * [Newgrounds AS3 API](http://www.newgrounds.com/wiki/creator-resources/flash-api/connecting-to-the-api) 47 | -------------------------------------------------------------------------------- /net/newgrounds/data/Medal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public class Medal { 5 | 6 | //public static const ICON_WIDTH:uint = 50; 7 | //public static const ICON_HEIGHT:uint = 50; 8 | public const string DIFFICULTY_EASY = "Easy"; 9 | public const string DIFFICULTY_MODERATE = "Moderate"; 10 | public const string DIFFICULTY_CHALLENGING = "Challenging"; 11 | public const string DIFFICULTY_DIFFICULT = "Difficult"; 12 | public const string DIFFICULTY_BRUTAL = "Brutal"; 13 | private static string[] DIFFICULTIES = new string[] {DIFFICULTY_EASY, DIFFICULTY_MODERATE, DIFFICULTY_CHALLENGING, DIFFICULTY_DIFFICULT, DIFFICULTY_BRUTAL}; 14 | //public static const DEFAULT_ICON:BitmapData = new DefaultMedalIcon(ICON_WIDTH, ICON_HEIGHT); 15 | 16 | 17 | // ================================================================================================================ 18 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 19 | 20 | public Medal(Action __successCallback) { 21 | } 22 | 23 | // ================================================================================================================ 24 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 25 | 26 | // ================================================================================================================ 27 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 28 | 29 | } -------------------------------------------------------------------------------- /net/newgrounds/services/BasicService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using SimpleJSON; 4 | using System.Collections; 5 | 6 | namespace Newgrounds.Services { 7 | 8 | public class BasicService { 9 | 10 | // Types 11 | public delegate void ServiceCallback(BasicService __service); 12 | 13 | // Constants 14 | private const string API_URL = "http://www.ngads.com/gateway_v2.php"; 15 | private const string RADIX_SET = "/g8236klvBQ#&|;Zb*7CEA59%s`Oue1wziFp$rDVY@TKxUPWytSaGHJ>dmoMR^<0~4qNLhc(I+fjn)X"; 16 | 17 | // Instances 18 | private ServiceCallback callback; 19 | 20 | private WWWForm formData; 21 | private WWW serviceRequest; 22 | 23 | // Request data 24 | protected string commandId; 25 | protected bool isSecure; 26 | 27 | // Result data 28 | private bool _resultSuccess; 29 | private string _resultErrorMessage; 30 | private int _resultErrorCode; 31 | 32 | // ================================================================================================================ 33 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 34 | 35 | public BasicService(ServiceCallback __callback = null) { 36 | callback = __callback; 37 | 38 | setDefaultData(); 39 | } 40 | 41 | 42 | // ================================================================================================================ 43 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 44 | 45 | public void execute() { 46 | // Creates the service 47 | 48 | Debug.Log("executing service"); 49 | 50 | IDictionaryEnumerator myEnumerator; 51 | 52 | formData = new WWWForm(); 53 | 54 | Hashtable formDataHash = new Hashtable(); 55 | 56 | // The command 57 | if (isSecure) { 58 | string seed = StringUtils.getRandomAlphanumericString(13); 59 | string seedMD5 = StringUtils.MD5(seed); 60 | 61 | // Everything in an ecrypted field 62 | Hashtable formDataSecureHash = new Hashtable(); 63 | 64 | // Generate the data first 65 | addBasicFields(formDataSecureHash); 66 | formDataSecureHash.Add("seed", seed); 67 | 68 | JSONObject j = new JSONObject(JSONObject.Type.OBJECT); 69 | myEnumerator = formDataSecureHash.GetEnumerator(); 70 | while (myEnumerator.MoveNext()) { 71 | if (myEnumerator.Value is int) { 72 | j.AddField((string)myEnumerator.Key, (int)myEnumerator.Value); 73 | // Debug.Log(" SECURE adding int => " + myEnumerator.Key + " = " + myEnumerator.Value); 74 | } else { 75 | j.AddField((string)myEnumerator.Key, (string)myEnumerator.Value); 76 | // Debug.Log(" SECURE adding string => " + myEnumerator.Key + " = " + myEnumerator.Value); 77 | } 78 | } 79 | 80 | string secureParameter = j.Print(); 81 | string encryptionKey = API.encryptionKey; 82 | 83 | // For some reason, the request never works the string length is a multiple of 6 (when the seed has a length of 1 or 13 or ...) 84 | // The documentation is conflicting in this regard and it's hard to know the cause (probably number padding and alignment) 85 | // So just add a space in that case 86 | // Sizes that don't work: 174, 180 87 | // Sizes that work: 175, 176, 177, 178 88 | if (secureParameter.Length % 3 == 0) secureParameter += " "; 89 | 90 | Debug.Log("Secure param => (" + secureParameter.Length + ") " + secureParameter); 91 | 92 | // Encode all data with RC4 93 | string secureParameterHex = seedMD5 + StringUtils.convertBytesToString(StringUtils.encodeRC4(secureParameter, encryptionKey)); 94 | 95 | // Testingh 96 | 97 | // Convert to baseN 98 | int pos = 0; 99 | string secureParameterBaseN = ""; 100 | string hexString; 101 | int hexNumber; 102 | while (pos < secureParameterHex.Length) { 103 | hexString = secureParameterHex.Substring(pos, Math.Min(pos + 6, secureParameterHex.Length)-pos); 104 | hexNumber = Convert.ToInt32(hexString, 16); 105 | secureParameterBaseN += StringUtils.convertIntToCustomBase(hexNumber, RADIX_SET, 4); 106 | //secureParameterBaseN += StringUtils.convertIntToCustomBase(hexNumber, RADIX_SET, hexString.Length / 3 * 2); 107 | pos += 6; 108 | } 109 | 110 | // Add tail length 111 | secureParameterBaseN = (secureParameterBaseN.Length % 6) + secureParameterBaseN; 112 | 113 | // Add real fields 114 | formDataHash.Add("secure", secureParameterBaseN); 115 | formDataHash.Add("command_id", "securePacket"); 116 | } else { 117 | // Everything in main request 118 | addBasicFields(formDataHash); 119 | } 120 | 121 | // The tracker ID of your game 122 | formDataHash.Add("tracker_id", API.apiId); 123 | 124 | // Generate form data 125 | myEnumerator = formDataHash.GetEnumerator(); 126 | while (myEnumerator.MoveNext()) { 127 | if (myEnumerator.Value is int) { 128 | formData.AddField((string)myEnumerator.Key, (int)myEnumerator.Value); 129 | // Debug.Log(" adding int => " + myEnumerator.Key + " = " + myEnumerator.Value); 130 | } else { 131 | formData.AddField((string)myEnumerator.Key, (string)myEnumerator.Value); 132 | // Debug.Log(" adding string => " + myEnumerator.Key + " = " + myEnumerator.Value); 133 | } 134 | } 135 | 136 | // Send 137 | serviceRequest = new WWW(API_URL, formData); 138 | 139 | API.surrogate.doPost(serviceRequest, onDone); 140 | } 141 | 142 | 143 | // ================================================================================================================ 144 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 145 | 146 | public bool resultSuccess { 147 | get { return _resultSuccess; } 148 | } 149 | 150 | public string resultErrorMessage { 151 | get { return _resultErrorMessage; } 152 | } 153 | 154 | public int resultErrorCode { 155 | get { return _resultErrorCode; } 156 | } 157 | 158 | 159 | // ================================================================================================================ 160 | // EXTENDED INTERFACE --------------------------------------------------------------------------------------------- 161 | 162 | protected virtual void setDefaultData() { 163 | // Extend 164 | 165 | commandId = ""; 166 | isSecure = false; 167 | } 168 | 169 | protected virtual void addCustomFields(Hashtable __hash) { 170 | // Extend 171 | } 172 | 173 | protected virtual void parseSuccessResult(JSONNode __result) { 174 | // Extend 175 | } 176 | 177 | 178 | // ================================================================================================================ 179 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 180 | 181 | private void addBasicFields(Hashtable __hash) { 182 | __hash.Add("command_id", commandId); 183 | 184 | if (API.connectionPublisherId != null) __hash.Add("publisher_id", API.connectionPublisherId); 185 | if (API.connectionSessionId != null) __hash.Add("session_id", API.connectionSessionId); 186 | //if (API.connectionUserId != null) __hash.AddField("user_id", API.connectionSessionId); 187 | 188 | addCustomFields(__hash); 189 | } 190 | 191 | private void onDone() { 192 | JSONNode result = JSON.Parse(serviceRequest.text); 193 | // {"error_msg":"You must be logged in or provide an e-mail address to use postScore().","error_code":7,"success":0,"command_id":"postScore"} 194 | 195 | if (result["success"].AsInt == 1) { 196 | // Success! 197 | _resultSuccess = true; 198 | 199 | parseSuccessResult(result); 200 | } else { 201 | // Error! 202 | _resultSuccess = false; 203 | _resultErrorMessage = result["error_msg"]; 204 | _resultErrorCode = result["error_code"].AsInt; 205 | } 206 | 207 | if (callback != null) callback(this); 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /net/newgrounds/services/PostScoreService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using System.Collections; 4 | using SimpleJSON; 5 | 6 | namespace Newgrounds.Services { 7 | 8 | public class PostScoreService:BasicService { 9 | 10 | // Data 11 | private string scoreBoardName; 12 | private int numericScore; 13 | private string tag; 14 | 15 | 16 | // ================================================================================================================ 17 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 18 | 19 | public PostScoreService(ServiceCallback __callback = null) : base(__callback) { 20 | 21 | } 22 | 23 | 24 | // ================================================================================================================ 25 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 26 | 27 | public void setData(string __scoreBoardName, int __numericScore, string __tag = null) { 28 | scoreBoardName = __scoreBoardName; 29 | numericScore = __numericScore; 30 | tag = __tag; 31 | } 32 | 33 | 34 | // ================================================================================================================ 35 | // EXTENDED INTERFACE --------------------------------------------------------------------------------------------- 36 | 37 | protected override void setDefaultData() { 38 | // Extend 39 | commandId = "postScore"; 40 | isSecure = true; 41 | } 42 | 43 | protected override void addCustomFields(Hashtable __hash) { 44 | __hash.Add("board", scoreBoardName); 45 | __hash.Add("value", numericScore); 46 | if (tag != null) __hash.Add("tag", tag); 47 | } 48 | 49 | protected override void parseSuccessResult(JSONNode __result) { 50 | // Extend 51 | 52 | // ==> {"command_id":"postScore","board":-4970,"tag":"","value":100,"success":1} 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /net/newgrounds/services/UnlockMedalService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Newgrounds.Services { 5 | 6 | public class UnlockMedalService:BasicService { 7 | 8 | // ================================================================================================================ 9 | // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- 10 | 11 | public UnlockMedalService(ServiceCallback __callback = null) : base(__callback) { 12 | 13 | } 14 | 15 | // ================================================================================================================ 16 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 17 | 18 | // ================================================================================================================ 19 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 20 | 21 | /* 22 | protected override WWW createService() { 23 | WWWForm form = new WWWForm(); 24 | 25 | // The command 26 | form.AddField("command_id", "getMedals"); 27 | 28 | // The tracker ID of your game 29 | form.AddField("tracker_id", apiId); 30 | 31 | // The ID of the publisher hosting the game (optional, used to mark unlocked medals) 32 | if (connectionPublisherId != null) form.AddField("publisher_id", connectionPublisherId); 33 | 34 | // The user ID of the current player (optional, used to mark unlocked medals) 35 | if (connectionUserId != null) form.AddField("user_id", connectionUserId); 36 | 37 | // Send 38 | WWW w = new WWW(API_URL, form); 39 | 40 | return w; 41 | } 42 | * */ 43 | 44 | /* 45 | Posts a high score to a score board. This command is sent as a secure packet (see securePacket command) 46 | 47 | POST Values 48 | command_id = securePacket 49 | tracker_id - The tracker ID of your game 50 | secure - An encrypted object (see Encryption Process) containing the following properties: 51 | command_id = unlockMedal 52 | publisher_id - The ID of the publisher hosting the game 53 | session_id - The current player session ID 54 | medal_id - The ID of the medal 55 | 56 | Return Object 57 | command_id = unlockMedal 58 | medal_id - The ID of the medal 59 | medal_name - The name of the medal 60 | medal_value - The point value of the medal 61 | medal_difficulty - 1=Easy, 2=Moderate, 3=Challenging, 4=Difficult, 5=Brutal 62 | success = 1 63 | **/ 64 | } 65 | } 66 | /* 67 | * GetMedals(() => medalsDone()); 68 | 69 | public static void GetMedals(Action __callback) { 70 | // http://docs.unity3d.com/Documentation/ScriptReference/WWWForm.html 71 | 72 | WWWForm form = new WWWForm(); 73 | 74 | // The command 75 | form.AddField("command_id", "getMedals"); 76 | 77 | // The tracker ID of your game 78 | form.AddField("tracker_id", _apiId); 79 | 80 | // The ID of the publisher hosting the game (optional, used to mark unlocked medals) 81 | if (_connectionPublisherId != null) form.AddField("publisher_id", _connectionPublisherId); 82 | 83 | // The user ID of the current player (optional, used to mark unlocked medals) 84 | if (_connectionUserId != null) form.AddField("user_id", _connectionUserId); 85 | 86 | // Send 87 | WWW w = new WWW(API_URL, form); 88 | 89 | Debug.Log("getting medals from " + API_URL + ", form = " + form); 90 | _surrogate.doPost(w, __callback); 91 | }*/ -------------------------------------------------------------------------------- /objects/MeshColliderCreator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | #if UNITY_EDITOR 5 | [ExecuteInEditMode()] 6 | #endif 7 | 8 | public class MeshColliderCreator : MonoBehaviour { 9 | 10 | // Automatically add mesh colliders to all child meshes 11 | 12 | // TODO: actually remove this file 13 | 14 | // Properties 15 | public bool actOnChildren; 16 | 17 | // ================================================================================================================ 18 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 19 | 20 | #if UNITY_EDITOR 21 | void Start () { 22 | createColliders(); 23 | } 24 | 25 | void Update() { 26 | if (!Application.isPlaying) createColliders(); 27 | } 28 | #endif 29 | 30 | // ================================================================================================================ 31 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 32 | 33 | private void createColliders() { 34 | GameObjectUtils.createMeshColliders(gameObject, actOnChildren); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /objects/ModelResizer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | #if UNITY_EDITOR 5 | [ExecuteInEditMode()] 6 | #endif 7 | 8 | public class ModelResizer:MonoBehaviour { 9 | 10 | // Resizes a model to a desired maximum size 11 | // TODO: Actually get rid of this file. 12 | 13 | // Properties 14 | public float desiredHeight; 15 | public float desiredWidth; 16 | public float desiredDepth; 17 | 18 | // ================================================================================================================ 19 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 20 | 21 | #if UNITY_EDITOR 22 | void Start () { 23 | resize(); 24 | } 25 | 26 | void Update() { 27 | if (!Application.isPlaying) resize(); 28 | } 29 | #endif 30 | 31 | // ================================================================================================================ 32 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 33 | 34 | private void resize() { 35 | foreach (Transform child in transform) { 36 | // Resize 37 | GameObjectUtils.resizeToFit(child.gameObject, desiredWidth, desiredHeight, desiredDepth); 38 | // Center in the bottom 39 | GameObjectUtils.alignToVector(child.gameObject, transform.position, 0.5f, 0, 0.5f); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /objects/SimpleButton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | public class SimpleButton:MonoBehaviour { 7 | 8 | /* 9 | A button with simple actions; syntax sugar 10 | */ 11 | 12 | // Properties 13 | private bool _isPointerOver; 14 | private bool _isPressed; 15 | 16 | // ================================================================================================================ 17 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 18 | 19 | // Gives warnings on Android: 20 | // "Game scripts or other custom code contains OnMouse_ event handlers. Presence of such handlers might impact performance on handheld devices." 21 | 22 | // Use separate events? 23 | // http://wiki.unity3d.com/index.php/OnMouseDown 24 | 25 | void OnMouseDown() { 26 | // Called when the user has pressed the mouse button while over the GUIElement or Collider. 27 | _isPressed = true; 28 | animatePress(); 29 | } 30 | 31 | void OnMouseDrag() { 32 | // Called every frame when the user has clicked on a GUIElement or Collider and is still holding down the mouse (inside or not) 33 | } 34 | 35 | void OnMouseEnter() { 36 | // Called when the mouse entered the GUIElement or Collider. 37 | _isPointerOver = true; 38 | if (_isPressed) animatePress(); 39 | animateOver(); 40 | } 41 | 42 | void OnMouseExit() { 43 | // Called when the mouse is not any longer over the GUIElement or Collider. 44 | _isPointerOver = false; 45 | if (_isPressed) animateRelease(); 46 | animateOut(); 47 | } 48 | 49 | void OnMouseOver() { 50 | // Called every frame while the mouse is over the GUIElement or Collider. 51 | } 52 | 53 | void OnMouseUpAsButton() { 54 | // Only called when the mouse is released over the same GUIElement or Collider as it was pressed. This is called BEFORE OnMouseUp 55 | if (_isPressed) { 56 | performAction(); 57 | animateRelease(); 58 | } 59 | } 60 | 61 | void OnMouseUp() { 62 | // Called when the user has released the mouse button 63 | if (_isPressed) { 64 | _isPressed = false; 65 | } 66 | } 67 | 68 | 69 | // ================================================================================================================ 70 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 71 | 72 | // ================================================================================================================ 73 | // ACCESSOR INTERFACE --------------------------------------------------------------------------------------------- 74 | 75 | protected bool isPointerOver { 76 | get { 77 | return _isPointerOver; 78 | } 79 | } 80 | 81 | protected bool isPressed { 82 | get { 83 | return _isPressed; 84 | } 85 | } 86 | 87 | 88 | // ================================================================================================================ 89 | // EXTENDABLE INTERFACE ------------------------------------------------------------------------------------------- 90 | 91 | protected virtual void animatePress() { 92 | // Press 93 | } 94 | 95 | protected virtual void animateRelease() { 96 | // Release 97 | } 98 | 99 | protected virtual void animateOver() { 100 | // Pointer over 101 | } 102 | 103 | protected virtual void animateOut() { 104 | // Pointer out 105 | } 106 | 107 | protected virtual void performAction() { 108 | // Pressed and released without moving: execute action 109 | } 110 | 111 | 112 | // ================================================================================================================ 113 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 114 | 115 | } -------------------------------------------------------------------------------- /objects/TextMeshSharpener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | #if UNITY_EDITOR 5 | [ExecuteInEditMode()] 6 | #endif 7 | 8 | public class TextMeshSharpener:MonoBehaviour { 9 | 10 | /* 11 | Makes TextMesh look sharp regardless of camera size/resolution 12 | Do NOT change character size or font size; use scale only 13 | */ 14 | 15 | // Properties 16 | private float lastPixelHeight = -1; 17 | private TextMesh textMesh; 18 | 19 | 20 | // ================================================================================================================ 21 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 22 | 23 | void Awake() { 24 | } 25 | 26 | void Start() { 27 | textMesh = GetComponent(); 28 | resize(); 29 | } 30 | 31 | void Update() { 32 | if (Camera.main.pixelHeight != lastPixelHeight || (Application.isEditor && !Application.isPlaying)) resize(); 33 | } 34 | 35 | // ================================================================================================================ 36 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 37 | 38 | private void resize() { 39 | float ph = Camera.main.pixelHeight; 40 | float ch = Camera.main.orthographicSize; 41 | 42 | /* 43 | //Constant size: 44 | float pixelRatio = (ch * 2.0f) / ph; 45 | textMesh.characterSize = 1; 46 | transform.localScale = new Vector3(pixelRatio * 10.0f, pixelRatio * 10.0f, pixelRatio * 0.1f); 47 | */ 48 | 49 | float pixelRatio = (ch * 2.0f) / ph; 50 | float targetRes = 128f; 51 | 52 | textMesh.characterSize = pixelRatio * Camera.main.orthographicSize / Math.Max(transform.localScale.x, transform.localScale.y); 53 | textMesh.fontSize = (int)Math.Round(targetRes / textMesh.characterSize); 54 | 55 | lastPixelHeight = ph; 56 | } 57 | 58 | 59 | } -------------------------------------------------------------------------------- /objects/layout/AutoAligner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | #if UNITY_EDITOR 5 | [ExecuteInEditMode()] 6 | #endif 7 | 8 | public class AutoAligner:AutoLayoutElement { 9 | 10 | // Enums 11 | private enum Alignments:int { 12 | NONE = 0, 13 | ANCHOR, 14 | START, 15 | MIDDLE, 16 | END, 17 | } 18 | 19 | private enum Axis { 20 | X, 21 | Y, 22 | Z 23 | } 24 | 25 | public enum AlignmentsX:int { 26 | NONE = 0, 27 | ANCHOR, 28 | LEFT, 29 | CENTER, 30 | RIGHT, 31 | } 32 | 33 | public enum AlignmentsY:int { 34 | NONE = 0, 35 | ANCHOR, 36 | BOTTOM, 37 | MIDDLE, 38 | TOP, 39 | } 40 | 41 | public enum AlignmentsZ:int { 42 | NONE = 0, 43 | ANCHOR, 44 | FRONT, 45 | CENTER, 46 | BACK, 47 | } 48 | 49 | public GameObject parentX; 50 | public AlignmentsX anchorX; 51 | public AlignmentsX parentAnchorX; 52 | public float marginX; 53 | 54 | public GameObject parentY; 55 | public AlignmentsY anchorY; 56 | public AlignmentsY parentAnchorY; 57 | public float marginY; 58 | 59 | public GameObject parentZ; 60 | public AlignmentsZ anchorZ; 61 | public AlignmentsZ parentAnchorZ; 62 | public float marginZ; 63 | 64 | 65 | // ================================================================================================================ 66 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 67 | 68 | void Awake() { 69 | OnShouldApplyLayout += checkAllParents; 70 | OnShouldApplyLayout += applyLayout; 71 | } 72 | 73 | 74 | // ================================================================================================================ 75 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 76 | 77 | private void checkAllParents() { 78 | // Check if the parents were changed 79 | checkParents(parentX, parentY, parentZ); 80 | } 81 | 82 | private void applyLayout() { 83 | // Applies the alignment position to this object 84 | 85 | // Find own bounding box 86 | var bounds = getBoundsOrPoint(gameObject); 87 | 88 | // Find actual mapping positions 89 | var newX = findAlignment((int)anchorX, (int)parentAnchorX, gameObject.transform.position.x, bounds.min.x, bounds.max.x, parentX, Axis.X, marginX); 90 | var newY = findAlignment((int)anchorY, (int)parentAnchorY, gameObject.transform.position.y, bounds.min.y, bounds.max.y, parentY, Axis.Y, marginY); 91 | var newZ = findAlignment((int)anchorZ, (int)parentAnchorZ, gameObject.transform.position.z, bounds.min.z, bounds.max.z, parentZ, Axis.Z, marginZ); 92 | 93 | // Set positions 94 | gameObject.transform.position = new Vector3(newX, newY, newZ); 95 | 96 | } 97 | 98 | private float findAlignment(int anchor, int parentAnchor, float currentPos, float minPos, float maxPos, GameObject parent, Axis axis, float margin) { 99 | 100 | // Check if it's valid 101 | if (parent != null && (Alignments)anchor != Alignments.NONE && (Alignments)parentAnchor != Alignments.NONE) { 102 | 103 | if (parent.GetComponent() == null && parent.GetComponent() == null) { 104 | // Invalid parent 105 | Debug.LogWarning("Error: GameObject must have a renderer or a collider attached to work as an Aligner parent"); 106 | } else { 107 | // Valid parent 108 | var parentBounds = parent.GetComponent() == null ? parent.GetComponent().bounds : parent.GetComponent().bounds; 109 | 110 | // Find boundaries 111 | float parentPos = 0; 112 | float parentMinPos = 0; 113 | float parentMaxPos = 0; 114 | 115 | switch (axis) { 116 | case Axis.X: 117 | parentPos = parent.transform.position.x; 118 | parentMinPos = parentBounds.min.x; 119 | parentMaxPos = parentBounds.max.x; 120 | break; 121 | case Axis.Y: 122 | parentPos = parent.transform.position.y; 123 | parentMinPos = parentBounds.min.y; 124 | parentMaxPos = parentBounds.max.y; 125 | break; 126 | case Axis.Z: 127 | parentPos = parent.transform.position.z; 128 | parentMinPos = parentBounds.min.z; 129 | parentMaxPos = parentBounds.max.z; 130 | break; 131 | 132 | } 133 | 134 | // Find base 135 | float basePos = 0; 136 | switch ((Alignments)parentAnchor) { 137 | case Alignments.ANCHOR: 138 | basePos = parentPos; 139 | break; 140 | case Alignments.START: 141 | basePos = parentMinPos; 142 | break; 143 | case Alignments.MIDDLE: 144 | basePos = (parentMinPos + parentMaxPos) / 2; 145 | break; 146 | case Alignments.END: 147 | basePos = parentMaxPos; 148 | break; 149 | } 150 | 151 | // Applies 152 | switch ((Alignments)anchor) { 153 | case Alignments.ANCHOR: 154 | return basePos + margin; 155 | case Alignments.START: 156 | return basePos + margin + (currentPos - minPos); 157 | case Alignments.MIDDLE: 158 | return basePos + margin + (currentPos - minPos - (maxPos - minPos) / 2); 159 | case Alignments.END: 160 | return basePos - margin + (currentPos - maxPos); 161 | } 162 | } 163 | } 164 | 165 | return currentPos; 166 | } 167 | 168 | } -------------------------------------------------------------------------------- /objects/layout/AutoLayoutElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using System.Collections.Generic; 4 | 5 | #if UNITY_EDITOR 6 | [ExecuteInEditMode()] 7 | #endif 8 | 9 | public class AutoLayoutElement:MonoBehaviour { 10 | 11 | // Delegates 12 | protected delegate void SimpleAction(); 13 | 14 | // Events 15 | protected event SimpleAction OnShouldApplyLayout; 16 | 17 | // Properties 18 | private List dependents = new List(); 19 | private List parents = new List(); 20 | 21 | private float lastCameraHeight; 22 | private float lastCameraWidth; 23 | 24 | protected bool shouldAutoUpdate; // If true, this instance should self-update when a change was detected (otherwise, it only updates when told so by parents) 25 | 26 | private bool needsAdditionalUpdate; 27 | 28 | 29 | // ================================================================================================================ 30 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 31 | 32 | void Awake() { 33 | } 34 | 35 | void Start() { 36 | applyLayoutRules(); 37 | } 38 | 39 | void Update() { 40 | if (needsAdditionalUpdate) { 41 | // This should not be needed, but it's always missing 1 frame at the end 42 | applyLayoutRules(); 43 | needsAdditionalUpdate = false; 44 | } 45 | } 46 | 47 | void LateUpdate() { 48 | if (shouldAutoUpdate) { 49 | // Should auto update: checks if necessary 50 | if (Camera.main.pixelHeight != lastCameraHeight || Camera.main.pixelWidth != lastCameraWidth) { 51 | // Needs to update 52 | //Debug.Log("=============> Updating starting @ " + transform.parent.gameObject.name + "." + gameObject.name); 53 | lastCameraHeight = Camera.main.pixelHeight; 54 | lastCameraWidth = Camera.main.pixelWidth; 55 | applyLayoutRules(); 56 | needsAdditionalUpdate = true; 57 | } 58 | } 59 | } 60 | 61 | /* 62 | void OnEnable() { 63 | if (shouldAutoUpdate) needsUpdate = true; 64 | } 65 | */ 66 | 67 | 68 | // ================================================================================================================ 69 | // PUBLIC INTERFACE ----------------------------------------------------------------------------------------------- 70 | 71 | public void requestUpdate() { 72 | applyLayoutRules(); 73 | } 74 | 75 | public void addDependent(GameObject dependent) { 76 | if (!dependents.Contains(dependent)) dependents.Add(dependent); 77 | } 78 | 79 | public void removeDependent(GameObject dependent) { 80 | if (dependents.Contains(dependent)) dependents.Remove(dependent); 81 | } 82 | 83 | 84 | // ================================================================================================================ 85 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 86 | 87 | protected void checkParents(params GameObject[] parentsToCheck) { 88 | // Checks if a parent has changed, adding or removing itself from the parent as a dependent 89 | 90 | var addedParents = new List(); 91 | var removedParents = new List(); 92 | 93 | // Checks differences 94 | 95 | // Checks for new parents 96 | foreach (var parentToCheck in parentsToCheck) { 97 | if (parentToCheck == gameObject) { 98 | Debug.LogWarning("Trying to add itself as a parent!"); 99 | continue; 100 | } 101 | 102 | if (parentToCheck == null) continue; 103 | 104 | if (!parents.Contains(parentToCheck) && !addedParents.Contains(parentToCheck)) { 105 | addedParents.Add(parentToCheck); 106 | } 107 | } 108 | 109 | // Checks for removed parents 110 | foreach (var parent in parents) { 111 | if (Array.IndexOf(parentsToCheck, parent) < 0 && !removedParents.Contains(parent)) { 112 | removedParents.Add(parent); 113 | } 114 | } 115 | 116 | // Finally, adds and removes all parent 117 | foreach (var parent in addedParents) { 118 | addToParent(parent); 119 | parents.Add(parent); 120 | } 121 | 122 | foreach (var parent in removedParents) { 123 | removeFromParent(parent); 124 | parents.Remove(parent); 125 | } 126 | } 127 | 128 | protected Bounds getBoundsOrPoint(GameObject gameObject) { 129 | // Gets the best bounding box from a game object, using a placeholder box if none can be found 130 | var bounds = getBounds(gameObject); 131 | return bounds == null ? new Bounds(gameObject.transform.position, new Vector3(0.0001f, 0.0001f, 0.0001f)) : (Bounds)bounds; 132 | } 133 | 134 | protected Bounds? getBounds(GameObject gameObject) { 135 | // Gets the best bounding box from a game object 136 | if (gameObject == null) { 137 | // No object, use the screen size instead 138 | var tl = Camera.main.ScreenToWorldPoint(new Vector3(0, Camera.main.pixelHeight, 0.1f)); 139 | var br = Camera.main.ScreenToWorldPoint(new Vector3(Camera.main.pixelWidth, 0, 0.1f)); 140 | return new Bounds((tl+br)/2, br-tl); 141 | } else if (gameObject.GetComponent() != null) { 142 | // Use collider 143 | return gameObject.GetComponent().bounds; 144 | } else if (gameObject.GetComponent() != null) { 145 | // Use renderer 146 | return gameObject.GetComponent().bounds; 147 | } 148 | 149 | // None found! 150 | return null; 151 | } 152 | 153 | 154 | // ================================================================================================================ 155 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 156 | 157 | private void removeFromParent(GameObject parent) { 158 | if (parent != null && parent.GetComponent() != null) { 159 | var autoLayoutElements = parent.GetComponents(); 160 | foreach (var autoLayoutElement in autoLayoutElements) autoLayoutElement.removeDependent(gameObject); 161 | } 162 | } 163 | 164 | private void addToParent(GameObject parent) { 165 | if (parent != null && parent.GetComponent() != null) { 166 | var autoLayoutElements = parent.GetComponents(); 167 | foreach (var autoLayoutElement in autoLayoutElements) autoLayoutElement.addDependent(gameObject); 168 | } 169 | } 170 | 171 | private void applyLayoutRules() { 172 | //needsUpdate = false; 173 | 174 | //Debug.Log("Applying update @ frame " + Time.frameCount + ", " + transform.parent.gameObject.name + "." + gameObject.name + ", with " + dependents.Count + " dependents"); 175 | 176 | // Applies layout to self 177 | if (OnShouldApplyLayout != null) { 178 | OnShouldApplyLayout(); 179 | } 180 | 181 | // Propagates to dependents 182 | var destroyedDependents = new List(); 183 | foreach (var dependent in dependents) { 184 | if (dependent != null && dependent.activeInHierarchy) { 185 | var autoLayoutElements = dependent.GetComponents(); 186 | foreach (var autoLayoutElement in autoLayoutElements) autoLayoutElement.requestUpdate(); 187 | } else { 188 | // Object has been destroyed! 189 | Debug.LogWarning("Dependent object was destroyed during gameplay, will remove it from list of dependents"); 190 | destroyedDependents.Add(dependent); 191 | } 192 | } 193 | 194 | foreach (var dependent in destroyedDependents) { 195 | dependents.Remove(dependent); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /objects/layout/AutoResizer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | #if UNITY_EDITOR 5 | [ExecuteInEditMode()] 6 | #endif 7 | 8 | public class AutoResizer:AutoLayoutElement { 9 | 10 | // Enums 11 | private enum Alignments:int { 12 | NONE = 0, 13 | ANCHOR, 14 | START, 15 | MIDDLE, 16 | END, 17 | } 18 | 19 | private enum Axis { 20 | X, 21 | Y, 22 | Z 23 | } 24 | 25 | public enum AlignmentsX:int { 26 | NONE = 0, 27 | ANCHOR, 28 | LEFT, 29 | CENTER, 30 | RIGHT, 31 | } 32 | 33 | public enum AlignmentsY:int { 34 | NONE = 0, 35 | ANCHOR, 36 | BOTTOM, 37 | MIDDLE, 38 | TOP, 39 | } 40 | 41 | public enum FitTypes { 42 | FREE_DISTORT, 43 | FIT_INSIDE, 44 | FIT_OUTSIDE, 45 | FIT_X, 46 | FIT_Y 47 | } 48 | 49 | public GameObject parentLeft; 50 | public AlignmentsX parentLeftAnchor; 51 | public float marginLeft; 52 | 53 | public GameObject parentRight; 54 | public AlignmentsX parentRightAnchor; 55 | public float marginRight; 56 | 57 | public bool autoMoveX; 58 | 59 | public GameObject parentTop; 60 | public AlignmentsY parentTopAnchor; 61 | public float marginTop; 62 | 63 | public GameObject parentBottom; 64 | public AlignmentsY parentBottomAnchor; 65 | public float marginBottom; 66 | 67 | public bool autoMoveY; 68 | 69 | public FitTypes fitType; 70 | public float fitAspectRatio = 1; 71 | 72 | public float multiplyScale = 1; 73 | 74 | public bool updateAutomatically; 75 | public bool flipX; 76 | public bool flipY; 77 | 78 | 79 | // ================================================================================================================ 80 | // MAIN EVENT INTERFACE ------------------------------------------------------------------------------------------- 81 | 82 | void Awake() { 83 | shouldAutoUpdate = updateAutomatically; 84 | OnShouldApplyLayout += checkAllParents; 85 | OnShouldApplyLayout += applyLayout; 86 | } 87 | 88 | 89 | // ================================================================================================================ 90 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 91 | 92 | #if UNITY_EDITOR 93 | private void onUpdate() { 94 | applyLayout(); 95 | } 96 | #endif 97 | 98 | private void checkAllParents() { 99 | // Check if the parents were changed 100 | checkParents(parentLeft, parentRight, parentTop, parentBottom); 101 | } 102 | 103 | private void applyLayout() { 104 | // Applies the alignment position to this object 105 | 106 | // TODO: use screen coordinates if null 107 | // TODO: allow some axis to be free 108 | 109 | // Reset size for better calculation (in case any scale is set to 0) 110 | gameObject.transform.localScale = new Vector3(1, 1, 1); 111 | 112 | // Find own bounding box 113 | var bounds = getBoundsOrPoint(gameObject); 114 | 115 | // Find actual mapping positions 116 | var left = findPosition(parentLeft, (int)parentLeftAnchor, bounds.min.x, Axis.X) + marginLeft; 117 | var top = findPosition(parentTop, (int)parentTopAnchor, bounds.max.y, Axis.Y) - marginTop; 118 | var right = findPosition(parentRight, (int)parentRightAnchor, bounds.max.x, Axis.X) - marginRight; 119 | var bottom = findPosition(parentBottom, (int)parentBottomAnchor, bounds.min.y, Axis.Y) + marginBottom; 120 | 121 | // Set positions 122 | var scaleX = (right - left) / (bounds.max.x - bounds.min.x); 123 | var scaleY = (top - bottom) / (bounds.max.y - bounds.min.y); 124 | var scaleZ = gameObject.transform.localScale.z; 125 | 126 | var scaleAspectRatio = scaleX / scaleY; 127 | var objectIsWider = scaleAspectRatio < fitAspectRatio; 128 | 129 | var oldScaleX = scaleX; 130 | var oldScaleY = scaleY; 131 | 132 | if (fitType == FitTypes.FIT_X || (fitType == FitTypes.FIT_INSIDE && objectIsWider) || (fitType == FitTypes.FIT_OUTSIDE && !objectIsWider)) { 133 | // Keep ratio and use X as base 134 | scaleY = scaleX / fitAspectRatio; 135 | } else if (fitType == FitTypes.FIT_Y || (fitType == FitTypes.FIT_INSIDE && !objectIsWider) || (fitType == FitTypes.FIT_OUTSIDE && objectIsWider)) { 136 | // Keep ratio and use Y as base 137 | scaleX = scaleY * fitAspectRatio; 138 | } else { 139 | // Free distort, do nothing 140 | } 141 | 142 | scaleX *= multiplyScale * (flipX ? -1 : 1); 143 | scaleY *= multiplyScale * (flipY ? -1 : 1); 144 | 145 | var newX = gameObject.transform.position.x; 146 | var newY = gameObject.transform.position.y; 147 | var newZ = gameObject.transform.position.z; 148 | 149 | if (autoMoveX) newX = left + (gameObject.transform.position.x - bounds.min.x) * oldScaleX; 150 | if (autoMoveY) newY = bottom + (gameObject.transform.position.y - bounds.min.y) * oldScaleY; 151 | 152 | gameObject.transform.position = new Vector3(newX, newY, newZ); 153 | gameObject.transform.localScale = new Vector3(scaleX, scaleY, scaleZ); 154 | 155 | } 156 | 157 | private float findPosition(GameObject parent, int parentAnchorType, float currentPos, Axis axis) { 158 | 159 | // Check if it's valid 160 | if ((Alignments)parentAnchorType != Alignments.NONE) { 161 | 162 | var parentBoundsTry = getBounds(parent); 163 | 164 | if (parentBoundsTry == null) { 165 | // Invalid parent 166 | Debug.LogWarning("Error: GameObject must have a renderer or a collider attached to work as an AutoResizer parent"); 167 | } else { 168 | // Valid parent 169 | var parentBounds = (Bounds)parentBoundsTry; 170 | var parentAnchor = parent == null ? parentBounds.center : parent.transform.position; 171 | 172 | // Find boundaries 173 | float parentPos = 0; 174 | float parentMinPos = 0; 175 | float parentMaxPos = 0; 176 | 177 | switch (axis) { 178 | case Axis.X: 179 | parentPos = parentAnchor.x; 180 | parentMinPos = parentBounds.min.x; 181 | parentMaxPos = parentBounds.max.x; 182 | break; 183 | case Axis.Y: 184 | parentPos = parentAnchor.y; 185 | parentMinPos = parentBounds.min.y; 186 | parentMaxPos = parentBounds.max.y; 187 | break; 188 | case Axis.Z: 189 | parentPos = parentAnchor.z; 190 | parentMinPos = parentBounds.min.z; 191 | parentMaxPos = parentBounds.max.z; 192 | break; 193 | 194 | } 195 | 196 | // Find base 197 | switch ((Alignments)parentAnchorType) { 198 | case Alignments.ANCHOR: 199 | return parentPos; 200 | case Alignments.START: 201 | return parentMinPos; 202 | case Alignments.MIDDLE: 203 | return (parentMinPos + parentMaxPos) / 2; 204 | case Alignments.END: 205 | return parentMaxPos; 206 | } 207 | } 208 | } 209 | 210 | return currentPos; 211 | } 212 | } -------------------------------------------------------------------------------- /overlays/FPSDisplay.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class FPSDisplay:MonoBehaviour { 4 | 5 | private int frames = 0; 6 | private float updateInterval = 0.5f; // Time to wait, in seconds 7 | private float lastUpdate = 0; 8 | private TextMesh textMesh; 9 | 10 | void Start() { 11 | textMesh = GetComponent(); 12 | } 13 | 14 | void Update() { 15 | frames++; 16 | if (Time.time - lastUpdate > updateInterval) { 17 | updateValues(); 18 | } 19 | } 20 | 21 | void updateValues() { 22 | float timePassed = Time.time - lastUpdate; 23 | float msec = timePassed / (float)frames * 1000f; 24 | float fps = frames / timePassed; 25 | 26 | lastUpdate += timePassed; 27 | frames = 0; 28 | 29 | textMesh.text = string.Format("{0:0.0} ms ({1:0.} fps)", msec, fps); 30 | } 31 | } -------------------------------------------------------------------------------- /overlays/HoloLensStatusDisplay.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class HoloLensStatusDisplay:MonoBehaviour { 4 | 5 | private int _frames = 0; 6 | private float _updateInterval = 0.10f; // Time to wait, in seconds 7 | private float _lastUpdate = 0; 8 | private TextMesh _textMesh; 9 | 10 | private GazeWatcher _gazeWatcher; 11 | 12 | void Start() { 13 | _textMesh = GetComponent(); 14 | _gazeWatcher = new GazeWatcher(Camera.main); 15 | } 16 | 17 | void Update() { 18 | _frames++; 19 | if (Time.time - _lastUpdate > _updateInterval) { 20 | updateValues(); 21 | } 22 | } 23 | 24 | void updateValues() { 25 | float timePassed = Time.time - _lastUpdate; 26 | float msec = timePassed / (float)_frames * 1000f; 27 | float fps = _frames / timePassed; 28 | 29 | _lastUpdate += timePassed; 30 | _frames = 0; 31 | 32 | var caption = ""; 33 | caption += string.Format("Time: {0:0.0}ms", msec); 34 | caption += "\n"; 35 | caption += string.Format("FPS: {0:0.} ", fps); 36 | caption += "\n"; 37 | caption += "Camera rot: " + _gazeWatcher.direction; 38 | caption += "\n"; 39 | caption += "Camera pos: " + _gazeWatcher.position; 40 | _textMesh.text = caption; 41 | } 42 | } -------------------------------------------------------------------------------- /textures/Texture2DUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public class Texture2DUtils { 5 | 6 | /** 7 | * Draws a non-aliased line to a texture 8 | * Originally from http://wiki.unity3d.com/index.php?title=TextureDrawLine 9 | */ 10 | /* 11 | public static void drawLine(Texture2D __texture, int __x0, int __y0, int __x1, int __y1, Color __color, float __alpha = 1) { 12 | int dy = __y1 - __y0; 13 | int dx = __x1 - __x0; 14 | int stepx, stepy; 15 | 16 | if (dy < 0) { 17 | dy = -dy; 18 | stepy = -1; 19 | } else { 20 | stepy = 1; 21 | } 22 | if (dx < 0) { 23 | dx = -dx; 24 | stepx = -1; 25 | } else { 26 | stepx = 1; 27 | } 28 | dy <<= 1; 29 | dx <<= 1; 30 | 31 | float fraction = 0; 32 | Color32[] textureColors = __texture.GetPixels32(); 33 | int iw = __texture.width; 34 | int ih = __texture.height; 35 | int pos; 36 | 37 | __texture.SetPixel(__x0, __y0, __color); 38 | if (dx > dy) { 39 | fraction = dy - (dx >> 1); 40 | while (Mathf.Abs(__x0 - __x1) > 1) { 41 | if (fraction >= 0) { 42 | __y0 += stepy; 43 | fraction -= dx; 44 | } 45 | __x0 += stepx; 46 | fraction += dy; 47 | pos = __y0 * iw + __x0; 48 | textureColors[pos] = mixColors(textureColors[pos], __color, __alpha); 49 | __texture.SetPixel(__x0, __y0, col); 50 | } 51 | } else { 52 | fraction = dx - (dy >> 1); 53 | while (Mathf.Abs(__y0 - __y1) > 1) { 54 | if (fraction >= 0) { 55 | __x0 += stepx; 56 | fraction -= dy; 57 | } 58 | __y0 += stepy; 59 | fraction += dx; 60 | pos = __y0 * iw + __x0; 61 | textureColors[pos] = mixColors(textureColors[pos], __color, __alpha); 62 | } 63 | } 64 | 65 | __texture2D.SetPixels(textureColors); 66 | } 67 | */ 68 | 69 | 70 | public static void drawAntiAliasLine(Texture2D __texture, int __x0, int __y0, int __x1, int __y1, Color __color, float __alpha = 1) { 71 | // http://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm 72 | 73 | 74 | 75 | } 76 | 77 | // ================================================================================================================ 78 | // INTERNAL INTERFACE --------------------------------------------------------------------------------------------- 79 | 80 | /** 81 | * Applies an array of colors to a texture (allows pre-multiplication with alpha, and only applies to a certain rectangle) 82 | */ 83 | /* 84 | private static void applyColorsToTexture(Texture2D __texture, Color32[] __newColors, int __startX, int __startY, int __width, float __alpha = 1) { 85 | Color32[] textureColors = __texture.GetPixels32(); 86 | 87 | int iw = __texture.width; 88 | int ih = __texture.height; 89 | 90 | int colorRectRight = __startX + __width; 91 | int colorRectBottom = __startY + (__newColors.Length / __width); 92 | 93 | int x, y; 94 | int pos; 95 | 96 | for (y = 0; y < ih; y++) { 97 | for (x = 0; x < iw; x++) { 98 | if (x >= __startX && y >= __startY && x < colorRectRight && y < colorRectBottom) { 99 | // Inside the rectangle, so mix colors properly 100 | pos = y * iw + x; 101 | textureColors[pos] = mixColors(textureColors[pos], __newColors[x - __startX + (y - __startY) * __width], __alpha); 102 | } 103 | } 104 | } 105 | 106 | // Apply the actual colors to the texture 107 | __texture.SetPixels(textureColors); 108 | __texture.Apply(); 109 | } 110 | */ 111 | 112 | /* 113 | private static Color32 mixColors(Color32 __oldColor, Color32 __newColor, float __alpha = 1) { 114 | // This is not an interpolated color because it must flatten, not just interpolate 115 | float oa = __oldColor.a / 255f; 116 | float na = (__newColor.a / 255f) * __alpha; 117 | float nna = 1f - na; 118 | 119 | float a = oa + (1 - oa) * na * 255f; 120 | float r = (__oldColor.r * nna + __newColor.r * na) / 2f; 121 | float g = (__oldColor.g * nna + __newColor.g * na) / 2f; 122 | float b = (__oldColor.b * nna + __newColorbr * na) / 2f; 123 | 124 | return new Color32(Math.round(r), Math.round(g), Math.round(b), Math.round(a)); 125 | } 126 | */ 127 | } -------------------------------------------------------------------------------- /transitions/ZTweenSurrogate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | public class ZTweenSurrogate:MonoBehaviour { 5 | 6 | // Tween surrogate, to control its updating 7 | 8 | private List tweenSequences = new List(); 9 | 10 | void Update() { 11 | for (int i = 0; i < tweenSequences.Count; i++) { 12 | if (tweenSequences[i] != null) { 13 | tweenSequences[i].update(); 14 | } else { 15 | tweenSequences.RemoveAt(i); 16 | i--; 17 | } 18 | } 19 | } 20 | 21 | internal void add(ZTween.ZTweenSequence tweenSequence) { 22 | tweenSequences.Add(tweenSequence); 23 | } 24 | 25 | internal void remove(ZTween.ZTweenSequence tweenSequence) { 26 | // Nullify first, remove later - otherwise it gets remove while doing Update(), which can cause the list to trip on itself 27 | tweenSequences[tweenSequences.IndexOf(tweenSequence)] = null; 28 | } 29 | } -------------------------------------------------------------------------------- /utils/ByteUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public class ByteUtils { 5 | 6 | public enum Endian { 7 | LITTLE_ENDIAN, 8 | BIG_ENDIAN 9 | } 10 | 11 | /** 12 | * Writes a short (Int16) to a byte array. 13 | * This is an aux function used when creating the WAV data. 14 | */ 15 | public static void writeShortToBytes(byte[] __bytes, ref int __position, short __newShort, Endian __endian) { 16 | writeBytes(__bytes, ref __position, new byte[2] { (byte)((__newShort >> 8) & 0xff), (byte)(__newShort & 0xff) }, __endian); 17 | } 18 | 19 | /** 20 | * Writes a uint (UInt32) to a byte array. 21 | * This is an aux function used when creating the WAV data. 22 | */ 23 | public static void writeUintToBytes(byte[] __bytes, ref int __position, uint __newUint, Endian __endian) { 24 | writeBytes(__bytes, ref __position, new byte[4] { (byte)((__newUint >> 24) & 0xff), (byte)((__newUint >> 16) & 0xff), (byte)((__newUint >> 8) & 0xff), (byte)(__newUint & 0xff) }, __endian); 25 | } 26 | 27 | /** 28 | * Writes any number of bytes into a byte array, at a given position. 29 | * This is an aux function used when creating the WAV data. 30 | */ 31 | public static void writeBytes(byte[] __bytes, ref int __position, byte[] __newBytes, Endian __endian) { 32 | // Writes __newBytes to __bytes at position __position, increasing the position depending on the length of __newBytes 33 | for (int i = 0; i < __newBytes.Length; i++) { 34 | __bytes[__position] = __newBytes[__endian == Endian.BIG_ENDIAN ? i : __newBytes.Length - i - 1]; 35 | __position++; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /utils/Editor/MathUtilsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using NUnit.Framework; 6 | 7 | /** 8 | * @author zeh 9 | */ 10 | [TestFixture] 11 | public class MathUtilsTests { 12 | 13 | /* 14 | * A Assert.AreEqualfor the JSON class 15 | * 16 | * Uses NUnit: http://www.nunit.org/index.php?p=quickStart&r=2.6.4 17 | * 18 | * Assert.AreEqual( int expected, int actual, string message ); 19 | * Assert.AreEqual( int expected, int actual ); 20 | * Assert.AreSame( object expected, object actual ); 21 | * Assert.Contains( object anObject, IList collection ); 22 | * Assert.AreNotSame( object expected, object actual ); 23 | * Assert.Greater( int arg1, int arg2 ); 24 | * Assert.GreaterOrEqual( int arg1, int arg2 ); 25 | * Assert.Less( int arg1, int arg2 ); 26 | * Assert.IsInstanceOf( object actual ); 27 | * Assert.IsNotInstanceOf( object actual ); 28 | * Assert.IsAssignableFrom( object actual ); 29 | * Assert.IsNotAssignableFrom( object actual ); 30 | * Assert.IsTrue( bool condition ); 31 | * Assert.IsFalse( bool condition); 32 | * Assert.IsNull( object anObject ); 33 | * Assert.IsNotNull( object anObject ); 34 | * Assert.IsEmpty( string aString ); // Or ICollection 35 | * Assert.IsNotEmpty( string aString ); // Or ICollection 36 | */ 37 | 38 | // ================================================================================================================ 39 | // SETUP ---------------------------------------------------------------------------------------------------------- 40 | 41 | [TestFixtureSetUp] 42 | public void SetUp() { 43 | } 44 | 45 | [TestFixtureTearDown] 46 | public void TearDown() { 47 | } 48 | 49 | 50 | // ================================================================================================================ 51 | // TEST INTERFACE ------------------------------------------------------------------------------------------------- 52 | 53 | [Test] 54 | public void listCycleDistance() { 55 | Assert.AreEqual(0, MathUtils.listCycleDistance(0, 0, 10)); 56 | Assert.AreEqual(0.5f, MathUtils.listCycleDistance(0.5f, 0, 10)); 57 | Assert.AreEqual(1, MathUtils.listCycleDistance(1, 0, 10)); 58 | Assert.AreEqual(2, MathUtils.listCycleDistance(2, 0, 10)); 59 | Assert.AreEqual(3, MathUtils.listCycleDistance(3, 0, 10)); 60 | Assert.AreEqual(4, MathUtils.listCycleDistance(4, 0, 10)); 61 | Assert.AreEqual(5, MathUtils.listCycleDistance(5, 0, 10)); 62 | Assert.AreEqual(-4, MathUtils.listCycleDistance(6, 0, 10)); 63 | Assert.AreEqual(-3, MathUtils.listCycleDistance(7, 0, 10)); 64 | Assert.AreEqual(-2, MathUtils.listCycleDistance(8, 0, 10)); 65 | Assert.AreEqual(-1, MathUtils.listCycleDistance(9, 0, 10)); 66 | Assert.AreEqual(-0.5f, MathUtils.listCycleDistance(9.5f, 0, 10)); 67 | 68 | Assert.AreEqual(-1, MathUtils.listCycleDistance(0, 1, 10)); 69 | Assert.AreEqual(-0.5f, MathUtils.listCycleDistance(0.5f, 1, 10)); 70 | Assert.AreEqual(0, MathUtils.listCycleDistance(1, 1, 10)); 71 | Assert.AreEqual(1, MathUtils.listCycleDistance(2, 1, 10)); 72 | Assert.AreEqual(2, MathUtils.listCycleDistance(3, 1, 10)); 73 | Assert.AreEqual(3, MathUtils.listCycleDistance(4, 1, 10)); 74 | Assert.AreEqual(4, MathUtils.listCycleDistance(5, 1, 10)); 75 | Assert.AreEqual(5, MathUtils.listCycleDistance(6, 1, 10)); 76 | Assert.AreEqual(-4, MathUtils.listCycleDistance(7, 1, 10)); 77 | Assert.AreEqual(-3, MathUtils.listCycleDistance(8, 1, 10)); 78 | Assert.AreEqual(-2, MathUtils.listCycleDistance(9, 1, 10)); 79 | Assert.AreEqual(-1.5f, MathUtils.listCycleDistance(9.5f, 1, 10)); 80 | 81 | Assert.AreEqual(1, MathUtils.listCycleDistance(0, 9, 10)); 82 | Assert.AreEqual(1.5f, MathUtils.listCycleDistance(0.5f, 9, 10)); 83 | Assert.AreEqual(2, MathUtils.listCycleDistance(1, 9, 10)); 84 | Assert.AreEqual(3, MathUtils.listCycleDistance(2, 9, 10)); 85 | Assert.AreEqual(4, MathUtils.listCycleDistance(3, 9, 10)); 86 | Assert.AreEqual(5, MathUtils.listCycleDistance(4, 9, 10)); 87 | Assert.AreEqual(-4, MathUtils.listCycleDistance(5, 9, 10)); 88 | Assert.AreEqual(-3, MathUtils.listCycleDistance(6, 9, 10)); 89 | Assert.AreEqual(-2, MathUtils.listCycleDistance(7, 9, 10)); 90 | Assert.AreEqual(-1, MathUtils.listCycleDistance(8, 9, 10)); 91 | Assert.AreEqual(0, MathUtils.listCycleDistance(9, 9, 10)); 92 | Assert.AreEqual(0.5f, MathUtils.listCycleDistance(9.5f, 9, 10)); 93 | 94 | Assert.AreEqual(5, MathUtils.listCycleDistance(0, 5, 10)); 95 | Assert.AreEqual(-4.5f, MathUtils.listCycleDistance(0.5f, 5, 10)); 96 | Assert.AreEqual(-4, MathUtils.listCycleDistance(1, 5, 10)); 97 | Assert.AreEqual(-3, MathUtils.listCycleDistance(2, 5, 10)); 98 | Assert.AreEqual(-2, MathUtils.listCycleDistance(3, 5, 10)); 99 | Assert.AreEqual(-1, MathUtils.listCycleDistance(4, 5, 10)); 100 | Assert.AreEqual(0, MathUtils.listCycleDistance(5, 5, 10)); 101 | Assert.AreEqual(1, MathUtils.listCycleDistance(6, 5, 10)); 102 | Assert.AreEqual(2, MathUtils.listCycleDistance(7, 5, 10)); 103 | Assert.AreEqual(3, MathUtils.listCycleDistance(8, 5, 10)); 104 | Assert.AreEqual(4, MathUtils.listCycleDistance(9, 5, 10)); 105 | Assert.AreEqual(4.5f, MathUtils.listCycleDistance(9.5f, 5, 10)); 106 | } 107 | } -------------------------------------------------------------------------------- /utils/GameObjectUtils.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | 4 | public class GameObjectUtils { 5 | 6 | public static List getAllChildren(GameObject gameObject) { 7 | var children = new List(); 8 | foreach (Transform child in gameObject.transform) { 9 | children.Add(child.gameObject); 10 | } 11 | return children; 12 | } 13 | 14 | public static void removeAllChildren(GameObject gameObject) { 15 | var children = getAllChildren(gameObject); 16 | foreach(GameObject child in children) { 17 | destroySafely(child.gameObject); 18 | } 19 | } 20 | 21 | // TODO: allow to set bounds, align inside, outside, ...? 22 | 23 | public static void resizeToFit(GameObject gameObject, float maxWidth = 0, float maxHeight = 0, float maxDepth = 0) { 24 | // Resize an object to the desired size 25 | 26 | // Reset size for better calculation (in case any scale is set to 0) 27 | gameObject.transform.localScale = Vector3.one; 28 | 29 | // Find bounding box 30 | var bounds = getBounds(gameObject); 31 | 32 | // Scale accordingly 33 | var width = bounds.max.x - bounds.min.x; 34 | var height = bounds.max.y - bounds.min.y; 35 | var depth = bounds.max.z - bounds.min.z; 36 | 37 | var newScale = 0.0f; 38 | var tempScale = 0.0f; 39 | var scaleWillChange = false; 40 | 41 | if (maxHeight != 0) { 42 | // Decide by height 43 | tempScale = maxHeight / height; 44 | newScale = scaleWillChange ? Mathf.Min(tempScale, newScale) : tempScale; 45 | scaleWillChange = true; 46 | } 47 | 48 | if (maxDepth != 0) { 49 | // Decide by depth 50 | tempScale = maxDepth / depth; 51 | newScale = scaleWillChange ? Mathf.Min(tempScale, newScale) : tempScale; 52 | scaleWillChange = true; 53 | } 54 | 55 | if (maxWidth != 0) { 56 | // Decide by width 57 | tempScale = maxWidth / width; 58 | newScale = scaleWillChange ? Mathf.Min(tempScale, newScale) : tempScale; 59 | scaleWillChange = true; 60 | } 61 | 62 | if (scaleWillChange) { 63 | // Finally, scale 64 | gameObject.transform.localScale = new Vector3(newScale, newScale, newScale); 65 | } 66 | } 67 | 68 | public static void alignToVector(GameObject gameObject, Vector3 targetPosition, float alignX, float alignY, float alignZ) { 69 | // Align is from 0 to 1 (min to max) 70 | 71 | // TODO: move this somewhere else 72 | // TODO: allow to set bounds, align inside, outside, ...? 73 | 74 | // Also, position the object so we only use the bottom 75 | /* 76 | var newX = gameObject.transform.localPosition.x; 77 | var newZ = gameObject.transform.localPosition.z; 78 | 79 | newX = 0; // left + (gameObject.transform.position.x - bounds.min.x) * oldScaleX; 80 | var newY = (gameObject.transform.localPosition.y - bounds.min.y) * newScale; 81 | newZ = 0; 82 | 83 | gameObject.transform.localPosition = new Vector3(newX, newY, newZ); 84 | */ 85 | 86 | // Find position and bounding box 87 | var bounds = getBounds(gameObject); 88 | var pivot = gameObject.transform.position; 89 | 90 | // Calculate and set new positions 91 | var newX = pivot.x - MathUtils.map(alignX, 0.0f, 1.0f, bounds.min.x, bounds.max.x); 92 | var newY = pivot.y - MathUtils.map(alignY, 0.0f, 1.0f, bounds.min.y, bounds.max.y); 93 | var newZ = pivot.z - MathUtils.map(alignZ, 0.0f, 1.0f, bounds.min.z, bounds.max.z); 94 | 95 | gameObject.transform.position = targetPosition + new Vector3(newX, newY, newZ); 96 | } 97 | 98 | public static Bounds getBounds(GameObject gameObject) { 99 | // Gets the best bounding box from a game object, using world space coordinates 100 | 101 | var bounds = new Bounds(gameObject.transform.position, new Vector3(0, 0, 0)); 102 | var hasBounds = true; 103 | 104 | if (gameObject.GetComponent() != null) { 105 | // Use collider 106 | bounds = gameObject.GetComponent().bounds; 107 | } else if (gameObject.GetComponent() != null) { 108 | // Use renderer 109 | bounds = gameObject.GetComponent().bounds; 110 | } else { 111 | hasBounds = false; 112 | } 113 | 114 | // Find children if needed 115 | foreach (Transform child in gameObject.transform) { 116 | var childBounds = getBounds(child.gameObject); 117 | if (childBounds.extents.magnitude > 0) { 118 | if (!hasBounds) { 119 | bounds = childBounds; 120 | hasBounds = true; 121 | } else { 122 | bounds.Encapsulate(childBounds); 123 | } 124 | } 125 | } 126 | 127 | return bounds; 128 | } 129 | 130 | public static void createMeshColliders(GameObject gameObject, bool actOnChildren) { 131 | // Create mesh colliders on a specific gameObject 132 | 133 | // First, remove existing colliders 134 | removeComponents(gameObject); 135 | 136 | // Now, create mesh colliders if meshes exist 137 | var mesh = gameObject.GetComponent(); 138 | if (mesh != null) { 139 | var meshCollider = gameObject.AddComponent(); 140 | meshCollider.convex = false; 141 | meshCollider.isTrigger = false; 142 | } 143 | 144 | // Recurse into children if desired 145 | if (actOnChildren) { 146 | foreach (Transform child in gameObject.transform) { 147 | createMeshColliders(child.gameObject, true); 148 | } 149 | } 150 | } 151 | 152 | public static void setRenderersVisibility(GameObject gameObject, bool visible) { 153 | var renderers = gameObject.GetComponentsInChildren(); 154 | foreach (var renderer in renderers) { 155 | renderer.enabled = visible; 156 | } 157 | } 158 | 159 | public static void setCollidersEnabled(GameObject gameObject, bool visible) { 160 | var colliders = gameObject.GetComponentsInChildren(); 161 | foreach (var collider in colliders) { 162 | collider.enabled = visible; 163 | } 164 | } 165 | 166 | public static void setAlpha(GameObject gameObject, float alpha) { 167 | var renderers = gameObject.GetComponentsInChildren(); 168 | foreach (var renderer in renderers) { 169 | foreach (var material in renderer.sharedMaterials) { 170 | material.color = new Color(material.color.r, material.color.g, material.color.b, alpha); 171 | } 172 | } 173 | } 174 | 175 | public static void removeComponents(GameObject gameObject) where T:Component { 176 | var components = gameObject.GetComponents(); 177 | foreach (Component component in components) { 178 | destroySafely(component); 179 | } 180 | } 181 | 182 | public static void destroySafely(GameObject gameObject) { 183 | #if UNITY_EDITOR 184 | GameObject.DestroyImmediate(gameObject); 185 | #else 186 | GameObject.Destroy(gameObject); 187 | #endif 188 | } 189 | 190 | public static void destroySafely(Component component) { 191 | #if UNITY_EDITOR 192 | GameObject.DestroyImmediate(component); 193 | #else 194 | GameObject.Destroy(component); 195 | #endif 196 | } 197 | } 198 | 199 | -------------------------------------------------------------------------------- /utils/JSONUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using System.Text.RegularExpressions; 4 | using System.Collections.Generic; 5 | 6 | public class JSONUtils { 7 | 8 | /* 9 | public static Dictionary FromJsonToDictionary(string input) { 10 | var d = JsonUtility.FromJson>(input); 11 | return d.getDictionary(); 12 | } 13 | 14 | publ static string FromDictionaryToJson(Dictionary input, bool prettyPrint) { 15 | //var d = new SerializableDictionary(input); 16 | return JsonUtility.ToJson(d, prettyPrint); 17 | } 18 | */ 19 | 20 | public static Dictionary DictionaryFromJSON(string input) { 21 | var d = new Dictionary(); 22 | //d.Add("stringKey", "value"); 23 | //d.Add("numberKey", 10); 24 | //JsonUtility.FromJsonOverwrite(input, d); 25 | return d; 26 | } 27 | 28 | public static string removeComments(string input) { 29 | return Regex.Replace(input, "/\\*[\\w\\W]*?\\*/", ""); 30 | } 31 | 32 | /* 33 | class SerializableDictionary:ISerializationCallbackReceiver { 34 | // Allows a dictionary to be serialized, and deserialized into 35 | // Inspired by http://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.OnBeforeSerialize.html 36 | 37 | private Dictionary dictionary = new Dictionary(); 38 | private List keys = new List(); 39 | private List values = new List(); 40 | 41 | public SerializableDictionary(Dictionary newDictionary) { 42 | dictionary = newDictionary; 43 | } 44 | 45 | public void OnBeforeSerialize() { 46 | keys.Clear(); 47 | values.Clear(); 48 | foreach (var pair in dictionary) { 49 | keys.Add(pair.Key); 50 | if (pair.Value is IDictionary) { 51 | // Another dictionary 52 | values.Add(new SerializableDictionary<>(pair.value)); 53 | } else { 54 | // Something else 55 | values.Add(pair.Value); 56 | } 57 | } 58 | } 59 | 60 | public void OnAfterDeserialize() { 61 | dictionary = new Dictionary(); 62 | for (int i = 0; i < < Math.Min(keys.Count, values.Count); i++) { 63 | if (values[i] is SerializableDictionary) { 64 | // Another dictionary 65 | dictionary.Add(keys[i], values[i].getDictionary()); 66 | } else { 67 | // Something else 68 | dictionary.Add(keys[i], values[i]); 69 | } 70 | } 71 | } 72 | 73 | public Dictionary getDictionary() { 74 | return dictionary; 75 | } 76 | } 77 | */ 78 | 79 | } 80 | -------------------------------------------------------------------------------- /utils/MathUtils.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class MathUtils { 4 | 5 | /* 6 | public static double map(double __value, double __oldMin, double __oldMax, double __newMin = 0, double __newMax = 0, bool __clamp = false) { 7 | if (__oldMin == __oldMax) return __newMin; 8 | double p = ((__value - __oldMin) / (__oldMax - __oldMin) * (__newMax - __newMin)) + __newMin; 9 | if (__clamp) p = __newMin < __newMax ? clamp(p, __newMin, __newMax) : clamp(p, __newMax, __newMin); 10 | return p; 11 | } 12 | */ 13 | 14 | public static float map(float value, float oldMin, float oldMax, float newMin = 0, float newMax = 1, bool clamp = false) { 15 | if (oldMin == oldMax) return newMin; 16 | float p = ((value - oldMin) / (oldMax - oldMin) * (newMax - newMin)) + newMin; 17 | if (clamp) p = newMin < newMax ? MathUtils.clamp(p, newMin, newMax) : MathUtils.clamp(p, newMax, newMin); 18 | return p; 19 | } 20 | 21 | /* 22 | public static double map(TimeSpan __value, TimeSpan __oldMin, TimeSpan __oldMax, double __newMin = 0, double __newMax = 0, bool __clamp = false) { 23 | return MathUtils.map(__value.TotalMilliseconds, __oldMin.TotalMilliseconds, __oldMax.TotalMilliseconds, __newMin, __newMax, __clamp); 24 | } 25 | */ 26 | 27 | public static float clamp(float value, float min = 0.0f, float max = 1.0f) { 28 | return value < min ? min : value > max ? max : value; 29 | } 30 | 31 | /* 32 | public static double clamp(double __value, double __min = 0.0, double __max = 1.0) { 33 | return __value < __min ? __min : __value > __max ? __max : __value; 34 | } 35 | */ 36 | 37 | /** 38 | * Clamps a value to a range, by restricting it to a minimum and maximum values but folding the value to the range instead of simply resetting to the minimum and maximum. It works like a more powerful Modulo function. 39 | * @param value The value to be clamped. 40 | * @param min Minimum value allowed. 41 | * @param max Maximum value allowed. 42 | * @return The newly clamped value. 43 | * @example Some examples: 44 | * 45 | * MathUtils.rangeMod(14, 0, 10); // 4 46 | * MathUtils.rangeMod(360, 0, 360); // 0 47 | * MathUtils.rangeMod(360, -180, 180); // 0 48 | * MathUtils.rangeMod(21, 0, 10); // 1 49 | * MathUtils.rangeMod(-98, 0, 100); // 2 50 | */ 51 | public static float rangeMod(float value, float min, float pseudoMax) { 52 | float range = pseudoMax - min; 53 | value = (value - min) % range; 54 | if (value < 0) value = range - (-value % range); 55 | value += min; 56 | return value; 57 | } 58 | 59 | /** 60 | * Find the "distance" two item indexes are in a list. The list cycles through. 61 | * If negative, the item index is behind the base index. 62 | */ 63 | // TODO: better name? 64 | public static float listCycleDistance(float newPosition, float basePosition, int baseLength) { 65 | float posBefore, posAfter; 66 | 67 | if (newPosition > basePosition) { 68 | posBefore = - baseLength + newPosition - basePosition; 69 | posAfter = newPosition - basePosition; 70 | } else { 71 | posBefore = newPosition - basePosition; 72 | posAfter = baseLength - basePosition + newPosition; 73 | } 74 | 75 | //posBefore = posBefore % baseLength; 76 | //posAfter = posAfter % baseLength; 77 | 78 | return Mathf.Abs(posBefore) < posAfter ? posBefore : posAfter; 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /utils/StringUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public class StringUtils { 5 | 6 | public static string getRandomAlphanumericString(int __length = 1) { 7 | // Returns a random alphanumeric string with the specific number of chars 8 | string chars = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"; 9 | int i = 0; 10 | 11 | string str = ""; 12 | 13 | for (i = 0; i < __length; i++) { 14 | str += chars.Substring((int)Math.Round((float)UnityEngine.Random.Range(0, chars.Length-1)), 1); 15 | } 16 | 17 | return str; 18 | } 19 | 20 | // http://wiki.unity3d.com/index.php?title=MD5 21 | public static string MD5(string strToEncrypt) { 22 | System.Text.UTF8Encoding ue = new System.Text.UTF8Encoding(); 23 | byte[] bytes = ue.GetBytes(strToEncrypt); 24 | 25 | // encrypt bytes 26 | System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); 27 | byte[] hashBytes = md5.ComputeHash(bytes); 28 | 29 | // Convert the encrypted bytes back to a string (base 16) 30 | string hashString = ""; 31 | 32 | for (int i = 0; i < hashBytes.Length; i++) { 33 | hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, '0'); 34 | } 35 | 36 | return hashString.PadLeft(32, '0'); 37 | } 38 | 39 | // Encrypts a string using a key and the RC4 algorithm 40 | // http://entitycrisis.blogspot.com/2011/04/encryption-between-python-and-c.html 41 | // Test: http://rc4.online-domain-tools.com/ 42 | public static byte[] encodeRC4(string __data, string __skey) { 43 | byte[] bytes = System.Text.ASCIIEncoding.ASCII.GetBytes(__data); 44 | var key = System.Text.ASCIIEncoding.ASCII.GetBytes(__skey); 45 | byte[] s = new byte[256]; 46 | byte[] k = new byte[256]; 47 | byte temp; 48 | int i, j; 49 | 50 | for (i = 0; i < 256; i++) { 51 | s[i] = (byte)i; 52 | k[i] = key[i % key.GetLength (0)]; 53 | } 54 | 55 | j = 0; 56 | for (i = 0; i < 256; i++) { 57 | j = (j + s[i] + k[i]) % 256; 58 | temp = s[i]; 59 | s[i] = s[j]; 60 | s[j] = temp; 61 | } 62 | 63 | i = j = 0; 64 | for (int x = 0; x < bytes.GetLength (0); x++) { 65 | i = (i + 1) % 256; 66 | j = (j + s[i]) % 256; 67 | temp = s[i]; 68 | s[i] = s[j]; 69 | s[j] = temp; 70 | int t = (s[i] + s[j]) % 256; 71 | bytes[x] ^= s[t]; 72 | } 73 | 74 | return bytes; 75 | } 76 | 77 | public static string convertBytesToString(byte[] bytes) { 78 | // Converts bytes to a string representation (e.g. ffd4a0) 79 | string result = ""; 80 | for (int i = 0; i < bytes.Length; i++) { 81 | result += bytes[i].ToString("x2"); 82 | } 83 | return result; 84 | } 85 | 86 | public static string convertIntToCustomBase(int __number, string __charset, int __padSize = 0) { 87 | // Convert a number to any base given a charset 88 | // Radix, as per http://www.newgrounds.com/wiki/creator-resources/newgrounds-apis/encryption-process 89 | 90 | int baseLen = __charset.Length; 91 | int currValue = __number; 92 | int rest = 0; 93 | string result = ""; 94 | while (currValue >= baseLen) { 95 | rest = currValue % baseLen; 96 | currValue = (currValue - rest) / baseLen; 97 | result = __charset.Substring(rest, 1) + result; 98 | } 99 | 100 | rest = currValue % baseLen; 101 | result = __charset.Substring(rest, 1) + result; 102 | 103 | while (result.Length < __padSize) result = __charset.Substring(0,1) + result; 104 | 105 | return result; 106 | } 107 | } 108 | --------------------------------------------------------------------------------