Thanks for the response! I was finally able to sit down with it for a while and your response gave me a great place to start.
I was able to get it working with this:
using UnityEditor.PackageManager;
sealed class CheckForTextMeshPro {
const string tmpDefine = "TMP_PRESENT";
[UnityEditor.Callbacks.DidReloadScripts]
static void CheckForTMPPackage(){
var asyncRequest = Client.List(true);
while (!asyncRequest.IsCompleted){} //Idle loop
foreach (var package in asyncRequest.Result){
if (package.displayName == "TextMesh Pro"){
if (package.status == PackageStatus.Available)
DefineUtilities.AddDefine(tmpDefine, DefineUtilities.GetValidBuildTargets());
else
DefineUtilities.RemoveDefine(tmpDefine, DefineUtilities.GetValidBuildTargets());
return;
}
}
}
}
But after milling it over a bit I figured that this solution wasn’t greatly elegant and that it wouldn’t work with Unity versions older than 2018, so I figured: why not just check for the existence of a namespace?
sealed class CheckForTextMeshPro {
const string tmpDefine = "TMP_PRESENT";
[UnityEditor.Callbacks.DidReloadScripts]
static void CheckForTMPro(){
var namespaceFound = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where type.Namespace == "TMPro"
select type).Any();
if (namespaceFound)
DefineUtilities.AddDefine(tmpDefine, DefineUtilities.GetValidBuildTargets());
else
DefineUtilities.RemoveDefine(tmpDefine, DefineUtilities.GetValidBuildTargets());
}
}
This works like a charm and is significantly faster (4ms vs 250-500ms), which might be relevant since it runs every script reload. Also might be worth doing an “if (EditorApplication.isPlayingOrWillChangePlaymode) return;” at the start of the method since reload also happens when entering play mode.
And let’s of course not forget to include my DefineUtilities class:
public static class DefineUtilities {
/// <summary>
/// ScriptingDefineSymbols are separated into a collection by any of these,
/// but always written back using index 0.
/// </summary>
public static char[] separators = { ';', ' ' };
public static void AddDefine(string _define, IEnumerable<BuildTargetGroup> _buildTargets){
foreach (var target in _buildTargets){
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(target).Trim();
var list = defines.Split(separators)
.Where(x => !string.IsNullOrEmpty(x))
.ToList();
if (list.Contains(_define))
continue;
list.Add(_define);
defines = list.Aggregate((a, b) => a + separators[0] + b);
PlayerSettings.SetScriptingDefineSymbolsForGroup(target, defines);
}
}
public static void RemoveDefine(string _define, IEnumerable<BuildTargetGroup> _buildTargets){
foreach (var target in _buildTargets){
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(target).Trim();
var list = defines.Split(separators)
.Where(x => !string.IsNullOrEmpty(x))
.ToList();
if (!list.Remove(_define)) //If not in list then no changes needed
continue;
defines = list.Aggregate((a, b) => a + separators[0] + b);
PlayerSettings.SetScriptingDefineSymbolsForGroup(target, defines);
}
}
public static IEnumerable<BuildTargetGroup> GetValidBuildTargets(){
return Enum.GetValues(typeof(BuildTargetGroup))
.Cast<BuildTargetGroup>()
.Where(x => x != BuildTargetGroup.Unknown)
.Where(x => !IsObsolete(x));
}
public static bool IsObsolete(BuildTargetGroup group){
var obsoleteAttributes = typeof(BuildTargetGroup)
.GetField(group.ToString())
.GetCustomAttributes(typeof(ObsoleteAttribute), false);
return obsoleteAttributes != null && obsoleteAttributes.Length > 0;
}
}
For includes, these cover all the classes:
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEditor;
On that topic, you wouldn’t happen to know the difference between DidReloadScripts and InitializeOnLoadMethod? From my testing I have been unable to tell any difference between the two, and the cryptic info in the docs isn’t helping much.
Been googling for this, but can’t seem to find any info on how to write ‘import’ scripts. Are you referring to some kind of AssetPostprocessor or ScriptedImporter? Because those don’t seem quite like they would be useful for this sort of thing.