- Home /
How to: Conditional use of UnityEditor In DLL?
I’m attempting to create a simple utility function DLL, but seem to be running into dependency issues in general and an argument exception specifically, when I try to “build&run” the project that uses this DLL.
The issue arises because I want certain code from the DLL executed when in the editor, and other code, when not in the editor (built project). E.g. using DestoryImmidiate()
when in the editor and not in play-mode, and Destroy()
otherwise.
I’ve attempted using the `Conditional` attribute in the DLL, as well as pre-processor pragmas('#if UNITY_EDITOR`). Alas, even with everything else compiling and running ok, the lack of UnityEditor.DLL being allowed in the output "build" prevents it from compiling and generates the following ArgumentException.
ArgumentException: The Assembly UnityEditor is referenced by EditorTools ('Assets/EyeEngines/EditorTools.dll'). But the dll is not allowed to be included or could not be found.
Is it possible to put conditional UnityEditor code like this into a DLL? If so, how?
One obvious workaround is to create TWO versions of the DLL (one with the UNITY_EDITOR pragma defined, one with it undefined), but unless Unity has some way to automatically use the right one, I don’t like it: too confusing for users of the dll (Initial tests of this method generated "class redefined" compilation errors in editor.)
@Bunny83 @Owen-Reynolds @tanoshimi @meat5000 Sorry to bug you, my favorite UA members, personally with this bump, feel free to ignore. The work-around solution I provided below is "the suck". Anyone have a better idea?
Answer by Glurth · Apr 30, 2017 at 12:07 AM
New and improved answer:
I finally figured out a better solution for this….
only TWO assemblies (DLL’s) are created, one to run in the player, one to run in the editor.
In the assembly that will run in the player, we define inside our class, a (few) public static delegate.
using UniyEngine;
/// <summary>
/// This class allows player dlls to compile in the player (where UnityEditor.dll may not be referenced) while accessing certain Editor-only Functions.
/// When NOT being run in the editor these functions will immidiately return with a default value.
/// But if running in the Editor, they will invoke the approate Editor-only function.
/// </summary>
static public class EditorAdapterFunctions
{
public delegate void DoSomethingReturnNothingDelegate();
public delegate string GetStringUsingObjectDelegate(Object o);
static public DoSomethingReturnNothingDelegate EditorRepaintAllViews = null;
static void RepaintAllViews()
{
if (EditorRepaintAllViews != null)
EditorRepaintAllViews();
}
static public GetStringUsingObjectDelegate GetAssetPathEditor = null;
static string GetAssetPath(Object obj)
{
if (GetAssetPathEditor != null)
return GetAssetPathEditor(obj);
return "";
}
static public DoSomethingReturnNothingDelegate RefreshHierarchyEditor = null;
static public void RefreshHierarchy()
{
if (RefreshHierarchyEditor != null)
RefreshHierarchyEditor();
}
}
These funtions will obviously invoke NOTHING, unless we do something else.
So next we define an EDITOR based class, that will be included in the Editor (only) dll.
using UnityEditor;
public class EditorAdapterFunctionsEditorComponent
{
[InitializeOnLoadMethod]
static private void Init()
{
EditorAdapterFunctions.EditorRepaintAllViews = UnityEditorInternal.InternalEditorUtility.RepaintAllViews;
EditorAdapterFunctions.GetAssetPathEditor = AssetDatabase.GetAssetPath;
EditorAdapterFunctions.RefreshHierarchyEditor = RefreshHierarchy;
}
static public void RefreshHierarchy()
{
EditorApplication.DirtyHierarchyWindowSorting();
EditorApplication.RepaintHierarchyWindow();
}
}
Now, when the Editor is loaded, this class simply assigns the Editor only functions to the delegates in our Non-editor class.
RESULT:
When the Editor DLL’s are NOT in use (like when building the final project), the delegate remains null, and the function is not invoked.
When the Editor DLL’s ARE in use, the editor function gets invoked via the assigned delegate.
OLD ANSWER: Here is one non-ideal workaround I came up with.
Define the class(es) TWICE; once for the editor dll, and once for the non-editor dll. (So, both DLL project's will have a reference to the same file)
I mentioned this in the OP. But, to avoid compilation errors, we define each version of the class in different namespaces, using the compile-time pragma.
(DLL code)
#if UNITY_EDITOR
namespace UnityExtensionsEditor //the editor version DLL will compile this
#else
namespace UnityExtensions //the non-editor version DLL will compile this
#endif
class GameObjectExtensions....
Now, in the using-project, the user can simply specify:
(using-project code)
#if UNITY_EDITOR
using UnityEditor;
using UnityExtensionsEditor //the editor version of the project will compile this
#else
using UnityExtensions //the non-editor version of the project will compile this
#endif
This way, the appropriate GameObjectExtensions class (either UnityExtensionsEditor.GameObjectExtensions
or UnityExtensions.GameObjectExtensions
) will be used in the compiled (using-project) code.
I feel like the fact that I'm defining the same class name(s) in two different namespaces, is going to cause problems. Is it acceptable in this case?
Note: Using the (DLL code) above directly in a unity project will lead to errors unless a copy (yuk) of the file is put in the unity project's editor folder.
Also worth noting is that BOTH DLL's will need to go in a non-editor folder of the using-project.
While the delegate is one way to change how the code works based on an external state, i don't quite see the advantage here. You have a runtime check each time you would call your "EditorDestory". This could be handled easily by a simple static bool flag which you set when you are in the editor. DestroyImmediate is not an editor only function.
Since you already have a runtime check when executing your code you could even directly query Application.isEditor. The main point why having conditional compilation is to avoid runtime checks and have specialized code for each condition.
However i do agree that the usage of actual editor-only functions can be solved by using a bridge / adapter / strategy pattern (a delegate is basically a simple form of those patterns, just for a single method).
"The main point why having conditional compilation is to avoid runtime checks and have specialized code for each condition." Right, but this creates issues when attempting to generate DLLs, that can be referenced with EITHER condition.
Indeed, I see now that DestroyImmidiate is a poor example (thanks for pointing out how to simplify that!). I've now also implemented this for use with EditorApplication.update, which probably would have made a better example...
FYI: Changed example to use some ACTUAL editor-only functions, rather than DestoryImmidiate.
Answer by tMahon · Apr 29, 2017 at 10:12 PM
#if UNITY_STANDALONE
//do stuff
#endif
#if UNITY_EDITOR
//do other stuff
#endif
I'm not sure how this answers the question. Putting pragma's in the DLL source does not resolve the issue, (since they are not recompiled when the project that uses them is built). Putting pragmas in the final project does not help since the exception is based on the DLL.
Your answer
Follow this Question
Related Questions
Why can't I build Web Player in Unity while I have no problems with building standalone versions? 2 Answers
DLL plugins unsupported for Android Application Building 0 Answers
MonoDevelop DLL Build : Problem with default parameters 1 Answer
Build error when using BooleanOps.dll 1 Answer
Metadata file Debug\Assembly-UnityScript-firstpass.dll' could not be found 4 Answers