Search Unity

Detecting Unity via C# preprocessor directives?

Discussion in 'Scripting' started by aaronfranke, Jun 1, 2019.

  1. aaronfranke

    aaronfranke

    Joined:
    Jan 23, 2017
    Posts:
    20
    Hello, I'm working on a simple math library. I would like to be able to make it work simultaneously in both Unity and Godot, and perhaps other engines as well. The next release of Godot will have a preprocessor directive defined called GODOT, which will allow me to do this:

    Code (CSharp):
    1. #if GODOT
    2. GD.Print("This is Godot");
    3. #else
    4. throw new InvalidWorkflowException("Only Godot is supported");
    5. #endif
    I've found this article which covers specific defines within Unity, for example, detecting the editor or detecting platforms. But is there a way to just detect Unity and have it be defined on all export platforms?

    I would like to do something like this:

    Code (CSharp):
    1. #if GODOT
    2. GD.Print("This is Godot.");
    3. #elif UNITY
    4. print("This is Unity.");
    5. #else
    6. throw new InvalidWorkflowException("Only Godot and Unity are supported.");
    7. #endif
    Right now I think the closest thing would be UNITY_STANDALONE, but this only covers PC platforms, and doesn't cover if games are exported to mobile or consoles.

    EDIT: This works perfectly for all modern versions of Unity:

    Code (CSharp):
    1. #if GODOT
    2. GD.Print("This is Godot.");
    3. #elif UNITY_5_3_OR_NEWER
    4. print("This is Unity.");
    5. #else
    6. throw new InvalidWorkflowException("Only Godot and Unity are supported.");
    7. #endif
     
    Last edited: Jun 2, 2019
    jemonsuarez likes this.
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I'm not sure if that exists, but if nothing else, I imagine you could check for UNITY_X_Y_OR_NEWER for old values of X_Y and that will presumably be defined in all moderately recent versions of Unity and undefined on non-Unity platforms. This says it was added in Unity 5.3.4.

    You could also presumably check for UNITY_5, UNITY_4, UNITY_3, and UNITY_2 to detect as far back as version 2.6.
     
    aaronfranke likes this.
  3. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Preprocessor directives are used to physically cut sections of your code out of the library when it's compiled, based on settings. The most common example outside of game engines is #if DEBUG, for including or not including whole sections of script based on whether you're in a debug mode and want more/less logging. If you set a new define in the project settings, you then have to rebuild the project to get that code cut back out of the result again.

    Unity re-compiles the loose scripts in your project into libraries every time you change one (there are technically four DLLs it builds to by default, before using Assembly Definitions- one for Plugins/Editor, one for the rest of Plugins, one for the rest of Editor, and one for everything else), which is what the little load time is for when you go from your IDE back to the Unity application. When you build a library externally, and then drag the DLL into Unity or Godot, the compiling for that library is finished, past-tense, it's already a DLL. The code inside of the preprocessor directive areas either exists at that point, or doesn't, based on the defines you set in the IDE. No defines that are set in Unity matter at that point, the deal is done.

    You can't really do something like you're suggesting, as far as I'm aware. You could set your own BUILDING_FOR_UNITY define in Visual Studio or something, and build multiple versions of your library, but if your goal is to be able to set Unity library dependencies so that you can access the UnityEngine namespace in parts of your library (within those preprocessor-defined areas), you'll have to change the whole set of library dependencies for building for Unity or Godot- as far as I know, dependencies are completely independent of your configuration settings, and don't care what your preprocessor directives are.

    I could be wrong about that last point though- I haven't actually tried something like that. If you don't need Unity libraries though, just make your own define and use that to change the build target, and have multiple DLL versions. That's perfectly viable.
     
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I assumed the plan must be to copy the source code into various projects.
     
    aaronfranke likes this.
  5. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Yeah I dunno, I considered just attaching another project to the Unity solution, but last I checked that wasn't possible. Likewise moving the entire contents of the source code into projects doesn't really sound easier than just rebuilding the DLL using your own defines and copying that over instead. I'm having a bit of a difficult time imagining the workflow to make this worthwhile, so I fell back on just describing the whole process as well as I could (maybe there's some aspect being misunderstood) and hoping that sparked some ideas about alternative ways to go about it.

    I could just be thick though- I've been known to be =)
     
    Last edited: Jun 1, 2019
  6. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I'd probably try:
    Code (csharp):
    1. #if  (ENABLE_MONO || ENABLE_IL2CPP || ENABLE_DOTNET)
    If I'm correct, that should always resolve to true for Unity (at least for Unity 5.3 or later), and always false when not Unity.
     
    Last edited: Jun 1, 2019
    aaronfranke likes this.
  7. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    When I have source code that's shared between multiple projects, I use SVN externals. So there's a separate copy on my local machine for each project, but they're all linked to a single entry in version control; changing one version will cause all copies to receive the changes the next time they're updated.

    For instance, I used this technique recently in a game where some gameplay code was shared between the client (in Unity) and the server (ASP.NET Core).

    But this approach assumes that everyone sharing the code is in a single organization; if you're publishing a library for outside parties to use, this is a little more dubious.
     
    aaronfranke and DonLoquacious like this.
  8. aaronfranke

    aaronfranke

    Joined:
    Jan 23, 2017
    Posts:
    20
    The approach is indeed to copy the source code (or at the very least, allow doing this). I have no experience with compiling DLLs, and some places require posting source code anyway (such as Godot's asset library). I might look into DLLs in the future as well, but I want to be able to just use source files via copying or Git submodules.

    Code (CSharp):
    1. #if  (ENABLE_MONO || ENABLE_IL2CPP || ENABLE_DOTNET)
    This might work, but because it does not contain the word "Unity" in it, there's not really any guarantee that this won't be triggered somewhere else.

    The ideal scenario is for the Unity devs to add UNITY or UNITY_ENGINE to their list of preprocessor defines. Does anyone know where we can contact the Unity devs to get this added?

    And yes, I know that if this were added now it would take a few years to be useful (as you'd have to use newer versions of Unity for it to work), but I'm fine with that.
     
  9. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    As Antistone pointed, you should use UNITY_X_Y_OR_NEWER.

    For example, if the code works on any Unity starting from the first version of Unity 2018 it should be UNITY_2018_1_OR_NEWER.
     
    aaronfranke likes this.
  10. aaronfranke

    aaronfranke

    Joined:
    Jan 23, 2017
    Posts:
    20
    For the record, UNITY_5_3_OR_NEWER works perfectly in Unity 2019, and I believe that it is the farthest back preprocessor directive that covers all of Unity. I'll be using this in my code. Thanks everyone!

    Though, it would still be nice to replace the #if in my code with a simple UNITY or UNITY_ENGINE one day.
     
    Edy likes this.
  11. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I haven't done anything elaborate with preprocessor defines in C#, but in C++ if you wanted to have a more convenient way to refer to an existing define, you'd just add a bit in your own file that said something like

    Code (csharp):
    1. #ifdef UNITY_5_3_OR_NEWER
    2. #define UNITY
    3. #endif
    And then you could check for "UNITY" later on.

    Note that even if Unity added a new "UNITY" define in some future version, it will never be in older versions of Unity, so if you want to support those older versions you'd need to continue using UNITY_5_3_OR_NEWER anyway.