#if UNITY_EDITOR using UnityEngine; using UnityEditor; using System.Linq; namespace Crosstales.Common.EditorUtil { /// Base for various Editor helper functions. public abstract class BaseEditorHelper : Crosstales.Common.Util.BaseHelper { #region Static variables private static readonly System.Type MODULE_MANAGER = System.Type.GetType("UnityEditor.Modules.ModuleManager,UnityEditor.dll"); private static readonly System.Reflection.MethodInfo isPlatformSupportLoaded = MODULE_MANAGER.GetMethod("IsPlatformSupportLoaded", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); private static readonly System.Reflection.MethodInfo getTargetStringFromBuildTarget = MODULE_MANAGER.GetMethod("GetTargetStringFromBuildTarget", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); private static Texture2D logo_asset_bwf; private static Texture2D logo_asset_dj; private static Texture2D logo_asset_fb; private static Texture2D logo_asset_oc; private static Texture2D logo_asset_radio; private static Texture2D logo_asset_rtv; private static Texture2D logo_asset_tb; private static Texture2D logo_asset_tpb; private static Texture2D logo_asset_tps; private static Texture2D logo_asset_tr; private static Texture2D logo_ct; private static Texture2D logo_unity; private static Texture2D icon_save; private static Texture2D icon_reset; private static Texture2D icon_refresh; private static Texture2D icon_delete; private static Texture2D icon_folder; private static Texture2D icon_plus; private static Texture2D icon_minus; private static Texture2D icon_manual; private static Texture2D icon_api; private static Texture2D icon_forum; private static Texture2D icon_product; private static Texture2D icon_check; private static Texture2D social_Discord; private static Texture2D social_Facebook; private static Texture2D social_Twitter; private static Texture2D social_Youtube; private static Texture2D social_Linkedin; private static Texture2D video_promo; private static Texture2D video_tutorial; private static Texture2D icon_videos; private static Texture2D icon_3p_assets; private static Texture2D asset_PlayMaker; private static Texture2D asset_VolumetricAudio; private static Texture2D asset_rocktomate; private static Texture2D asset_RTFB; #endregion #region Static properties public static Texture2D Logo_Asset_BWF => loadImage(ref logo_asset_bwf, "logo_asset_bwf.png"); public static Texture2D Logo_Asset_DJ => loadImage(ref logo_asset_dj, "logo_asset_dj.png"); public static Texture2D Logo_Asset_FB => loadImage(ref logo_asset_fb, "logo_asset_fb.png"); public static Texture2D Logo_Asset_OC => loadImage(ref logo_asset_oc, "logo_asset_oc.png"); public static Texture2D Logo_Asset_Radio => loadImage(ref logo_asset_radio, "logo_asset_radio.png"); public static Texture2D Logo_Asset_RTV => loadImage(ref logo_asset_rtv, "logo_asset_rtv.png"); public static Texture2D Logo_Asset_TB => loadImage(ref logo_asset_tb, "logo_asset_tb.png"); public static Texture2D Logo_Asset_TPB => loadImage(ref logo_asset_tpb, "logo_asset_tpb.png"); public static Texture2D Logo_Asset_TPS => loadImage(ref logo_asset_tps, "logo_asset_tps.png"); public static Texture2D Logo_Asset_TR => loadImage(ref logo_asset_tr, "logo_asset_tr.png"); public static Texture2D Logo_CT => loadImage(ref logo_ct, "logo_ct.png"); public static Texture2D Logo_Unity => loadImage(ref logo_unity, "logo_unity.png"); public static Texture2D Icon_Save => loadImage(ref icon_save, "icon_save.png"); public static Texture2D Icon_Reset => loadImage(ref icon_reset, "icon_reset.png"); public static Texture2D Icon_Refresh => loadImage(ref icon_refresh, "icon_refresh.png"); public static Texture2D Icon_Delete => loadImage(ref icon_delete, "icon_delete.png"); public static Texture2D Icon_Folder => loadImage(ref icon_folder, "icon_folder.png"); public static Texture2D Icon_Plus => loadImage(ref icon_plus, "icon_plus.png"); public static Texture2D Icon_Minus => loadImage(ref icon_minus, "icon_minus.png"); public static Texture2D Icon_Manual => loadImage(ref icon_manual, "icon_manual.png"); public static Texture2D Icon_API => loadImage(ref icon_api, "icon_api.png"); public static Texture2D Icon_Forum => loadImage(ref icon_forum, "icon_forum.png"); public static Texture2D Icon_Product => loadImage(ref icon_product, "icon_product.png"); public static Texture2D Icon_Check => loadImage(ref icon_check, "icon_check.png"); public static Texture2D Social_Discord => loadImage(ref social_Discord, "social_Discord.png"); public static Texture2D Social_Facebook => loadImage(ref social_Facebook, "social_Facebook.png"); public static Texture2D Social_Twitter => loadImage(ref social_Twitter, "social_Twitter.png"); public static Texture2D Social_Youtube => loadImage(ref social_Youtube, "social_Youtube.png"); public static Texture2D Social_Linkedin => loadImage(ref social_Linkedin, "social_Linkedin.png"); public static Texture2D Video_Promo => loadImage(ref video_promo, "video_promo.png"); public static Texture2D Video_Tutorial => loadImage(ref video_tutorial, "video_tutorial.png"); public static Texture2D Icon_Videos => loadImage(ref icon_videos, "icon_videos.png"); public static Texture2D Icon_3p_Assets => loadImage(ref icon_3p_assets, "icon_3p_assets.png"); public static Texture2D Asset_PlayMaker => loadImage(ref asset_PlayMaker, "asset_PlayMaker.png"); public static Texture2D Asset_VolumetricAudio => loadImage(ref asset_VolumetricAudio, "asset_VolumetricAudio.png"); public static Texture2D Asset_RockTomate => loadImage(ref asset_rocktomate, "asset_rocktomate.png"); public static Texture2D Asset_RTFB => loadImage(ref asset_RTFB, "asset_RTFB.png"); #endregion #region Public methods /// Restart Unity. /// Executed method after the restart (optional) public static void RestartUnity(string executeMethod = "") { if (UnityEditor.SceneManagement.EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) { bool success = false; using (System.Diagnostics.Process process = new System.Diagnostics.Process()) { try { process.StartInfo.CreateNoWindow = true; process.StartInfo.UseShellExecute = false; string scriptfile; switch (Application.platform) { case RuntimePlatform.WindowsEditor: scriptfile = $"{Crosstales.Common.Util.FileHelper.TempPath}RestartUnity-{System.Guid.NewGuid()}.cmd"; Crosstales.Common.Util.FileHelper.WriteAllText(scriptfile, generateWindowsRestartScript(executeMethod)); process.StartInfo.FileName = "cmd.exe"; process.StartInfo.Arguments = $"/c start \"\" \"{scriptfile}\""; break; case RuntimePlatform.OSXEditor: scriptfile = $"{Crosstales.Common.Util.FileHelper.TempPath}RestartUnity-{System.Guid.NewGuid()}.sh"; Crosstales.Common.Util.FileHelper.WriteAllText(scriptfile, generateMacRestartScript(executeMethod)); process.StartInfo.FileName = "/bin/sh"; process.StartInfo.Arguments = $"\"{scriptfile}\" &"; break; case RuntimePlatform.LinuxEditor: scriptfile = $"{Crosstales.Common.Util.FileHelper.TempPath}RestartUnity-{System.Guid.NewGuid()}.sh"; Crosstales.Common.Util.FileHelper.WriteAllText(scriptfile, generateLinuxRestartScript(executeMethod)); process.StartInfo.FileName = "/bin/sh"; process.StartInfo.Arguments = $"\"{scriptfile}\" &"; break; default: Debug.LogError($"Unsupported Unity Editor: {Application.platform}"); return; } process.Start(); if (isWindowsEditor) process.WaitForExit(Crosstales.Common.Util.BaseConstants.PROCESS_KILL_TIME); success = true; } catch (System.Exception ex) { string errorMessage = $"Could restart Unity: {ex}"; Debug.LogError(errorMessage); } } if (success) EditorApplication.Exit(0); } else { Debug.LogWarning("User canceled the restart."); } } /// Shows a separator-UI. /// Space in pixels between the component and the separator line (default: 12, optional). public static void SeparatorUI(int space = 12) { GUILayout.Space(space); GUILayout.Box(string.Empty, GUILayout.ExpandWidth(true), GUILayout.Height(1)); } /// Generates a read-only text field with a label. public static void ReadOnlyTextField(string label, string text) { EditorGUILayout.BeginHorizontal(); { EditorGUILayout.LabelField(label, GUILayout.Width(EditorGUIUtility.labelWidth - 4)); EditorGUILayout.SelectableLabel(text, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight)); } EditorGUILayout.EndHorizontal(); } /// Refreshes the asset database. /// Asset import options (default: ImportAssetOptions.Default, optional). public static void RefreshAssetDatabase(ImportAssetOptions options = ImportAssetOptions.Default) { if (!Application.isPlaying) AssetDatabase.Refresh(options); } /// Returns the true if the BuildTarget is installed in Unity. /// BuildTarget to test /// True if the BuildTarget is installed in Unity. public static bool isValidBuildTarget(BuildTarget target) { return (bool)isPlatformSupportLoaded.Invoke(null, new object[] { (string)getTargetStringFromBuildTarget.Invoke(null, new object[] { target }) }); } /* public static IEnumerable GetAvailableBuildTargets() { foreach (BuildTarget target in (BuildTarget[])Enum.GetValues(typeof(BuildTarget))) { BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target); if (BuildPipeline.IsBuildTargetSupported(group, target)) { yield return target; } } } */ /// Returns the BuildTarget for a build name, like 'win64'. /// Build name, like 'win64' /// The BuildTarget for a build name. public static BuildTarget GetBuildTargetForBuildName(string build) { if ("win32".CTEquals(build) || "win".CTEquals(build)) return BuildTarget.StandaloneWindows; if ("win64".CTEquals(build)) return BuildTarget.StandaloneWindows64; if (!string.IsNullOrEmpty(build) && build.CTContains("osx")) return BuildTarget.StandaloneOSX; if (!string.IsNullOrEmpty(build) && build.CTContains("linux")) return BuildTarget.StandaloneLinux64; if ("android".CTEquals(build)) return BuildTarget.Android; if ("ios".CTEquals(build)) return BuildTarget.iOS; if ("wsaplayer".CTEquals(build) || "WindowsStoreApps".CTEquals(build)) return BuildTarget.WSAPlayer; if ("webgl".CTEquals(build)) return BuildTarget.WebGL; if ("tvOS".CTEquals(build)) return BuildTarget.tvOS; if ("ps4".CTEquals(build)) return BuildTarget.PS4; if ("ps5".CTEquals(build)) return BuildTarget.PS5; if ("xboxone".CTEquals(build)) return BuildTarget.XboxOne; if ("switch".CTEquals(build)) return BuildTarget.Switch; Debug.LogWarning($"Build target '{build}' not found! Returning 'StandaloneWindows64'."); return BuildTarget.StandaloneWindows64; } /// Returns the build name for a BuildTarget. /// BuildTarget for a build name /// The build name for a BuildTarget. public static string GetBuildNameFromBuildTarget(BuildTarget build) { switch (build) { case BuildTarget.StandaloneWindows: return "Win"; case BuildTarget.StandaloneWindows64: return "Win64"; case BuildTarget.StandaloneOSX: return "OSXUniversal"; case BuildTarget.StandaloneLinux64: return "Linux64"; case BuildTarget.Android: return "Android"; case BuildTarget.iOS: return "iOS"; case BuildTarget.WSAPlayer: return "WindowsStoreApps"; case BuildTarget.WebGL: return "WebGL"; case BuildTarget.tvOS: return "tvOS"; case BuildTarget.PS4: return "PS4"; case BuildTarget.PS5: return "PS5"; case BuildTarget.XboxOne: return "XboxOne"; case BuildTarget.Switch: return "Switch"; default: Debug.LogWarning($"Build target '{build}' not found! Returning Windows standalone."); return "Win64"; } } /// Returns assets for a certain type. /// List of assets for a certain type. public static System.Collections.Generic.List FindAssetsByType() where T : Object { string[] guids = AssetDatabase.FindAssets($"t:{typeof(T)}"); return guids.Select(AssetDatabase.GUIDToAssetPath).Select(AssetDatabase.LoadAssetAtPath).Where(asset => asset != null).ToList(); } /// /// Create and return a new asset in a smart location based on the current selection and then select it. /// /// Name of the new asset. Do not include the .asset extension. /// Shows the save file browser to select a destination for the asset (default: true, optional). /// The new asset. public static T CreateAsset(string name, bool showSaveFileBrowser = true) where T : ScriptableObject { string path; if (showSaveFileBrowser) { //string classname = nameof(T); path = EditorUtility.SaveFilePanelInProject($"Save asset {name}", name, "asset", ""); } else { path = AssetDatabase.GetAssetPath(Selection.activeObject); if (path.Length == 0) { // no asset selected, place in asset root path = "Assets/" + name + ".asset"; } else if (Crosstales.Common.Util.FileHelper.ExistsDirectory(path)) { // place in currently selected directory path += "/" + name + ".asset"; } else { // place in current selection's containing directory path = Crosstales.Common.Util.FileHelper.GetDirectoryName(path) + "/" + name + ".asset"; } } if (string.IsNullOrEmpty(path)) return default; T asset = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(asset, AssetDatabase.GenerateUniqueAssetPath(path)); EditorUtility.FocusProjectWindow(); Selection.activeObject = asset; return asset; } /// Instantiates a prefab. /// Name of the prefab. /// Path to the prefab. public static void InstantiatePrefab(string prefabName, string path) { PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath($"Assets{path}{prefabName}.prefab", typeof(GameObject))); if (isEditorMode) UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()); } #endregion #region Private methods private static string generateWindowsRestartScript(string executeMethod) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); // setup sb.AppendLine("@echo off"); sb.AppendLine("cls"); // title sb.Append("title Restart of "); sb.Append(Application.productName); sb.AppendLine(" - DO NOT CLOSE THIS WINDOW!"); // header sb.AppendLine("echo ##############################################################################"); sb.AppendLine("echo # #"); sb.AppendLine("echo # Common 2023.1.0 - Windows #"); sb.AppendLine("echo # Copyright 2018-2023 by www.crosstales.com #"); sb.AppendLine("echo # #"); sb.AppendLine("echo # This script restarts Unity. #"); sb.AppendLine("echo # This will take some time, so please be patient and DON'T CLOSE THIS #"); sb.AppendLine("echo # WINDOW before the process is finished! #"); sb.AppendLine("echo # #"); sb.AppendLine("echo ##############################################################################"); sb.Append("echo "); sb.AppendLine(Application.productName); sb.AppendLine("echo."); sb.AppendLine("echo."); // check if Unity is closed sb.AppendLine(":waitloop"); sb.Append("if not exist \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH); sb.Append("Temp\\UnityLockfile\" goto waitloopend"); sb.AppendLine(); sb.AppendLine("echo."); sb.AppendLine("echo Waiting for Unity to close..."); sb.AppendLine("timeout /t 3"); sb.AppendLine("goto waitloop"); sb.AppendLine(":waitloopend"); // Restart Unity sb.AppendLine("echo."); sb.AppendLine("echo ##############################################################################"); sb.AppendLine("echo # Restarting Unity #"); sb.AppendLine("echo ##############################################################################"); sb.Append("start \"\" \""); sb.Append(Crosstales.Common.Util.FileHelper.ValidatePath(EditorApplication.applicationPath, false)); sb.Append("\" -projectPath \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH.Substring(0, Crosstales.Common.Util.BaseConstants.APPLICATION_PATH.Length - 1)); sb.Append("\""); if (!string.IsNullOrEmpty(executeMethod)) { sb.Append(" -executeMethod "); sb.Append(executeMethod); } sb.AppendLine(); sb.AppendLine("echo."); // check if Unity is started sb.AppendLine(":waitloop2"); sb.Append("if exist \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH); sb.Append("Temp\\UnityLockfile\" goto waitloopend2"); sb.AppendLine(); sb.AppendLine("echo Waiting for Unity to start..."); sb.AppendLine("timeout /t 3"); sb.AppendLine("goto waitloop2"); sb.AppendLine(":waitloopend2"); sb.AppendLine("echo."); sb.AppendLine("echo Bye!"); sb.AppendLine("timeout /t 1"); sb.AppendLine("exit"); return sb.ToString(); } private static string generateMacRestartScript(string executeMethod) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); // setup sb.AppendLine("#!/bin/bash"); sb.AppendLine("set +v"); sb.AppendLine("clear"); // title sb.Append("title='Relaunch of "); sb.Append(Application.productName); sb.AppendLine(" - DO NOT CLOSE THIS WINDOW!'"); sb.AppendLine("echo -n -e \"\\033]0;$title\\007\""); // header sb.AppendLine("echo \"+----------------------------------------------------------------------------+\""); sb.AppendLine("echo \"¦ ¦\""); sb.AppendLine("echo \"¦ Common 2023.1.0 - macOS ¦\""); sb.AppendLine("echo \"¦ Copyright 2018-2023 by www.crosstales.com ¦\""); sb.AppendLine("echo \"¦ ¦\""); sb.AppendLine("echo \"¦ This script restarts Unity. ¦\""); sb.AppendLine("echo \"¦ This will take some time, so please be patient and DON'T CLOSE THIS ¦\""); sb.AppendLine("echo \"¦ WINDOW before the process is finished! ¦\""); sb.AppendLine("echo \"¦ ¦\""); sb.AppendLine("echo \"+----------------------------------------------------------------------------+\""); sb.Append("echo \""); sb.Append(Application.productName); sb.AppendLine("\""); sb.AppendLine("echo"); sb.AppendLine("echo"); // check if Unity is closed sb.Append("while [ -f \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH); sb.Append("Temp/UnityLockfile\" ]"); sb.AppendLine(); sb.AppendLine("do"); sb.AppendLine(" echo \"Waiting for Unity to close...\""); sb.AppendLine(" sleep 3"); sb.AppendLine("done"); // Restart Unity sb.AppendLine("echo"); sb.AppendLine("echo \"+----------------------------------------------------------------------------+\""); sb.AppendLine("echo \"¦ Restarting Unity ¦\""); sb.AppendLine("echo \"+----------------------------------------------------------------------------+\""); sb.Append("open -a \""); sb.Append(EditorApplication.applicationPath); sb.Append("\" --args -projectPath \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH); sb.Append("\""); if (!string.IsNullOrEmpty(executeMethod)) { sb.Append(" -executeMethod "); sb.Append(executeMethod); } sb.AppendLine(); //check if Unity is started sb.AppendLine("echo"); sb.Append("while [ ! -f \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH); sb.Append("Temp/UnityLockfile\" ]"); sb.AppendLine(); sb.AppendLine("do"); sb.AppendLine(" echo \"Waiting for Unity to start...\""); sb.AppendLine(" sleep 3"); sb.AppendLine("done"); sb.AppendLine("echo"); sb.AppendLine("echo \"Bye!\""); sb.AppendLine("sleep 1"); sb.AppendLine("exit"); return sb.ToString(); } private static string generateLinuxRestartScript(string executeMethod) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); // setup sb.AppendLine("#!/bin/bash"); sb.AppendLine("set +v"); sb.AppendLine("clear"); // title sb.Append("title='Relaunch of "); sb.Append(Application.productName); sb.AppendLine(" - DO NOT CLOSE THIS WINDOW!'"); sb.AppendLine("echo -n -e \"\\033]0;$title\\007\""); // header sb.AppendLine("echo \"+----------------------------------------------------------------------------+\""); sb.AppendLine("echo \"¦ ¦\""); sb.AppendLine("echo \"¦ Common 2023.1.0 - Linux ¦\""); sb.AppendLine("echo \"¦ Copyright 2018-2023 by www.crosstales.com ¦\""); sb.AppendLine("echo \"¦ ¦\""); sb.AppendLine("echo \"¦ This script restarts Unity. ¦\""); sb.AppendLine("echo \"¦ This will take some time, so please be patient and DON'T CLOSE THIS ¦\""); sb.AppendLine("echo \"¦ WINDOW before the process is finished! ¦\""); sb.AppendLine("echo \"¦ ¦\""); sb.AppendLine("echo \"+----------------------------------------------------------------------------+\""); sb.Append("echo \""); sb.Append(Application.productName); sb.AppendLine("\""); sb.AppendLine("echo"); sb.AppendLine("echo"); // check if Unity is closed sb.Append("while [ -f \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH); sb.Append("Temp/UnityLockfile\" ]"); sb.AppendLine(); sb.AppendLine("do"); sb.AppendLine(" echo \"Waiting for Unity to close...\""); sb.AppendLine(" sleep 3"); sb.AppendLine("done"); // Restart Unity sb.AppendLine("echo"); sb.AppendLine("echo \"+----------------------------------------------------------------------------+\""); sb.AppendLine("echo \"¦ Restarting Unity ¦\""); sb.AppendLine("echo \"+----------------------------------------------------------------------------+\""); sb.Append('"'); sb.Append(EditorApplication.applicationPath); sb.Append("\" --args -projectPath \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH); sb.Append("\""); if (!string.IsNullOrEmpty(executeMethod)) { sb.Append(" -executeMethod "); sb.Append(executeMethod); } sb.Append(" &"); sb.AppendLine(); // check if Unity is started sb.AppendLine("echo"); sb.Append("while [ ! -f \""); sb.Append(Crosstales.Common.Util.BaseConstants.APPLICATION_PATH); sb.Append("Temp/UnityLockfile\" ]"); sb.AppendLine(); sb.AppendLine("do"); sb.AppendLine(" echo \"Waiting for Unity to start...\""); sb.AppendLine(" sleep 3"); sb.AppendLine("done"); sb.AppendLine("echo"); sb.AppendLine("echo \"Bye!\""); sb.AppendLine("sleep 1"); sb.AppendLine("exit"); return sb.ToString(); } private static Texture2D loadImage(ref Texture2D logo, string fileName) { if (logo == null) { #if CT_DEVELOP logo = (Texture2D)AssetDatabase.LoadAssetAtPath($"Assets/Plugins/crosstales/Common/Icons/{fileName}", typeof(Texture2D)); #else logo = (Texture2D)EditorGUIUtility.Load($"crosstales/Common/{fileName}"); #endif if (logo == null) Debug.LogWarning($"Image not found: {fileName}"); } return logo; } #endregion } } #endif // © 2018-2023 crosstales LLC (https://www.crosstales.com)