I know this was aksed and answered a a couple of times e.g. Solution-wide #define, Is There anyway to #define Constant on a Solution Basis? and How to define a constant globally in C# (like DEBUG).
But in my case I can not use any of the suggested methods:
I'm writing on different "modules" (or plugins if you want so) for UnityProjects (kind of a package providing a certain functionality). The idea is that a developer can load a certain "module" to use in his project by importing a UnityPackage
with all scripts and resources in it.
But some of these modules themselves depend on other modules. So what I tried so far was having a class Constants
in each module with seperated namespaces and preprocessor definitions.
Module A
#if !MODULE_A
#define MODULE_A // BUT I WOULD NEED THIS GLOBAL NOT ONLY HERE
#endif
namespace Module_A
{
public static class Constants
{
// some constants for this namespace here
}
}
Module B
#if !MODULE_B
#define MODULE_B // BUT I WOULD NEED THIS GLOBAL NOT ONLY HERE
#endif
#if !MODULE_A // WILL BE NOT DEFINED OFCOURSE SINCE #define IS NOT GLOBAL
#error Module A missing!
#else
namespace Module_B
{
public static class Constants
{
// some constants for this namespace here
}
// and other code that might require Module A
}
#endif
But ofcourse this cannot work like this since #define
s are not global but only in the current file.
For this whole idea of modules and a simple "load your modules" I can not ask the user to first make changes to the project or solution settings how e.g. suggested by this answer but instead have to use only the (c#) resources that come imported with the UnityPackage (at least with my current know-how).
Is there any way to somehow set/define those constants for the entire Unity-Project by only importing the module's UnityPackage?
I could find a solution for 1 definition in Unity using Assets/msc.rsp. But this still wouldn't work for multiple modules since they would have to write into the same file.
After a lot of searches I've finally been able to put together a surprisingly simple solution I'ld like to share with you:
InitializeOnLoadMethod
Unity has an attribute [InitializeOnLoadMethodAttribute]
. It tells Unity to run a method as soon as
Scripting Define Symbols
Further Unity actually has project wide defines in the PlayerSettings
.
And the good part is: We also have access to them via scripting API:
So what I did now is the following
This module has no dependencies but just defines a "global define" in the PlayerSettings. I placed this script somewhere e.g. in Assets/ModuleA/Editor
(important is the last folder's name).
#if !MODULE_A
using System.Linq;
using UnityEditor;
namespace ModuleA
{
// Will be initialized on load or recompiling
internal static class Startup
{
[InitializeOnLoadMethod]
private static void Init()
{
// Get the current defines
// returns a string like "DEFINE_1;DEFINE_2;DEFINE_3"
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
// split into list just to check if my define is already there
var define = defines.Split(';').ToList();
if (!define.Contains("MODULE_A")
{
// if not there already add my define
defines += ";MODULE_A";
}
// and write back the new defines
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);
}
}
}
#endif
This module depends on Module A
. So itself defines a "global define" (so later Modules can check their dependecies on Module B
) but additionally it checks first, if Module A is imported. If Module A
is missing, it prints an error to the Debug Console.
(You could as well throw a compiler error using #error SOME TEXT
, but for some reason this is not capable of printing out the URL correctly so I decided for the Debug.LogError
)
I placed this script somewhere e.g. in Assets/ModuleB/Editor
#if !MODULE_B
using UnityEditor;
#if MODULE_A
using System.Linq;
#esle
using UnityEngine;
#endif
namespace ModuleB
{
internal static class Startup
{
[InitializeOnLoadMethod]
private static void Init()
{
#if !MODULE_A
Debug.LogError($"! Missing Module Dependency !\nThe module MODULE_B depends on the module MODULE_A.\n\nDownload it from https://Some.page.where./to.find.it/MyModules/ModuleA.unitypackage");
#else
// Add Compiler Define
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
var define = defines.Split(';').ToList();
if (!define.Contains("MODULE_B"))
{
defines += ";MODULE_B";
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);
#endif
}
}
}
#endif
So later in other scripts of Module B I have two options (both do basically the same)
I can either check everywhere #if MODULE_A
to check exactly the module this script relies on
or I can instead check #if MODULE_B
to rather check with one line if all dependecies are fulfilled since otherwise I don't define MODULE_B
.
On this way I can completely check all dependencies between certain modules which is awesome. The only two flaws I saw until now are:
MODULE_A
) looks like for every module and if it is changed in the future it has to be changed in all depending modules as wellBut well - which solution is perfect?