├── Editor.meta ├── Editor ├── DomainReloadHandler.cs ├── DomainReloadHandler.cs.meta ├── DomainReloadHelper.Editor.asmdef └── DomainReloadHelper.Editor.asmdef.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── DomainReloadAttributes.cs ├── DomainReloadAttributes.cs.meta ├── DomainReloadHelper.Runtime.asmdef └── DomainReloadHelper.Runtime.asmdef.meta ├── package.json └── package.json.meta /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a91ef3f9d42ff64429cd6d2db5670506 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/DomainReloadHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | using System.Reflection; 6 | using UnityEditor; 7 | using UnityEngine.Profiling; 8 | 9 | public class DomainReloadHandler 10 | { 11 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 12 | private static void OnRuntimeLoad() 13 | { 14 | Profiler.BeginSample("DomainReloadHandler"); 15 | int clearedValues = 0; 16 | int executedMethods = 0; 17 | 18 | /* Clear on reload */ 19 | foreach(FieldInfo field in TypeCache.GetFieldsWithAttribute()) 20 | { 21 | if (field != null && !field.FieldType.IsGenericParameter && field.IsStatic) 22 | { 23 | Type fieldType = field.FieldType; 24 | 25 | // Extract attribute and access its parameters 26 | var reloadAttribute = field.GetCustomAttribute(); 27 | if (reloadAttribute == null) 28 | continue; 29 | object valueToAssign = reloadAttribute.valueToAssign; 30 | bool assignNewTypeInstance = reloadAttribute.assignNewTypeInstance; 31 | 32 | // Use valueToAssign only if it's convertible to the field value type 33 | object value = null; 34 | if (valueToAssign != null) { 35 | value = Convert.ChangeType(valueToAssign, fieldType); 36 | if (value == null) 37 | Debug.LogWarning("Unable to assign value of type {valueToAssign.GetType()} to field {field.Name} of type {fieldType}."); 38 | } 39 | 40 | // If assignNewTypeInstance is set, create a new instance of this type and assign it to the field 41 | if (assignNewTypeInstance) 42 | value = Activator.CreateInstance(fieldType); 43 | 44 | try { 45 | field.SetValue(null, value); 46 | clearedValues++; 47 | } 48 | catch { 49 | if (valueToAssign == null) 50 | Debug.LogWarning("Unable to clear field {field.Name}."); 51 | else 52 | Debug.LogWarning("Unable to assign field {field.Name}."); 53 | } 54 | 55 | } else { 56 | Debug.LogWarning("Inapplicable field {field.Name} to clear; must be static and non-generic."); 57 | } 58 | 59 | } 60 | 61 | /* Execute on reload */ 62 | foreach(MethodInfo method in TypeCache.GetMethodsWithAttribute()) 63 | { 64 | if (method != null && !method.IsGenericMethod && method.IsStatic) 65 | { 66 | method.Invoke(null, new object[] { }); 67 | executedMethods++; 68 | } 69 | } 70 | Debug.Log($"Cleared {clearedValues} members; executed {executedMethods} methods."); 71 | 72 | Profiler.EndSample(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Editor/DomainReloadHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8f366dfaa838b4841868c414c238ec71 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/DomainReloadHelper.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DomainReloadHelper.Editor", 3 | "references": [ 4 | "GUID:90dcd41b2020c2c429fb2d91a618c51c" 5 | ], 6 | "includePlatforms": [ 7 | "Editor" 8 | ], 9 | "excludePlatforms": [], 10 | "allowUnsafeCode": false, 11 | "overrideReferences": false, 12 | "precompiledReferences": [], 13 | "autoReferenced": true, 14 | "defineConstraints": [], 15 | "versionDefines": [], 16 | "noEngineReferences": false 17 | } -------------------------------------------------------------------------------- /Editor/DomainReloadHelper.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 72b3ab3029c76b0439d73246b45e824d 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2021 Josh Steinhauer 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 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1ed87529b14a305478ca42cbd9b6b74d 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityDomainReloadHelper 2 | 3 | A couple of attributes that help when [Domain Reloading](https://docs.unity3d.com/2019.3/Documentation/Manual/DomainReloading.html) is disabled, which significantly decreases the time it takes for Unity to play a scene. By default Unity provides [RuntimeInitializeOnLoadMethod](https://docs.unity3d.com/Manual/DomainReloading.html) attribute to assist but it can be a little cumbersome. Here are a few helpful additions! 4 | 5 | ## ClearOnReloadAttribute 6 | Use the `ClearOnReload` attribute on static fields that you wish to reset on playmode. You can either "clear" the field (set the value to default), set it to a specified value, or make it assign itself a new instance of its type using a default constructor. 7 | 8 | ## ExecuteOnReloadAttribute 9 | Use the `ExecuteOnReload` attribute on static methods that you want to execute when entering play mode with domain reloading disabled. 10 | 11 | ### Examples 12 | ```csharp 13 | public class CharacterManager : MonoBehaviour 14 | { 15 | // Will set value to default (null). 16 | [ClearOnReload] 17 | static CharacterManager instance; 18 | 19 | // Will set variable to given value (10). 20 | [ClearOnReload(valueToAssign=10)] 21 | static int startsAsTen; 22 | 23 | // Will reset value, creating a new instance using default constructor. 24 | [ClearOnReload(assignNewTypeInstance=true)] 25 | static CharacterManager myNeverNullManager; 26 | 27 | // Will execute this method. 28 | [ExecuteOnReload] 29 | static void RunThis() 30 | { 31 | Debug.Log("Clean up here.") 32 | } 33 | 34 | // Does not work on properties! 35 | // [ClearOnReload] 36 | static int number { get; set; } 37 | 38 | // Does not work on events! 39 | // [ClearOnReload] 40 | static event Action onDoSomething; 41 | 42 | // However, one can use ExecuteOnReload to do their own clean up. 43 | [ExecuteOnReload] 44 | static void CleanUpEvents() 45 | { 46 | foreach(Delegate d in onDoSomething.GetInvocationList()) 47 | onDoSomething -= d; 48 | 49 | } 50 | } 51 | ``` 52 | 53 | ## FAQ 54 | 55 | - Why not support clearing properties and events? 56 | 57 | [TypeCache](https://docs.unity3d.com/ScriptReference/TypeCache.html) makes finding attributes fast; however, it only supports finding fields, methods, types, and derived types. 58 | 59 | ## License 60 | 61 | This project is released under the MIT license. 62 | 63 | ## Acknowledgments 64 | 65 | This [project](https://github.com/joshcamas/unity-domain-reload-helper) is written by [Josh Steinhauer](https://twitter.com/joshcamas) with contributions from [Yevhen Bondarenko](https://github.com/JGroxz) and [Shane Celis](https://twitter.com/shanecelis). 66 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c2c1941a3746944428fa5181342cc655 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de45a39de6e6ef94490b93fb1d88a1d0 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/DomainReloadAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | [AttributeUsage(AttributeTargets.Field)] 4 | public class ClearOnReloadAttribute : Attribute 5 | { 6 | public readonly object valueToAssign; 7 | public readonly bool assignNewTypeInstance; 8 | 9 | /// 10 | /// Marks field, property or event to be cleared on reload. 11 | /// 12 | public ClearOnReloadAttribute() 13 | { 14 | this.valueToAssign = null; 15 | this.assignNewTypeInstance = false; 16 | } 17 | 18 | /// 19 | /// Marks field of property to be cleared and assigned given value on reload. 20 | /// 21 | /// Explicit value which will be assigned to field/property on reload. Has to match field/property type. Has no effect on events. 22 | public ClearOnReloadAttribute(object valueToAssign) 23 | { 24 | this.valueToAssign = valueToAssign; 25 | this.assignNewTypeInstance = false; 26 | } 27 | 28 | /// 29 | /// Marks field of property to be cleared or re-initialized on reload. 30 | /// 31 | /// If true, field/property will be assigned a newly created object of its type on reload. Has no effect on events. 32 | public ClearOnReloadAttribute(bool assignNewTypeInstance = false) 33 | { 34 | this.valueToAssign = null; 35 | this.assignNewTypeInstance = assignNewTypeInstance; 36 | } 37 | } 38 | 39 | [AttributeUsage(AttributeTargets.Method)] 40 | public class ExecuteOnReloadAttribute : Attribute 41 | { 42 | /// 43 | /// Marks method to be executed on reload. 44 | /// 45 | public ExecuteOnReloadAttribute() {} 46 | } -------------------------------------------------------------------------------- /Runtime/DomainReloadAttributes.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 428bed064b603ad40abb302300d6d258 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/DomainReloadHelper.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DomainReloadHelper.Runtime", 3 | "references": [], 4 | "includePlatforms": [], 5 | "excludePlatforms": [], 6 | "allowUnsafeCode": false, 7 | "overrideReferences": false, 8 | "precompiledReferences": [], 9 | "autoReferenced": true, 10 | "defineConstraints": [], 11 | "versionDefines": [], 12 | "noEngineReferences": false 13 | } -------------------------------------------------------------------------------- /Runtime/DomainReloadHelper.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 90dcd41b2020c2c429fb2d91a618c51c 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.jsteinhauer.unitydomainreloadhelper", 3 | "version": "1.0.0", 4 | "displayName": "Unity Domain Reload Helper", 5 | "description": "A couple of attributes that help disabling Domain Reloading in Unity easier.", 6 | "unity": "2019.3", 7 | "keywords": [ 8 | "domain", 9 | "reload", 10 | "helper", 11 | "unity" 12 | ], 13 | "author": { 14 | "name": "Joshua Steinhauer", 15 | "email": "joshcamas@gmail.com", 16 | "url": "https://jsteinhauer.com/" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d60b8b3429ff6df47a9a9382ef733e23 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------