As far as I’m aware that’s not possible in C# in the same way as it is in C++. You can set up #defines in the Unity settings though (e.g. ‘Build Settings->Player Settings->Other Settings->Scripting Define Symbols’)
You can also set up a custom unity editor script to automatically change those define symbols, you could set it up to check if a file exists in the project and automatically set the define up.
Thank you for your input, but how am I supposed to define the symbol other than writing its name in the settings?
I’m new to this, as I’ve never done it before, not in C# nor in C++.
Unity is a bit special, as in it’s necessary to add the define directives to the text field inside the editor settings.
There are some methods to access and change it’s contents from code, but it has to be done from within an editor script.
I wrote two methods that make it a bit easier to manage adding and removing define directives:
using UnityEditor;
public static class EditorUtils
{
public static void AddDefineIfNecessary(string _define, BuildTargetGroup _buildTargetGroup)
{
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(_buildTargetGroup);
if (defines == null) { defines = _define; }
else if (defines.Length == 0) { defines = _define; }
else { if (defines.IndexOf(_define, 0) < 0) { defines += ";" + _define; } }
PlayerSettings.SetScriptingDefineSymbolsForGroup(_buildTargetGroup, defines);
}
public static void RemoveDefineIfNecessary(string _define, BuildTargetGroup _buildTargetGroup)
{
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(_buildTargetGroup);
if (defines.StartsWith(_define + ";"))
{
// First of multiple defines.
defines = defines.Remove(0, _define.Length + 1);
}
else if (defines.StartsWith(_define))
{
// The only define.
defines = defines.Remove(0, _define.Length);
}
else if (defines.EndsWith(";" + _define))
{
// Last of multiple defines.
defines = defines.Remove(defines.Length - _define.Length - 1, _define.Length + 1);
}
else
{
// Somewhere in the middle or not defined.
var index = defines.IndexOf(_define, 0, System.StringComparison.Ordinal);
if (index >= 0) { defines = defines.Remove(index, _define.Length + 1); }
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(_buildTargetGroup, defines);
}
}
To use those methods, add a folder named “Editor” to your project and in it create a C# file named “EditorUtils”.
Then just copy and paste the above code and you can use those methods in any editor script.
I use them to enable/disable my own plugins, which is very convenient if you can do that by pressing a button.
That’s great, could you also provide an example of an editor script which uses these methods?
Just to clarify how should I use them… (sorry, still very new to this side of Unity )
using UnityEngine;
using UnityEditor;
public class Definer : EditorWindow
{
[MenuItem("Tools/Definer")]
private static void OpenWindow()
{
const float wndWidth = 200.0f;
const float wndHeight = 200.0f;
var pos = new Vector2(0.5f * (Screen.currentResolution.width - wndWidth),
0.5f * (Screen.currentResolution.height - wndHeight));
var window = GetWindow<Definer>();
window.titleContent = new GUIContent("Definer");
window.position = new Rect(pos, new Vector2(wndWidth, wndHeight));
}
private void OnGUI()
{
if (GUILayout.Button("Enable"))
{
EditorUtils.AddDefineIfNecessary("THE_DEFINE", BuildTargetGroup.Standalone);
}
if (GUILayout.Button("Disable"))
{
EditorUtils.RemoveDefineIfNecessary("THE_DEFINE", BuildTargetGroup.Standalone);
}
}
}
Just paste this into a script named “Definer” inside of an “Editor” folder. You can then access this little tool via the main menu Tools->Definer. Open the player settings where the defines are and enjoy the text being altered when you press the buttons.
Oh I got it, your tool is pretty helpful, though what I wasn’t getting was the very obvious usage of symbols.
So if I put my script between #if MY_SYMBOL and #endif, it will only be compiled if that symbol is defined in the settings. I wonder why it took me so long to realize it
Thanks a lot ^^
It’s worth noting that, in a case where you only want to do something when a certain define is set, you can do either of the following:
#if THE_DEFINE
// Some code.
#endif
or:
[System.Diagnoistics.Conditional("THE_DEFINE")]
public void CallMeOnlyWhenDefined()
{
// Some code.
}
The method in the second case is not allowed to have a return value, because it might not even be called, depending on whether THE_DEFINE is defined, or not.
This does, of course, not work if you need something like:
#if THE_DEFINE
// Some code.
#else
// Some code for when THE_DEFINE is not defined.
#endif
@amirebrahimi_unity if I understand the source code correctly, this looks like a great way to detect 3rd party features and be able to write code that is compile-time dependent, but auto-compiled-out when they’re missing.
But I can’t find any description of the UnityLabsUtils package that this comes from - anywhere on the web! - or what the correct way is of embedding/referencing/using them. It seems it’s shipping as a package right now? Is there any official guide/info on using these classes? I noticed that some of the others are actively fixing / workingaround other bugs or missing features in core Unity APIs, and so I wonder if the utils package is fragile and expected to delete those over time once official Unity versions no longer need them?
Re: stop-gap - packages are useless to me until the AssetStore supports them fully - I’m still investigating this, but it seems like the support is now partial (last time I checked, we weren’t even allowed to upload packages to asset store!) - in particular: (from my current - possibly incorrect! - understanding) things like package-based conditional defines don’t work at all for AssetStore because every user has to re-do them locally themself.
So if CCU works with AssetStore (which it seems it does) and works in situations like that one above (which - if my reading of the source + github readme suggests it does) then it’s really the only viable solution for me right now :).
(I love packages, but I’ve been waiting multiple years for Unity to allow non-Unity staff to use them fully. It was frustrating that the policy seemed to be “stop using assetstore if you want packages”. (Unity without the AssetStore, and the vast amount of core + critical packages on there, which now make up a large part of the overall engine/platform, is an impossible idea to anyone doing game dev today :)). But - as noted - still investigating to see if that’s finally changed)
Sounds good. If you’re going to include this in your AssetStore package, then I suggest a fork/copy & paste with your own namespace, so it doesn’t conflict with anything else that might use CCU.
And agreed on the AssetStore/Packages front – I think we’re working on a better solution for that, but don’t hold me to it. Package Manager doesn’t really replace Asset Store, especially from a discovery standpoint.
Hey @amirebrahimi_unity , AutoLOD doesn’t seem to actually use the ‘OptionalDependencyAttribute’ anywhere - I’m having some trouble getting it to work correctly in my case.
I’m trying to detect whether a developer is using SteamVR, OVR or XR in the project.
For the definition of the attribute, I had to write a constructor for it to compile:
using System;
using System.Diagnostics;
using UnityEngine;
[Conditional("UNITY_CCU")] // | This is necessary for CCU to pick up the right attributes
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class OptionalDependencyAttribute : Attribute // | Must derive from System.Attribute
{
public string dependentClass; // | Required field specifying the fully qualified dependent class
public string define; // | Required field specifying the define to add
public OptionalDependencyAttribute(string dependentClass, string define)
{
this.dependentClass = dependentClass;
this.define = define;
}
}
UPDATE: Even for non-packages projects - plain, simple, default Unity projects - VersionDefines appear to be broken. Unity detects projects that use it, and wipes the data when you send to the Asset Store.
Welcome to corrupt packages that worked correctly on upload, and now fail in bizarre ways for anyone who downloads them!
My conclusion: CCU is currently the only way you can do conditional compilation in Unity.
(I submitted a bug report a month ago, haven’t had any response positive or negative. I’ve pinged QA again to see if there’s anything else I can do to help with it)
I tried it before, but didn’t publish anything with CCU. I’m experimenting with it now, since VersionDefines are (seemingly) useless :(, and will try to publish something to asset store in the next few days.
@a436t4ataf cool, let me know if you make some progress. I’m also trying to publish something to the asset store this weekend, I will let you know if I can get CCU to work.
Hi there. I must’ve missed this somehow. AutoLOD has been updated to be a package now and as such, no longer includes CCU.
To your question: if you were using an earlier version of AutoLOD and didn’t pull from github w/ submodules intact, then it would be missing OptionalDependencyAttribute.
However, the point here is that you could include your own OptionalDependencyAttribute within a custom namespace and decorate it with [Conditional(“UNITY_CCU”)], and then proceed to mark any optional dependencies in code with defines. I didn’t want to have every dev to have to include the CCU in their project to enable optional dependencies. For the dev that pulls multiple projects in and happens to have CCU, then everything should just work. Or, optionally, they could simply add the defines manually to their project settings and it would work fine that way, too.