Configurable Enter Play Mode Compatibility
The documentation has moved to: https://mitschmr-studios.io/documentation/api-guides/configurableenterplaymodecompatibility.html
This version will no longer be updated and maintained.
With 2019.3, Unity released a brand new feature called the Configurable Enter Play Mode, which can drastically reduce the time it takes to enter the play mode inside the Unity Editor. Read more about it on the page linked above or the blog post.
The two options, namely Reload Domain and Reload Scene, either disable the resetting of the script state or the scene reload when enter play mode. Naturally, this brings some problems with itself.
When disabling this feature, static fields do not get reset and static event handlers will not unregister methods, meaning that when entering the play mode the methods get registered again. This may be very undesired and can cause troubles.
Disabling this feature makes Unity no longer destroying all existing scene gameobjects and reloading everything from disk, but only recreates the modified content of the scene. This has the consequence that certain ScriptableObject and MonoBehaviour fields keep their values and that scripts that use the
ExecuteInEditMode
or ExecuteAlways
attribute do not receive OnDestroy
or Awake
calls. Game Creator itself uses not only static fields but also the
ExecuteInEditMode
attribute, meaning that there are errors thrown when enabling either of these options. We will tackle this in this guide, read how to below. This tutorial uses Unity 2019.4.24f1 and the latest versions of GC and the modules at this point (GC v1.1.12, Inventory v1.1.3, Quests v1.0.2, Stats v1.1.3, 04/20/2021).
First we need to analyze which classes in Game Creator and the modules use either of the mentioned things above. These are the following:
Game Creator standalone:
- AudioManager
- Character
- CharacterHeadTrack
- CoroutinesManager
- EventDispatchManager
- EventSystemManager
- GlobalID
- GlobalVariablesManager
- LocalizationManager
- PlayerCharacter
- PoolManager
- RememberActive
- SaveLoadManager
- SimpleMessageManager
- Singleton<T>
- TimeManager
- TouchStickManager
Inventory:
- InventoryManager
- MerchantManager
Quests:
- QuestsManager
Stats:
- Stats
3rd party modules:
The safest way to know which classes are affected is by asking the module owner. Still, you can also find it out by yourself. Open the
GlobalID
script with your scripting environment (like Visual Studio) and check the references of the class. It is very unlikely for a module to use the ExecuteInEditMode
attribute. Once found out, follow the instructions below. The first thing we want to do is change the base class of every manager class, because there are 14 deriving from
Singleton<T>
. We add the following code after the "Constructor" comment and before the [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
attribute: // Singleton.cs
// CONSTRUCTOR: ---------------------------------------------------------------------------
// Code to add start
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
protected static void Init()
{
instance = null;
IS_EXITING = false;
}
// Code to add end
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void OnRuntimeStartSingleton()
{
IS_EXITING = false;
}
What does this code do? When the Reload Domain feature is unchecked, the attribute
RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
is called, the method Init
gets executed which resets the static variable instance
and sets the IS_Exiting
bool variable to false. Normally, the second one is not necessary, but with Reload Scene disabled, the method OnRuntimeStartSingleton
is not executed and thus we need to set it. The next point on the list is to add the following code to each of the manager classes:
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Initialize()
{
<className>.Init();
}
Replace <className> with the name of the class of the manager. Here are a few examples:
// TimeManager.cs
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Initialize()
{
TimeManager.Init();
}
// EventDispatchManager.cs
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
public static void Initialize()
{
EventDispatchManager.Init();
}
What does this code do? As we have seen before, when Reload Domain is disabled, the attribute in the code is called. The
<className>.Init();
tells the manager to be initialized (reset) when the play mode is entered. Game Creator and the modules feature a variety of components to add to the gameobjects. But the base class,
GlobalID
, also uses the ExecuteInEditMode
attribute. This means we can't use the Awake
method. We need to change it like this for all classes that derive from GlobalID
: - 1.Add a private boolean variable called
initialized<className>
, i.e.initializedStats
, with the value false - 2.If there is an
Awake
method, rename it toOnEnable
- 3.If you are not in the
GlobalID
class, add theOverride
keyword after the modifier if it is not already there - 4.Move the code in the renamed method behind an if condition that checks for the negative value of the newly created boolean variable
- 5.If there already is an
OnEnable
method, merge the two of them together, whereas the code of the oldOnEnable
method after the closing of the if condition comes
Let's take a look at an example:
// GlobalID.cs
// ----- Old Code -----
// Old code, will be changed
protected virtual void Awake()
{
this.CreateGuid();
}
// ----- New Code -----
// Add the private boolean variable
private bool initializedGlobalId = false;
// Rename method, add if condition
protected virtual void OnEnable()
{
if (!this.initializedGlobalId)
{
this.CreateGuid();
this.initializedGlobalId = true;
}
}
Another example with existing
OnEnable
method: // RememberActive.cs
// ----- Old Code -----
// Old code, will be changed
protected override void Awake()
{
base.Awake();
if (!Application.isPlaying || this.exitingApplication) return;
this.defaultState = this.gameObject.activeSelf;
}
// Old code, will be merged
private void OnEnable()
{
if (!Application.isPlaying || this.exitingApplication) return;
this.UpdateState();
}
// ----- New Code -----
// Add private boolean variable
private bool initializedRememberActive = false;
// Rename method, add if condition, merge methods
protected override void OnEnable()
{
if (!this.initializedRememberActive)
{
base.OnEnable();
if (!Application.isPlaying || this.exitingApplication) return;
this.defaultState = this.gameObject.activeSelf;
this.initializedRememberActive = true;
}
if (!Application.isPlaying || this.exitingApplication) return;
this.UpdateState();
}
Problem: You may experience the triggers On Mouse Left Click, On Mouse Middle Click and On Mouse Right Click (maybe some other triggers as well) to not be working when Reload Scene is deactivated.
Answer: Make sure to add the components
Physics Raycaster
and Physics 2D Raycaster
to the main camera. When Reload Scene is deactived, Unity doesn't check the gameobjects whether they have dependencies to the named components and thus won't add them. Last modified 2mo ago