I am familiar with using Platform Dependent Compilation in Unity to change how code compiles between editor/standalone/webgl/etc.
I've run into a weird, seemingly trivial case that I can't explain.
Given an empty Unity 5.6.1f1 (64bit windows) Project containing just the following script:
using UnityEngine;
public class TestA
{
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod] // Should be ignored for standalone
#else
[RuntimeInitializeOnLoadMethod]
#endif
static void OnInit() { Debug.Log("TEST A - This does NOT print in Standalone"); }
}
public class TestB
{
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod] // Should be ignored for standalone
#else
[RuntimeInitializeOnLoadMethod]
#endif
static void OnInit() { Debug.Log("TEST B - This DOES print in Standalone"); }
}
public class TestC
{
[RuntimeInitializeOnLoadMethod]
static void OnInit()
{
#if UNITY_EDITOR
Debug.Log("TEST C - UNITY_EDITOR is defined");
#else
Debug.Log("TEST C - UNITY_EDITOR is NOT defined");
#endif
}
}
When compiled and run in-editor I get the output that I would expect:

TestC shows that, when run in editor, the UNITY_EDITOR directive is defined as expected.However when compiled for Window standalone, TestA fails to print. Here is the relevant content of output_log.txt after running a standalone build containing only the above script:

TestA failed to print.TestC shows that when compiling for Window standalone, UNITY_EDITOR is not defined (as is expected) and therefore anything in an #if UNITY_EDITOR section should be ignored by the compiler when compiling for standalone.So why is there a difference between TestA and TestB? Why does TestA not print in the standalone compilation, but TestB does?
The only notable difference is that in TestA the #if UNITY_EDITOR define has the editor-only runtime init attribute, which is in a namespace that shouldn't even be available for compilation outside the editor (and the compiler would indeed complain if it was not within #if UNITY_EDITOR).
In other words, When compiling for standalone, only the sections between #else end #endif should be used in the compilation, and those two parts are identical in TestA and TestB
When compiling for standalone, It is my assumption that the above code should behave exactly the same as this code:
using UnityEngine;
public class TestA
{
[RuntimeInitializeOnLoadMethod]
static void OnInit() { Debug.Log("TEST A - This does NOT print in Standalone"); }
}
public class TestB
{
[RuntimeInitializeOnLoadMethod]
static void OnInit() { Debug.Log("TEST B - This DOES print in Standalone"); }
}
public class TestC
{
[RuntimeInitializeOnLoadMethod]
static void OnInit() { Debug.Log("TEST C - UNITY_EDITOR is NOT defined"); }
}
But when compiling this I do see all three lines in output_log.txt:

So something must be different.
What is going on here? Why is TestA not running in standalone, but TestB is?
I have submitted a bug report and the reply I received implies that it may be fixed in future Unity releases.
While the exact details of what causes the problem are not clear, this is what we know:
The main problem seems to be that the RuntimeInitializeOnLoadMethodAttribute (for whatever reason) does not function as expected when compiled inside a #define check (so using #if, #else, #if, etc) which would evaluate when the UNITY_EDITOR is false/undefined.
So for example, even this won't work:
#if !UNITY_EDITOR
public class SomeTest
{
[RuntimeInitializeOnLoadMethod]
static void OnInit() { Debug.Log("Does not show up"); }
}
#endif
Despite The class compiling, and the function existing and being callable from other classes (if it was public).
For [RuntimeInitializeOnLoadMethod] to work it seems like it must either be outside a #define check, or if inside, must exist in an identical fassion in both cases to work when UNITY_EDITOR evaluates to false.
So to get TestA in the original example to work (with in-editor using [InitializeOnLoadMethod] and standalone using [RuntimeInitializeOnLoadMethod]) you could do the following:
public class TestA
{
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod] static void EditorInitWrapper() { OnInit(); }
[RuntimeInitializeOnLoadMethod] static void RuntimeInitWrapper() { /* Do Nothing */ }
#else
[RuntimeInitializeOnLoadMethod] static void RuntimeInitWrapper() { OnInit(); }
#endif
static void OnInit() { Debug.Log("TEST A - This now prints in Standalone"); }
}
The wrappers call OnInit when necessary.
It should be noted that the [RuntimeInitializeOnLoadMethod] with the /* Do Nothing */ command is actually required and must have the same name in both the #if and the #else section for it to work. If the two don't match, then OnInit will not be called in the standalone build.
Alternatively you could avoid having to have the same function definition twice by moving it outside the #define check and wrapping the OnInit() call itself, though it's arguably less readable:
public class TestA
{
[RuntimeInitializeOnLoadMethod] static void RuntimeInitWrapper()
{
#if !UNITY_EDITOR
OnInit();
#endif
}
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
#endif
static void OnInit() { Debug.Log("TEST A - This now prints in Standalone"); }
}
Both of these will accomplish the exact behavior that would have been normally expected from TestA in the original example script.
Not an answer for the moment, but I wanted to add a thing. I tried your code, and got the same results.
Moreover, I wanted to check out the compiled code of the standalone build, using ILSpy, and oddly enough this is how ILSpy decompiled TestA class:
using System;
using UnityEngine;
public class TestA
{
[RuntimeInitializeOnLoadMethod]
private static void OnInit()
{
Debug.Log("TEST A - This does NOT print in Standalone");
}
}
So, now I'm more baffled than you: the compiled code is right (the preprocessor worked correctly), but it's not run as it should.
I'll think on it some more, but it feels like some very odd bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With