├── Scriptable Object Updater.unitypackage ├── Scriptable Object Updater ├── Editor.meta ├── Enums.meta ├── Tickers.meta ├── Attribute.meta ├── Enums │ ├── EEventType.cs │ └── EEventType.cs.meta ├── Tickers │ ├── AwakeTick.cs │ ├── StartTick.cs │ ├── UpdateTicker.cs │ ├── Ticker.cs.meta │ ├── AwakeTick.cs.meta │ ├── StartTick.cs.meta │ ├── UpdateTicker.cs.meta │ ├── FixedUpdateTicker.cs.meta │ ├── LateUpdateTicker.cs │ ├── LateUpdateTicker.cs.meta │ ├── FixedUpdateTicker.cs │ └── Ticker.cs ├── UpdateableEvent.cs.meta ├── InitializeableAssetContainer.cs.meta ├── Editor │ ├── ScriptableObjectInitializer.cs.meta │ ├── ScriptableObjectUpdaterConfiguration.cs.meta │ ├── ScriptableObjectUpdaterConfiguration.cs │ └── ScriptableObjectInitializer.cs ├── Attribute │ ├── InitializeAssetMethodAttribute.cs.meta │ └── InitializeAssetMethodAttribute.cs ├── UpdateableEvent.cs └── InitializeableAssetContainer.cs ├── README.md └── LICENSE /Scriptable Object Updater.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexMeesters/UpdateableScriptableObjects/HEAD/Scriptable Object Updater.unitypackage -------------------------------------------------------------------------------- /Scriptable Object Updater/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9320a2f1fcd4f0b499fad4e3b0703bca 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Enums.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 908e5507b03ede447a55b43dddb83bc8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8600aafefa32e1240b82ef8ba0a25409 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Attribute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3716c382d9ebb1f49a35f872d2f3691d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Enums/EEventType.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace Lowscope.ScriptableObjectUpdater 5 | { 6 | public enum EEventType { Awake, Start, Update, FixedUpdate, LateUpdate }; 7 | } -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/AwakeTick.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace Lowscope.ScriptableObjectUpdater 5 | { 6 | public class AwakeTick : Ticker 7 | { 8 | public void Awake() 9 | { 10 | DispatchTick(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/StartTick.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace Lowscope.ScriptableObjectUpdater 5 | { 6 | public class StartTick : Ticker 7 | { 8 | private void Start() 9 | { 10 | DispatchTick(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/UpdateTicker.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace Lowscope.ScriptableObjectUpdater 5 | { 6 | public class UpdateTicker : Ticker 7 | { 8 | private void Update() 9 | { 10 | DispatchTick(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/Ticker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 649c74d9f4c7ca04884f6b263241888a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/UpdateableEvent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc3267fbd0737b148b2bdc9e02449587 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Enums/EEventType.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0633efb573253cd4baa196e2cb901ead 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/AwakeTick.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: db0988e1619fa1f48827f54e45f341b6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/StartTick.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 832423da6401604478452ba96dd925eb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/UpdateTicker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aec49d9357c10ca4798d3da3093c5649 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/FixedUpdateTicker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b3eefeb3fd41d8d47a1916c9973ac7cc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/LateUpdateTicker.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace Lowscope.ScriptableObjectUpdater 5 | { 6 | public class LateUpdateTicker : Ticker 7 | { 8 | private void LateUpdate() 9 | { 10 | DispatchTick(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/LateUpdateTicker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4bf68f1fc5cc81c409c62572f097af66 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/InitializeableAssetContainer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7cc525aa4c3cead47befbc91e7d1a031 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Editor/ScriptableObjectInitializer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 047603513c0f271498a0c91793468c28 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/FixedUpdateTicker.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace Lowscope.ScriptableObjectUpdater 5 | { 6 | public class FixedUpdateTicker : Ticker 7 | { 8 | private void FixedUpdate() 9 | { 10 | DispatchTick(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Attribute/InitializeAssetMethodAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 45f6717d006f5f7459cdc9e428749210 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Editor/ScriptableObjectUpdaterConfiguration.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e156c37e1934b04d8c7bf4fce1b964f 3 | timeCreated: 1529617841 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Attribute/InitializeAssetMethodAttribute.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | 5 | namespace Lowscope.ScriptableObjectUpdater 6 | { 7 | [AttributeUsage(AttributeTargets.Method)] 8 | public class UpdateScriptableObjectAttribute : Attribute 9 | { 10 | public EEventType eventType { get; set; } 11 | public float Delay { get; set; } 12 | public float tickDelay { get; set; } 13 | public int ExecutionOrder { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Scriptable Object Updater/Tickers/Ticker.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace Lowscope.ScriptableObjectUpdater 6 | { 7 | public class Ticker : MonoBehaviour 8 | { 9 | public List Events = new List(); 10 | 11 | protected void DispatchTick() 12 | { 13 | for (int i = 0; i < Events.Count; i++) 14 | { 15 | Events[i].Tick(); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Updateable Scriptable Objects 2 | A methodology of calling Start, Awake update among other things in ScriptableObjects 3 | 4 | This package automatically searches through all scriptable objects for existing attributes during build time. 5 | Create a new GameObject at the first scene that invokes all the cached methods. 6 | 7 | Useage example: 8 | 9 | ![alt text](https://i.imgur.com/sUPrP6T.png) 10 | 11 | 12 | Define what folders to scan, to optimize building times: 13 | 14 | ![alt text](https://i.imgur.com/UK1dz68.png) 15 | 16 | 17 | If you like what I'm doing. Please support me by purchasing this package: 18 | [RPG Farming Kit](https://lowscope.itch.io/rpg-farming-kit) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alex Meesters (alexmeesters.nl) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Scriptable Object Updater/UpdateableEvent.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | 5 | namespace Lowscope.ScriptableObjectUpdater 6 | { 7 | [System.Serializable] 8 | public class UpdateableEvent 9 | { 10 | public UpdateableEvent(Action action, float updateRate, float delay, int executionOrder, InitializeableAssetContainer assetContainer) 11 | { 12 | this.tickableEvent = action; 13 | this.updateRate = updateRate; 14 | this.startDelay = delay; 15 | this.executionOrder = executionOrder; 16 | this.assetContainer = assetContainer; 17 | } 18 | 19 | private Action tickableEvent; 20 | 21 | private float updateRate; 22 | private float startDelay; 23 | private bool utilizingDelay; 24 | 25 | private float t; 26 | 27 | private InitializeableAssetContainer assetContainer; 28 | 29 | private int executionOrder; 30 | public int ExecutionOrder { get { return executionOrder; } } 31 | 32 | public void Tick() 33 | { 34 | if (startDelay != 0) 35 | { 36 | if (!utilizingDelay) 37 | { 38 | utilizingDelay = true; 39 | assetContainer.StartCoroutine(TickDelayed()); 40 | } 41 | return; 42 | } 43 | 44 | if (t <= 0) 45 | { 46 | tickableEvent.Invoke(); 47 | 48 | t = updateRate; 49 | } 50 | else 51 | { 52 | t -= Time.deltaTime; 53 | } 54 | } 55 | 56 | private IEnumerator TickDelayed() 57 | { 58 | yield return new WaitForSeconds(startDelay); 59 | tickableEvent.Invoke(); 60 | startDelay = 0; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Scriptable Object Updater/Editor/ScriptableObjectUpdaterConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEditor; 6 | using UnityEngine; 7 | 8 | namespace Lowscope.ScriptableObjectUpdater 9 | { 10 | public class ScriptableUpdaterConfiguration : EditorWindow 11 | { 12 | private bool searchSpecificFolders; 13 | 14 | [System.Serializable] 15 | public struct SearchPaths 16 | { 17 | public int arraySize; 18 | public List pathsToSearch; 19 | } 20 | 21 | private SearchPaths paths; 22 | 23 | [MenuItem("Tools/Scriptable Object Updater")] 24 | public static void ShowWindow() 25 | { 26 | GetWindow(false, "SO Updater", true); 27 | } 28 | 29 | private void OnEnable() 30 | { 31 | searchSpecificFolders = EditorPrefs.GetBool("SOUpdater_SearchSpecificFolders"); 32 | 33 | List searchPathList = GetSearchPaths(); 34 | paths = new SearchPaths() 35 | { 36 | arraySize = searchPathList.Count, 37 | pathsToSearch = searchPathList 38 | }; 39 | } 40 | 41 | void OnGUI() 42 | { 43 | EditorGUILayout.TextField("Scriptable Object Updater Configuration", EditorStyles.boldLabel); 44 | 45 | EditorGUILayout.TextField("To optimize the startup time, you can set it to only search for Scriptable Objects in specific folders", EditorStyles.label); 46 | EditorGUILayout.TextField("Example of a path is: Assets/ScriptableObjects", EditorStyles.label); 47 | 48 | EditorGUILayout.BeginVertical(EditorStyles.helpBox); 49 | 50 | EditorGUI.BeginChangeCheck(); 51 | 52 | searchSpecificFolders = EditorGUILayout.Toggle("Search Specific Folders", searchSpecificFolders); 53 | 54 | if (EditorGUI.EndChangeCheck()) 55 | { 56 | EditorPrefs.SetBool("SOUpdater_SearchSpecificFolders", searchSpecificFolders); 57 | } 58 | 59 | EditorGUI.BeginChangeCheck(); 60 | 61 | if (!searchSpecificFolders) 62 | { 63 | GUI.enabled = false; 64 | } 65 | 66 | EditorGUI.BeginChangeCheck(); 67 | 68 | paths.arraySize = EditorGUILayout.IntField("Paths", paths.arraySize); 69 | 70 | if (paths.pathsToSearch.Count < paths.arraySize) 71 | { 72 | paths.pathsToSearch.Add(""); 73 | } 74 | else 75 | { 76 | if (paths.pathsToSearch.Count > paths.arraySize) 77 | { 78 | string[] pathArray = paths.pathsToSearch.ToArray(); 79 | Array.Resize(ref pathArray, paths.arraySize); 80 | paths.pathsToSearch = pathArray.ToList(); 81 | } 82 | } 83 | 84 | EditorGUI.indentLevel++; 85 | for (int i = 0; i < paths.arraySize; i++) 86 | { 87 | paths.pathsToSearch[i] = EditorGUILayout.TextField("Path", paths.pathsToSearch[i]); 88 | } 89 | 90 | if (EditorGUI.EndChangeCheck()) 91 | { 92 | string savePathData = JsonUtility.ToJson(paths); 93 | EditorPrefs.SetString("SOUpdater_PathData", savePathData); 94 | } 95 | 96 | GUI.enabled = true; 97 | 98 | GUILayout.EndVertical(); 99 | } 100 | 101 | public static List GetSearchPaths() 102 | { 103 | string getPrefs = EditorPrefs.GetString("SOUpdater_PathData"); 104 | 105 | if (!string.IsNullOrEmpty(getPrefs)) 106 | { 107 | return JsonUtility.FromJson(getPrefs).pathsToSearch; 108 | } 109 | else 110 | { 111 | return null; 112 | } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /Scriptable Object Updater/Editor/ScriptableObjectInitializer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEditor; 4 | using UnityEditor.SceneManagement; 5 | using UnityEditor.Callbacks; 6 | using System.Reflection; 7 | using System; 8 | using UnityEngine.Events; 9 | using UnityEditor.Events; 10 | 11 | namespace Lowscope.ScriptableObjectUpdater 12 | { 13 | [System.Serializable] 14 | public class ScriptableObjectInitializer 15 | { 16 | private static BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; 17 | 18 | // This is required because using the PostProcessScene starts after Start() when using it in the editor 19 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] 20 | public static void OnLoad() 21 | { 22 | LoadItems(true); 23 | } 24 | 25 | [PostProcessSceneAttribute(-1)] 26 | public static void OnPostprocessScene() 27 | { 28 | if (BuildPipeline.isBuildingPlayer) 29 | { 30 | LoadItems(false); 31 | } 32 | } 33 | 34 | private static void LoadItems(bool testingInEditor) 35 | { 36 | int buildIndex = EditorSceneManager.GetActiveScene().buildIndex; 37 | 38 | bool canCreateAssetDatabase = (testingInEditor) ? true : (buildIndex == 0); 39 | 40 | // We only want to create this object at the first scene. 41 | if (canCreateAssetDatabase) 42 | { 43 | InitializeableAssetContainer startupEventContainer = null; 44 | 45 | if (BuildPipeline.isBuildingPlayer) 46 | { 47 | var scene = EditorSceneManager.GetActiveScene(); 48 | EditorSceneManager.MarkSceneDirty(scene); 49 | } 50 | 51 | GameObject assetContainer = new GameObject("Scriptable Object Initializer"); 52 | 53 | startupEventContainer = assetContainer.AddComponent(); 54 | 55 | string[] scriptableObjectGuids; 56 | 57 | if (EditorPrefs.GetBool("SOUpdater_SearchSpecificFolders")) 58 | { 59 | string[] lookPath = ScriptableUpdaterConfiguration.GetSearchPaths().ToArray(); 60 | scriptableObjectGuids = AssetDatabase.FindAssets("t:ScriptableObject", lookPath); 61 | 62 | if (scriptableObjectGuids.Length == 0) 63 | { 64 | Debug.Log("Could not find objects. Go to Tools > Scriptable Object Updater."); 65 | } 66 | } 67 | else 68 | { 69 | scriptableObjectGuids = AssetDatabase.FindAssets("t:ScriptableObject"); 70 | } 71 | 72 | for (int i = 0; i < scriptableObjectGuids.Length; i++) 73 | { 74 | string path = AssetDatabase.GUIDToAssetPath(scriptableObjectGuids[i]); 75 | 76 | var getObject = AssetDatabase.LoadAssetAtPath(path); 77 | 78 | if (getObject == null) 79 | continue; 80 | 81 | MethodInfo[] scriptableObjectMethods = getObject.GetType().GetMethods(bindingFlags); 82 | 83 | for (int i2 = 0; i2 < scriptableObjectMethods.Length; i2++) 84 | { 85 | // Check if a custom attribute is associated with a method 86 | UpdateScriptableObjectAttribute attribute = 87 | Attribute.GetCustomAttribute(scriptableObjectMethods[i2], 88 | typeof(UpdateScriptableObjectAttribute)) as UpdateScriptableObjectAttribute; 89 | 90 | // Continue searching methods for attributes if the current method has none. Or when it has more then 0 parameters. 91 | if (attribute == null || scriptableObjectMethods[i2].GetParameters().Length != 0) 92 | { 93 | continue; 94 | } 95 | 96 | if (scriptableObjectMethods[i2].IsPrivate) 97 | { 98 | #if NET_4_6 99 | Debug.Log($"Can only use UpdateScriptableObject on public methods. {getObject.name} has it set to private"); 100 | #else 101 | Debug.Log(String.Format("Can only use InitializeRuntimeAssetMethod on public methods. {0} has it set to private", getObject.name)); 102 | #endif 103 | continue; 104 | } 105 | 106 | 107 | #if NET_4_6 108 | UnityAction action = scriptableObjectMethods[i2].CreateDelegate(typeof(UnityAction), getObject) as UnityAction; 109 | #else 110 | UnityAction action = Delegate.CreateDelegate(typeof(UnityAction), getObject, scriptableObjectMethods[i2].Name) as UnityAction; 111 | #endif 112 | 113 | // Create a unityaction from the delegate, so that it can be added to a serialized game object 114 | 115 | 116 | AttachEventToContainer(startupEventContainer, attribute, action); 117 | } 118 | } 119 | 120 | // When the game is being built, set the objects that have changed serialized data dirty. 121 | // This ensures the changes made persist. 122 | if (BuildPipeline.isBuildingPlayer) 123 | { 124 | EditorUtility.SetDirty(assetContainer); 125 | EditorUtility.SetDirty(startupEventContainer); 126 | } 127 | else 128 | { 129 | startupEventContainer.InitializeEvents(true); 130 | } 131 | 132 | } 133 | } 134 | 135 | private static void AttachEventToContainer(InitializeableAssetContainer initAssetContainer, UpdateScriptableObjectAttribute attribute, UnityAction action) 136 | { 137 | InitializeableAssetContainer.EventData eventData = new InitializeableAssetContainer.EventData() 138 | { 139 | eventType = attribute.eventType, 140 | serializedEvent = new UnityEvent(), 141 | updateRate = attribute.tickDelay, 142 | delay = attribute.Delay, 143 | executionOrder = attribute.ExecutionOrder 144 | }; 145 | 146 | UnityEventTools.AddPersistentListener(eventData.serializedEvent, action); 147 | 148 | initAssetContainer.AddEvent(eventData); 149 | } 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /Scriptable Object Updater/InitializeableAssetContainer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.Collections.Generic; 4 | using UnityEngine.Events; 5 | using System.Linq; 6 | 7 | namespace Lowscope.ScriptableObjectUpdater 8 | { 9 | [System.Serializable] 10 | public class InitializeableAssetContainer : MonoBehaviour 11 | { 12 | [System.Serializable] 13 | public class EventData 14 | { 15 | public UnityEvent serializedEvent = new UnityEvent(); 16 | public EEventType eventType; 17 | public float updateRate; 18 | public float delay; 19 | public int executionOrder; 20 | 21 | public Action GetSerializedEventDelegate(int eventIndex) 22 | { 23 | UnityEngine.Object target = serializedEvent.GetPersistentTarget(eventIndex); 24 | System.Reflection.MethodInfo targetMethod = target.GetType().GetMethod(serializedEvent.GetPersistentMethodName(eventIndex)); 25 | 26 | #if NET_4_6 27 | return targetMethod.CreateDelegate(typeof(Action), target) as Action; 28 | #else 29 | return Delegate.CreateDelegate(typeof(Action), target, serializedEvent.GetPersistentMethodName(eventIndex)) as Action; 30 | #endif 31 | } 32 | } 33 | 34 | // Unity Events are more costly than C# delegates 35 | // So we only use the events for serialization purposes. 36 | 37 | [SerializeField] 38 | private List eventData = new List(); 39 | 40 | private AwakeTick awakeTicker; 41 | private StartTick startTicker; 42 | private UpdateTicker updateTicker; 43 | private FixedUpdateTicker fixedUpdateTicker; 44 | private LateUpdateTicker lateUpdateTicker; 45 | 46 | public void AddEvent(EventData data) 47 | { 48 | eventData.Add(data); 49 | } 50 | 51 | public void InitializeEvents(bool forceAwake) 52 | { 53 | for (int i = 0; i < eventData.Count; i++) 54 | { 55 | AssignEventData(eventData[i]); 56 | } 57 | 58 | OrderEventExecutions(); 59 | 60 | if (forceAwake && awakeTicker != null) 61 | { 62 | awakeTicker.Awake(); 63 | } 64 | } 65 | 66 | private void OrderEventExecutions() 67 | { 68 | if (awakeTicker != null) 69 | { 70 | awakeTicker.Events = awakeTicker.Events.OrderBy(u => u.ExecutionOrder).ToList(); 71 | } 72 | 73 | if (startTicker != null) 74 | { 75 | startTicker.Events = startTicker.Events.OrderBy(u => u.ExecutionOrder).ToList(); 76 | } 77 | 78 | if (updateTicker != null) 79 | { 80 | updateTicker.Events = updateTicker.Events.OrderBy(u => u.ExecutionOrder).ToList(); 81 | } 82 | 83 | if (fixedUpdateTicker != null) 84 | { 85 | fixedUpdateTicker.Events = fixedUpdateTicker.Events.OrderBy(u => u.ExecutionOrder).ToList(); 86 | } 87 | 88 | if (lateUpdateTicker != null) 89 | { 90 | lateUpdateTicker.Events = lateUpdateTicker.Events.OrderBy(u => u.ExecutionOrder).ToList(); 91 | } 92 | } 93 | 94 | private void AssignEventData(EventData data) 95 | { 96 | int eventCount = data.serializedEvent.GetPersistentEventCount(); 97 | 98 | for (int i = 0; i < eventCount; i++) 99 | { 100 | UpdateableEvent updateEventContainer = new UpdateableEvent 101 | ( 102 | data.GetSerializedEventDelegate(i) as System.Action, 103 | data.updateRate, 104 | data.delay, 105 | data.executionOrder, 106 | this 107 | ); 108 | 109 | switch (data.eventType) 110 | { 111 | case EEventType.Awake: 112 | 113 | if (awakeTicker == null) 114 | { 115 | awakeTicker = gameObject.AddComponent(); 116 | } 117 | 118 | AddEventToContainer(awakeTicker.Events, updateEventContainer); 119 | awakeTicker.Awake(); 120 | 121 | break; 122 | case EEventType.Start: 123 | 124 | if (startTicker == null) 125 | { 126 | startTicker = gameObject.AddComponent(); 127 | } 128 | 129 | AddEventToContainer(startTicker.Events, updateEventContainer); 130 | 131 | break; 132 | case EEventType.Update: 133 | 134 | if (updateTicker == null) 135 | { 136 | updateTicker = gameObject.AddComponent(); 137 | } 138 | 139 | AddEventToContainer(updateTicker.Events, updateEventContainer); 140 | 141 | break; 142 | case EEventType.FixedUpdate: 143 | 144 | if (fixedUpdateTicker == null) 145 | { 146 | fixedUpdateTicker = gameObject.AddComponent(); 147 | } 148 | 149 | AddEventToContainer(fixedUpdateTicker.Events, updateEventContainer); 150 | 151 | break; 152 | case EEventType.LateUpdate: 153 | 154 | if (lateUpdateTicker == null) 155 | { 156 | lateUpdateTicker = gameObject.AddComponent(); 157 | } 158 | 159 | AddEventToContainer(lateUpdateTicker.Events, updateEventContainer); 160 | 161 | break; 162 | default: 163 | break; 164 | } 165 | } 166 | } 167 | 168 | private static void AddEventToContainer(List target, UpdateableEvent updateEventContainer) 169 | { 170 | if (updateEventContainer == null) 171 | { 172 | return; 173 | } 174 | 175 | if (target == null) 176 | { 177 | target = new List(); 178 | } 179 | 180 | target.Add(updateEventContainer); 181 | } 182 | 183 | private void Awake() 184 | { 185 | InitializeEvents(false); 186 | } 187 | } 188 | 189 | } 190 | --------------------------------------------------------------------------------