Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Referencing Asset Store plugins in a custom package

Discussion in 'Package Manager' started by c_andrews, Jun 15, 2023.

  1. c_andrews

    c_andrews

    Joined:
    Jan 26, 2015
    Posts:
    106
    Does anyone know of a way to use external plugins from the Asset Store within a custom package?

    We have our own custom package which contains code that we reuse across our apps. However as we are growing our package we would like to reference external packages. The problem comes with the Assembly Definition Asset as it is unable to reference the external scripts.

    Can anyone recommend a work around?

    I was thinking that I could create Scripting Define Symbols for our scripts however is there a way to create an editor script that detects which packages are in the project and automatically add the Symbols?

    Would this approach work? Has anyone done anything similar?

    Thanks in advance
     
  2. AxonGenesis

    AxonGenesis

    Joined:
    Jun 24, 2016
    Posts:
    81
    I've encountered the same issue and it's a tricky one. Perhaps someone knows a better answer, which I'd be interested in knowing as well.

    The issue as I understand it is that when you use an Assembly Definition, no namespaces or classes outside of that scope can be referenced unless the Assembly Definition for those items is referenced in your assembly. This however is a problem if the other codebase doesn't include an Assembly Definition to reference. While you could add one, it isn't a great solution to have to add assembly definitions to other 3rd party packages.

    My solution so far is to use a Scripting Define Symbol and in my case the code I'm referencing is freely licensed so I can include it as an add-on package in my own assembly, which users can optionally import.

    In regards to automatically adding defines, perhaps this thread might help:
    https://forum.unity.com/threads/scripting-define-symbols-access-in-code.174390/

    I am going to look into it a bit more as well. My goal is simply to make the setup as hassle free as possible and avoid end-users encountering compile errors.
     
    c_andrews likes this.
  3. AxonGenesis

    AxonGenesis

    Joined:
    Jun 24, 2016
    Posts:
    81
    This is the solution I've come up with for adding scripting define symbols and detecting if a namespace is defined. Tested in Unity 2020, 2021 and 2022.

    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Reflection;
    6. using UnityEditor;
    7.  
    8. public class EditorScriptingDefineUtils
    9. {
    10.     static public void UpdateSymbolIfNamespaceExists(string name_space, string symbol)
    11.     {
    12.         bool exists = NamespaceExists(name_space);
    13.         if (exists) {
    14.             AddScriptingDefineSymbol(symbol);
    15.         }
    16.         else {
    17.             RemoveScriptingDefineSymbol(symbol);
    18.         }
    19.     }
    20.  
    21.     static public bool NamespaceExists(string desiredNamespace)
    22.     {
    23.         foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
    24.             foreach (Type type in assembly.GetTypes()) {
    25.                 if (type.Namespace == desiredNamespace) {
    26.                     return true;
    27.                 }
    28.             }
    29.         }
    30.         return false;
    31.     }
    32.  
    33.     static public List<string> GetScriptingDefineSymbols()
    34.     {
    35.         string defined = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
    36.         if (string.IsNullOrEmpty(defined)) {
    37.             return new List<string>();
    38.         }
    39.         return defined.Split(';').ToList<string>();
    40.     }
    41.  
    42.     static public void AddScriptingDefineSymbol(string symbol)
    43.     {
    44.         List<string> defines = GetScriptingDefineSymbols();
    45.         if (!defines.Contains(symbol)) {
    46.             defines.Add(symbol);
    47.             SetScriptingDefineSymbols(defines);
    48.         }
    49.     }
    50.  
    51.     static public void RemoveScriptingDefineSymbol(string symbol)
    52.     {
    53.         List<string> defines = GetScriptingDefineSymbols();
    54.         if (defines.Contains(symbol)) {
    55.             defines.Remove(symbol);
    56.             SetScriptingDefineSymbols(defines);
    57.         }
    58.     }
    59.  
    60.     static public void SetScriptingDefineSymbols(List<string> defines)
    61.     {
    62.         string newDefines = "";
    63.         if (defines != null && defines.Count > 0) {
    64.             newDefines = string.Join(";", defines.ToArray());
    65.         }
    66.         PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, newDefines);
    67.     }
    68.  
    69. }
    70.  
    To implement this, in my case I want to give the user complete control over when the symbols are added so I am displaying a message in an editor UI with a setup button.

    Code (CSharp):
    1. // replace with your own symbol
    2. #if XYZ_SYMBOL
    3. // Symbol already defined, so nothing to do here
    4. #else
    5. // replace with the extension namespace
    6. if (EditorScriptingDefineUtils.NamespaceExists("XYZ_NAMESPACE")) {
    7.     if (GUILayout.Button("Setup")) {
    8.         if (EditorUtility.DisplayDialog("Setup", "To activate XYZ_NAMESPACE, the Scripting Define Symbols in Player Settings must be updated by adding the symbol XYZ_SYMBOL. Click Continue to automatically set it up and recompile scripts. ", "Continue", "Cancel")) {
    9.             EditorScriptingDefineUtils.AddScriptingDefineSymbol("XYZ_SYMBOL");
    10.         }
    11.     }
    12. }
    13. #endif
    14.  
    If you are referencing 3rd party packages from the asset store, once again you should first check to see if they provide an assembly definition, and if so reference it in your assembly. Otherwise, you will run into the issue that the namespace is unreachable from within your assembly. In that case, the only work around I know of is to add an assembly definition so that it can be referenced.

    Since assembly definition references are by GUID, it should retain the reference whether or not the 3rd party package is present.

    If anyone has a better solution to this, I'd love to know about it.
     
  4. c_andrews

    c_andrews

    Joined:
    Jan 26, 2015
    Posts:
    106
    Thanks that is all really useful. As you say the more tricky part when dealing with Assets from the Asset Store is the assembly definition file. I'll hopefully get another chance to play around with this a little more this week.

    Again if anyone has any other possible solutions that would be great.
     
    AxonGenesis likes this.