Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

How to deal with optional components and Version Defines (exclude components if define is not met)

Discussion in 'Package Manager' started by Xarbrough, Nov 4, 2019.

  1. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    546
    I've set up version defines for my custom package to deal with other packages being present or not. For example: Text Mesh Pro. I treat my own code which is coupled to Text Mesh Pro as an extension of my own package. I have a special component which used Text Mesh Pro.

    Using Version Defines, this setup seems to work fine when dealing with source code:

    upload_2019-11-4_13-40-53.png

    If a project has Text Mesh Pro enabled, the TEXTMESH_PRO define is enabled and it shows as such in Visual Studio. See this snipped:

    Code (CSharp):
    1. #if TEXTMESH_PRO
    2. using TMPro;
    3. using UnityEngine;
    4.  
    5. public class LocalizedText_TextMeshPro : LocalizedText
    6. {
    7.     [SerializeField]
    8.     private TMP_Text target;
    9.  
    10.     protected override void SetString(string text)
    11.     {
    12.         if (target != null)
    13.             target.text = text;
    14.     }
    15. }
    16. #endif
    If my own ASMDEF file detects Text Mesh Pro, the define is true, an my code is enabled or disabled if TMP is not present.

    However, this setup does not seem to work for components in the inspector:

    upload_2019-11-4_13-43-53.png

    Although the preprocessor define seems to be true when looking at the code in the IDE, the script cannot be loaded. If I remove the define statements around my MonoBehaviour class, the component can be loaded correctly.

    How do I deal with this issue?

    What I'd like to ideally, is the same as how Unity handles e.g. the Terrain or Physics components. If a user disables these modules, the components can still be added to GameObjects, but they are disabled and show a warning message, that they cannot be used. How do I reproduce this setup for my custom code?
     
  2. felipemunoz_unity

    felipemunoz_unity

    Unity Technologies

    Joined:
    Apr 5, 2019
    Posts:
    10
    One way I can envision doing this is to add a new class deriving from Editor so that you can override the OnInspectorGUI method.

    This code would need to go inside the same folder as an ASMDEF that creates your version define (i think you want to have different assembly definitions for your runtime code and your editor code so that you don't ship unneeded scripts).

    Doing this would allow you to display a warning when your define is not met:
    Code (CSharp):
    1. using UnityEditor;
    2.  
    3. [CustomEditor(typeof(SomeClass))]
    4. public class SomeClassEditor : Editor
    5. {
    6.     public override void OnInspectorGUI()
    7.     {
    8.         base.OnInspectorGUI();
    9.         #if !TEXTMESH_PRO
    10.             EditorGUILayout.HelpBox("TextMeshPro not there", MessageType.Info);
    11.         #endif
    12.     }
    13. }
    Then, on your runtime code, instead of placing the #if TEXTMESH_PRO around your whole script, just doing it around the pieces you need (or the whole inside of your monobehaviour) so that the base class linked to the editor code you created is always there:

    Code (CSharp):
    1. using UnityEngine;
    2. #if TEXTMESH_PRO
    3. using TMPro;
    4. #endif
    5.  
    6. public class SomeClass : MonoBehaviour
    7. {
    8.     #if TEXTMESH_PRO
    9.     [SerializeField]
    10.     public TMP_Text target;
    11.     #endif
    12. }
    This should yield you (With TMP installed):
    upload_2019-11-4_13-40-27.png

    And the following without it:
    upload_2019-11-4_13-41-10.png

    Some links that helped me figure this out:
    https://docs.unity3d.com/ScriptReference/Editor.OnInspectorGUI.html
    https://answers.unity.com/questions/1019430/show-a-warning-in-the-inspector.html
     
    Xarbrough likes this.
  3. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    546
    Thank you for the recommendations! This does seem to be working best so far.

    I still find it a little confusing that the preprocessor statements around the entire class causes loading of the component to fail in the inspector in any case (even if the define is true). It looks as if the defines are set after components are loaded by Unity, but well, it does work to not compile the code within the class.