// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved. using System.Collections.Generic; using UnityEngine; using UnityEditor; using System; public class SSCEditorHelper { #region Heading or Info Methods /// /// Display one of two technical preview messages in the editor /// /// public static void InTechPreview(bool radicalChangePossible = false) { if (radicalChangePossible) { EditorGUILayout.HelpBox("This feature is in technical preview and could radically change without notice.", MessageType.Warning); } else { EditorGUILayout.HelpBox("This feature is currently in technical preview", MessageType.Warning); } } /// /// Display a in-development warning in the editor /// public static void InDevelopment() { EditorGUILayout.HelpBox("This feature is in development, is incomplete, and could radically change without notice.", MessageType.Warning); } public static void NotImplemented() { EditorGUILayout.HelpBox("This feature has not yet been implemented", MessageType.Warning); } public static void PerformanceImpact() { EditorGUILayout.HelpBox("This feature may negatively impact performance", MessageType.Warning); } /// /// Draw the Sci-Fi Ship Controller version header in the inspector /// /// public static void SSCVersionHeader (GUIStyle labelFieldRichText) { GUILayout.BeginVertical("HelpBox"); EditorGUILayout.LabelField("Sci-Fi Ship Controller Version " + SciFiShipController.ShipControlModule.SSCVersion + " " + SciFiShipController.ShipControlModule.SSCBetaVersion, labelFieldRichText); GUILayout.EndVertical(); } #endregion #region Inspector GUIContent public readonly static GUIContent gizmoToggleBtnContent = new GUIContent("G", "Toggle gizmos and visualisations on/off for all items in the scene view"); public readonly static GUIContent gizmoBtnContent = new GUIContent("G", "Toggle gizmos on/off in the scene view"); public readonly static GUIContent gizmoFindBtnContent = new GUIContent("F", "Find (select) in the scene view."); public readonly static GUIContent gizmoLHBtnContent = new GUIContent("LH", "Show left-hand gizmos (default right-hand)"); public readonly static GUIContent btnResetContent = new GUIContent("R", "Reset to default value(s)"); public readonly static GUIContent debugModeIndent1Content = new GUIContent(" Debug Mode", "Use this to troubleshoot the data at runtime in the editor."); public readonly static GUIContent debugIsInitialisedIndent1Content = new GUIContent(" Is Initialised?"); #endregion #region Inspector Methods /// /// Draw a foldout and label on a single line /// SSCEditorHelper.DrawSSCFoldout(showMessageSettingsInEditorProp, altMessageSettingsContent, foldoutStyleNoLabel, defaultEditorFieldWidth); /// /// /// /// /// public static void DrawSSCFoldout(SerializedProperty serializedProperty, GUIContent guiLabelContent, GUIStyle foldoutStyleNoLabel, float defaultEditorFieldWidth) { GUILayout.BeginHorizontal(); EditorGUI.indentLevel += 1; EditorGUIUtility.fieldWidth = 15f; serializedProperty.boolValue = EditorGUILayout.Foldout(serializedProperty.boolValue, GUIContent.none, foldoutStyleNoLabel); EditorGUI.indentLevel -= 1; EditorGUIUtility.fieldWidth = defaultEditorFieldWidth; EditorGUILayout.LabelField(guiLabelContent); GUILayout.EndHorizontal(); } /// /// Draw a foldout with no label /// Example: SSCEditorHelper.DrawSSCFoldout(isDisplayMessageListExpandedProp, foldoutStyleNoLabel, defaultEditorFieldWidth); /// /// /// /// public static void DrawSSCFoldout(SerializedProperty serializedProperty, GUIStyle foldoutStyleNoLabel, float defaultEditorFieldWidth) { EditorGUI.indentLevel += 1; EditorGUIUtility.fieldWidth = 15f; serializedProperty.boolValue = EditorGUILayout.Foldout(serializedProperty.boolValue, GUIContent.none, foldoutStyleNoLabel); EditorGUI.indentLevel -= 1; EditorGUIUtility.fieldWidth = defaultEditorFieldWidth; } /// /// Draw the standard set of Support, Discord, Help, and Tutorial buttons in the inspector /// /// public static void DrawSSCGetHelpButtons(GUIStyle buttonCompact) { GUILayout.BeginHorizontal(); if (GUILayout.Button(SSCEditorHelper.btnTxtGetSupport, buttonCompact)) { Application.OpenURL(SSCEditorHelper.urlGetSupport); } if (GUILayout.Button(SSCEditorHelper.btnDiscordContent, buttonCompact)) { Application.OpenURL(SSCEditorHelper.urlDiscordChannel); } if (GUILayout.Button(SSCEditorHelper.btnHelpContent, buttonCompact)) { Application.OpenURL(SSCEditorHelper.GetHelpURL()); } if (GUILayout.Button(SSCEditorHelper.tutorialsURLContent, buttonCompact)) { Application.OpenURL(SSCEditorHelper.urlTutorials); } EditorGUILayout.EndHorizontal(); } /// /// Draw a horzontal gap. e.g DrawSSCHorizontalGap(2f) // 2 pixels high /// /// public static void DrawSSCHorizontalGap(float h) { GUILayoutUtility.GetRect(1f, h); } /// /// Draw a sprite in the inspector. Set the height so that it displays an empty space if the sprite is null /// /// /// public static void DrawSprite(GUIContent guiContent, Sprite sprite, int height, float labelWidth) { GUILayout.BeginHorizontal(); if (guiContent != GUIContent.none) { GUILayout.Label(guiContent, GUILayout.Width(labelWidth)); } GUILayout.Label(AssetPreview.GetAssetPreview(sprite), GUILayout.Height(height)); GUILayout.EndHorizontal(); } /// /// Draw an array in the inspector /// /// /// public static void DrawArray(SerializedProperty serializedProperty, GUIContent labelGUIContent, float labelWidth, string elementName) { GUILayout.BeginHorizontal(); EditorGUILayout.LabelField(labelGUIContent, GUILayout.Width(labelWidth-1f)); EditorGUILayout.PropertyField(serializedProperty.FindPropertyRelative("Array.size"), GUIContent.none); GUILayout.EndHorizontal(); EditorGUI.indentLevel++; for (int arrayIdx = 0; arrayIdx < serializedProperty.arraySize; arrayIdx++) { GUILayout.BeginHorizontal(); EditorGUILayout.LabelField(elementName + " " + (arrayIdx+1).ToString(), GUILayout.Width(labelWidth-16f)); EditorGUILayout.PropertyField(serializedProperty.GetArrayElementAtIndex(arrayIdx), GUIContent.none); GUILayout.EndHorizontal(); } EditorGUI.indentLevel--; } /// /// WIP - only left implemented /// Draw left and right indent sliders. Convert from/to -1.0 to 1.0 OffsetX /// Returns true if the offsetX property has changed /// /// /// /// public static bool DrawOffsetLeftRight(SerializedProperty offsetXProp, SerializedProperty widthProp, float labelWidth) { float startoffsetX = offsetXProp.floatValue; // Normalised offsetX then subtract half the panel width float leftIndent = (startoffsetX + 1f) * 0.5f - (widthProp.floatValue * 0.5f); float newLeftIndent = EditorGUILayout.Slider(new GUIContent(" Left Indent"), leftIndent, -1f, 1f); if (newLeftIndent != leftIndent) { // Convert back into -1 to 1 value float offsetX = newLeftIndent + widthProp.floatValue - 1f; // Clamp -1.0 to 1.0 offsetXProp.floatValue = offsetX < -1f ? -1f : offsetX > 1f ? 1f : offsetX; } return offsetXProp.floatValue != startoffsetX; } /// /// WIP - only bottom implemented /// Draw top and bottom indent sliders. Convert from/to -1.0 to 1.0 OffsetY /// Returns true if the offsetY property has changed /// /// /// /// public static bool DrawOffsetTopBottom(SerializedProperty offsetYProp, SerializedProperty heightProp, float labelWidth) { float startoffsetY = offsetYProp.floatValue; // Normalised offsetX then subtract half the panel width float bottomIndent = (startoffsetY + 1f) * 0.5f - (heightProp.floatValue * 0.5f); float newBottomIndent = EditorGUILayout.Slider(new GUIContent(" Bottom Indent"), bottomIndent, -1f, 1f); if (newBottomIndent != bottomIndent) { // Convert back into -1 to 1 value float offsetY = newBottomIndent + heightProp.floatValue - 1f; // Clamp -1.0 to 1.0 offsetYProp.floatValue = offsetY < -1f ? -1f : offsetY > 1f ? 1f : offsetY; } return offsetYProp.floatValue != startoffsetY; } /// /// Useful util drawing lines in the editor /// /// /// /// public static void DrawUILine(Color color, int thickness = 2, int padding = 10) { Rect r = EditorGUILayout.GetControlRect(GUILayout.Height(padding + thickness)); r.height = thickness; r.y += padding / 2f; r.x -= 2; r.width += 6f; EditorGUI.DrawRect(r, color); } /// /// Returns a vector3 as text for debugging with 0,1,2 or 3 decimal places /// /// /// /// public static string GetVector3Text(Vector3 v3, int decimalPlaces) { float multiplier = decimalPlaces == 0 ? 10 : decimalPlaces == 1 ? 100f : decimalPlaces == 2 ? 1000f : 10000f; string sFormat = decimalPlaces == 0 ? "0" : decimalPlaces == 1 ? "0.0" : decimalPlaces == 2 ? "0.00" : "0.000"; float x = Mathf.RoundToInt(v3.x * multiplier) / multiplier; float y = Mathf.RoundToInt(v3.y * multiplier) / multiplier; float z = Mathf.RoundToInt(v3.z * multiplier) / multiplier; return x.ToString(sFormat) + ", " + y.ToString(sFormat) + ", " + z.ToString(sFormat); } #endregion #region Scene View Methods /// /// Attempt to display the point in the centre of the sceneview /// zoomDistance is the distance in metres to zoom out from the point in the scene /// /// /// /// public static void PositionSceneView(Vector3 centrePoint, float zoomDistance, Type sourceType) { try { // Switch to the scene view #if UNITY_2018_2_OR_NEWER CallMenu("Window/General/Scene"); #else CallMenu("Window/Scene"); #endif SceneView sceneView = UnityEditor.SceneView.lastActiveSceneView; // If the sceneView hasn't had the focus in this session, lastActiveSceneView will return null if (sceneView != null) { sceneView.LookAt(centrePoint); sceneView.size = zoomDistance; } } catch (System.Exception ex) { Debug.LogError(sourceType.Name + ": Couldn't position scene view\n" + ex.Message); } } /// /// Given a mouse position in 2D space, get the 3D Worldspace position of the "nearest" line of sight object /// in the scene view. Return 0,0,0 is something went wrong or no object in direct line. /// /// /// /// /// /// /// public static bool GetPositionFromMouse(SceneView sceneView, Vector2 mousePosition, float normalOffset, ref Vector3 worldspacePoint, bool showErrors) { bool isSuccessful = false; worldspacePoint = Vector3.zero; string methodName = "SSCEditorHelper.GetPositionFromMouse"; try { if (sceneView != null) { // Only process mouse positions over a scene view. // This avoid the situation where scene view has focus but mouse is over another window var win = EditorWindow.mouseOverWindow; if (win != null && win.GetType() == typeof(SceneView)) { Camera svCamera = sceneView.camera; if (svCamera != null) { // Cast a ray from the scene view camera through the mouse point onto (hopefully) the nearest object. // Mouse position Y in screen space is inverted Ray ray = svCamera.ScreenPointToRay(new Vector3(mousePosition.x, svCamera.pixelHeight - mousePosition.y, svCamera.nearClipPlane)); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { // We hit something worldspacePoint = hit.point + (hit.normal * normalOffset); } else { // If we didn't hit anything, create a point infront of the scene view camera worldspacePoint = ray.GetPoint(100f); } isSuccessful = true; } } } } catch (System.Exception ex) { Debug.LogWarning(methodName + " - sorry, something went wrong\n" + ex.Message); } return isSuccessful; } /// /// Given a mouse position in 2D space, get the 3D Worldspace position of the "nearest" line of sight object. /// Return 0,0,0 is something went wrong. /// /// /// /// public static Vector3 GetPositionFromMouse(Vector2 mousePosition, bool showErrors) { Vector3 locationPoint = Vector3.zero; string methodName = "SSCEditorHelper.GetPositionFromMouse"; try { SceneView sceneView = SceneView.lastActiveSceneView; bool isHit = false; if (sceneView != null) { Camera svCamera = sceneView.camera; if (svCamera != null) { // Cast a ray from the scene view camera through the mouse point onto (hopefully) the nearest object. // Mouse position Y in screen space is inverted Ray ray = svCamera.ScreenPointToRay(new Vector3(mousePosition.x, svCamera.pixelHeight - mousePosition.y, svCamera.nearClipPlane)); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { // We hit something locationPoint = hit.point; isHit = true; } if (!isHit) { // If we didn't hit anything, create a point infront of the scene view camera locationPoint = ray.GetPoint(100f); } } } } catch (System.Exception ex) { Debug.LogWarning(methodName + " - sorry, something went wrong\n" + ex.Message); } return locationPoint; } #endregion #region Menu Helper Methods /// /// Call an item from the Unity menu. Menu can also be one custom created. /// USAGE: SSCEditorHelper.CallMenu("Edit/Project Settings/Player"); /// /// public static void CallMenu(string menuItemPath) { if (!string.IsNullOrEmpty(menuItemPath)) { EditorApplication.ExecuteMenuItem(menuItemPath); } } #endregion #region Link Helper variables and Methods // URL buttons //public static readonly string urlAssetPage = "http://u3d.as/1oPf"; public static readonly string urlGetSupport = "http://forum.unity3d.com/threads/594448"; public static readonly string urlDiscordChannel = "https://discord.gg/CjzCK4b"; public static readonly string urlTutorials = "https://scsmmedia.com/sci-fi-ship-controller#938b782a-2c40-4590-9f23-de983ed33787"; //public static readonly string btnTxtAssetPage = "Asset Page"; public static readonly string btnTxtGetSupport = "Get Support"; public static readonly GUIContent tutorialsURLContent = new GUIContent("Tutorials", "Go to our website for a link to Video Tutorials about SSC concepts"); public static readonly GUIContent btnHelpContent = new GUIContent("Help", "Sci-Fi Ship Controller manual. Requires Adobe Reader\nAvailable from\nadobe.com/reader"); public static readonly GUIContent btnDiscordContent = new GUIContent("Discord", "Open SSC Discord Channel in browser"); /// /// Get help PDF path to point to the local copy of the manual. /// /// public static string GetHelpURL() { // Build a link to the file manual. #if UNITY_EDITOR_OSX return "File:///" + Application.dataPath.Replace(" " ,"%20") + "/scsm/SciFiShipController/ssc_manual.pdf"; #else return "file:///" + Application.dataPath + "/scsm/SciFiShipController/ssc_manual.pdf"; #endif } #endregion #region Audio /// /// Play an audio clip in the editor /// /// /// /// public static void PlayAudioClip(AudioClip audioClip, int startSample = 0, bool isLoop = false) { try { System.Reflection.Assembly unityEditorAssembly = typeof(AudioImporter).Assembly; System.Type audioUtilType = unityEditorAssembly.GetType("UnityEditor.AudioUtil"); #if UNITY_2020_2_OR_NEWER string playClipMethodName = "PlayPreviewClip"; #else string playClipMethodName = "PlayClip"; #endif // Get the method to play an audio clip in the editor System.Reflection.MethodInfo playClipMethod = audioUtilType.GetMethod(playClipMethodName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public, null, new System.Type[] { typeof(AudioClip), typeof(int), typeof(bool) }, null ); playClipMethod.Invoke(null, new object[] { audioClip, startSample, isLoop }); } catch (System.Exception ex) { #if UNITY_EDITOR Debug.LogWarning("SSCEditorHelper PlayAudioClip - " + ex.Message); #else if (ex != null) { } #endif } } /// /// Stop all audio clips playing in the editor /// public static void StopAllAudioClips() { try { System.Reflection.Assembly unityEditorAssembly = typeof(AudioImporter).Assembly; System.Type audioUtilType = unityEditorAssembly.GetType("UnityEditor.AudioUtil"); #if UNITY_2020_2_OR_NEWER string stopClipMethodName = "StopAllPreviewClips"; #else string stopClipMethodName = "StopAllClips"; #endif System.Reflection.MethodInfo stopClipsMethod = audioUtilType.GetMethod(stopClipMethodName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public, null, new System.Type[] { }, null ); stopClipsMethod.Invoke(null, new object[] { } ); } catch (System.Exception ex) { #if UNITY_EDITOR Debug.LogWarning("SSCEditorHelper StopAllAudioClips - " + ex.Message); #else if (ex != null) { } #endif } } #endregion #region Camera Methods // See SSCUtils.cs #endregion #region Gameobject and Transform Methods /// /// Find or create a child gameobject. If it doesn't already exist, optionally make it a child of the parent. /// /// /// /// /// public static GameObject GetOrCreateChildGameObject(GameObject parentGameObject, string gameObjectName, bool isMakeChildOnCreate) { GameObject gameObject = null; if (parentGameObject != null) { Transform parentTfrm = parentGameObject.transform; Transform trfm = parentTfrm.Find(gameObjectName); // Not found so create a new gameobject if (trfm == null) { gameObject = new GameObject(gameObjectName); if (gameObject != null) { trfm = gameObject.transform; if (isMakeChildOnCreate) { trfm.SetParent(parentTfrm, false); } } } else { gameObject = trfm.gameObject; } } return gameObject; } #endregion #region Project Helper Methods /// /// Reveal or hightlight the folder that in the Project window (if it is open and visible /// on the panel it is on). Highlights the item in yellow for a second or two. /// By default also selects the object. Not selecting the object can be useful if called /// from a CustomEditor inspector script so as not to loose focus. /// NOTE: The Project won't expand if it is already selected AND the user has collapsed it. /// USAGE: SSCEditorHelper.HighlightFolderInProjectWindow(SSCSetup.effectsFolder, true, true); /// /// /// /// public static void HighlightFolderInProjectWindow(string folderPath, bool selectFolder, bool showErrors) { if (AssetDatabase.IsValidFolder(folderPath)) { UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(folderPath, typeof(UnityEngine.Object)); if (obj != null) { // Highlight the item in yellow for a second or two. EditorGUIUtility.PingObject(obj); if (selectFolder) { UnityEditor.Selection.activeObject = obj; } } } else { Debug.Log("SSCEditorHelper.HighLightFolder - the following folder does not exist: " + folderPath); } } #endregion #region IO Helper Methods /// /// Get a full file path from the user. /// Has the option to restrict to only files in the current Unity project. /// fileExtensions format: { "Texture2D", "png,psd,jpg,jpeg", "All files", "*" } /// /// /// /// /// /// /// /// public static bool GetFilePathFromUser(string fileTypeName, string relativeFolderToOpen, string[] fileExtensions, bool restrictToProject, ref string folderPath, ref string fileName) { bool isSuccessful = false; // Returns the full absolute path string path = EditorUtility.OpenFilePanelWithFilters(fileTypeName, relativeFolderToOpen, fileExtensions); if (path.Contains(Application.dataPath) || (!restrictToProject && !string.IsNullOrEmpty(path))) { // Make sure the text field doesn't have the focus, else it won't update until user // moves to another control. GUI.FocusControl(""); folderPath = System.IO.Path.GetDirectoryName(path); fileName = System.IO.Path.GetFileName(path); isSuccessful = true; } // Did user cancel open file panel? else if (string.IsNullOrEmpty(path)) { } else { EditorUtility.DisplayDialog(fileTypeName + " File", "The file must be in the Assets folder of your project", "OK"); } return isSuccessful; } /// /// Get a folder from the user (EDITOR ONLY) /// EXAMPLE: GetPathFromUser("Path Data", SSCSetup.sscFolder, false, ref pathFileFolder); /// /// /// /// /// /// public static bool GetPathFromUser(string dialogTitle, string relativeFolderToOpen, bool restrictToProject, ref string folderPath) { bool isSuccessful = false; // Returns the full absolute path string path = EditorUtility.OpenFolderPanel(dialogTitle, relativeFolderToOpen, ""); if ((!restrictToProject && !string.IsNullOrEmpty(path)) || path.Contains(Application.dataPath)) { if (restrictToProject) { // Get the relative path from Assets if (path.Length > Application.dataPath.Length) { path = path.Remove(0, Application.dataPath.Length); } if (path.Length > 1) { if (path[0] == '/') { path = path.Remove(0, 1); } } } // Make sure the text field doesn't have the focus, else it won't update until user // moves to another control. GUI.FocusControl(""); folderPath = path; isSuccessful = true; } // Did user cancel open folder panel? else if (string.IsNullOrEmpty(path)) { } else { EditorUtility.DisplayDialog(dialogTitle + " Folder", "The folder must be in the Assets folder of your project", "OK"); } return isSuccessful; } /// /// Get the Project assets folder for an item in the Project folder given a full file path. /// /// /// public static string GetAssetFolderFromFilePath(string filePath) { if (string.IsNullOrEmpty(filePath)) { return string.Empty; } else { // First attempts to simply strip out the Application.dataPath, then tries to replace the forward slash with back slashes and then try stripping the dataPath again. return "Assets" + System.IO.Path.GetDirectoryName(filePath).Replace(Application.dataPath, "").Replace(Application.dataPath.Replace("/", "\\"), ""); } } #endregion #region Dialog Helpers /// /// Prompt user to respond Yes or No /// /// /// /// public static bool PromptYesNo(string dialogTile, string dialogText) { return EditorUtility.DisplayDialog(dialogTile, dialogText, "Yes", "NO!"); } /// /// Prompt user to continue with an action. /// /// /// /// public static bool PromptForContinue(string dialogTile, string dialogText) { return EditorUtility.DisplayDialog(dialogTile, dialogText, "Yes", "CANCEL!"); } /// /// Prompt the user to delete something or cancel. /// if (SSCEditorHelper.PromptForDelete("Delete Items?", labelText)) {...} /// /// /// /// public static bool PromptForDelete(string dialogTile, string dialogText) { return EditorUtility.DisplayDialog(dialogTile, dialogText, "Delete Now", "Cancel"); } #endregion #region Canvas Helpers public static UnityEngine.UI.Image AddCanvasPanelIfMissing ( UnityEngine.UI.Image[] uiImageArray, string panelName, float panelOffsetX, float panelOffsetY, float panelWidth, float panelHeight, float anchorMinX, float anchorMinY, float anchorMaxX, float anchorMaxY, Transform parentTrfm ) { UnityEngine.UI.Image panelImg = ArrayUtility.Find(uiImageArray, img => img.name == panelName); if (panelImg == null) { GameObject panelGO = new GameObject(panelName); panelGO.layer = 5; panelGO.transform.SetParent(parentTrfm); RectTransform rectTrfm = panelGO.AddComponent(); rectTrfm.anchorMin = new Vector2(anchorMinX, anchorMinY); rectTrfm.anchorMax = new Vector2(anchorMaxX, anchorMaxY); rectTrfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, panelWidth); rectTrfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, panelHeight); panelGO.transform.position = new Vector3(panelOffsetX + (panelWidth * 0.5f), panelOffsetY + (panelHeight * 0.5f), 0f); panelGO.AddComponent(); panelImg = panelGO.AddComponent(); panelImg.color = new Color(1f,1f,1f, 50f / 255f); panelImg.sprite = AssetDatabase.GetBuiltinExtraResource("UI/Skin/UISprite.psd"); panelImg.type = UnityEngine.UI.Image.Type.Sliced; panelImg.raycastTarget = false; panelImg.fillCenter = false; } return panelImg; } public static UnityEngine.UI.RawImage AddCanvasRawPanelIfMissing ( UnityEngine.UI.RawImage[] uiImageArray, string panelName, float panelOffsetX, float panelOffsetY, float panelWidth, float panelHeight, float anchorMinX, float anchorMinY, float anchorMaxX, float anchorMaxY, Texture imgTexture, Transform parentTrfm, Vector3 canvasScale ) { UnityEngine.UI.RawImage panelImg = ArrayUtility.Find(uiImageArray, img => img.name == panelName); if (panelImg == null) { GameObject panelGO = new GameObject(panelName); panelGO.layer = 5; panelGO.transform.SetParent(parentTrfm); RectTransform rectTrfm = panelGO.AddComponent(); rectTrfm.anchorMin = new Vector2(anchorMinX, anchorMinY); rectTrfm.anchorMax = new Vector2(anchorMaxX, anchorMaxY); rectTrfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, panelWidth); rectTrfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, panelHeight); panelGO.transform.position = new Vector3((panelOffsetX * canvasScale.x) + (panelWidth * 0.5f * canvasScale.x), (panelOffsetY * canvasScale.y) + (panelHeight * 0.5f * canvasScale.y), 0f); panelGO.AddComponent(); panelImg = panelGO.AddComponent(); // No transparency as the colour for the texture will set the transparency for each element panelImg.color = new Color(1f,1f,1f, 255f / 255f); panelImg.raycastTarget = false; panelImg.texture = imgTexture; } return panelImg; } #endregion #region Search Helpers /// /// Does the string value of the property match the search criteria. /// Typically this would be the name value of a LocationData or PathData class instance. /// If the search string is empty, always return true. /// /// /// /// public static bool IsInSearchFilter(SerializedProperty serializedProperty, string searchFilter) { // If the search string is empty, always return true. if (string.IsNullOrEmpty(searchFilter)) { return true; } else { // If this property has no stringvalue (typically a name), it can't be a match return !string.IsNullOrEmpty(serializedProperty.stringValue) && serializedProperty.stringValue.ToLower().Contains(searchFilter.ToLower()); } } /// /// Draw the search filter control in the editor /// /// /// /// /// /// public static void DrawSearchFilterControl(GUIContent filterContent, GUIStyle labelStyle, GUIStyle searchFieldStyle, GUIStyle cancelButtonStyle, ref string editorSearchFilter) { GUILayout.BeginHorizontal(); GUILayout.Label("", GUILayout.Width(2f)); GUILayout.Label(filterContent, labelStyle, GUILayout.Width(50f)); if (editorSearchFilter == null) { editorSearchFilter = string.Empty; } editorSearchFilter = GUILayout.TextField(editorSearchFilter, searchFieldStyle); // Sometimes the search text is reverse search almost seems invisible (small search icon and cancel cannot be seen) // Not sure how this occurs. if (GUILayout.Button("", cancelButtonStyle)) { editorSearchFilter = string.Empty; GUI.FocusControl(null); } //// Force buttons to be right justified //GUILayoutUtility.GetRect(1f, 1f); //// Toggle selection based on the first filtered one in the list //if (GUILayout.Button(selectBtnContent, buttonCompact, GUILayout.MaxWidth(20f))) //{ // ToggleSelectList(pathDataListProp, sscManager.editorSearchPathFilter); //} GUILayout.EndHorizontal(); // Provide space between previous controls and next ones GUILayoutUtility.GetRect(1f, 3f); } #endregion #region Prefab Helpers /// /// Return true if is part of a prefab in Project assets folder (or subfolders) /// but is NOT an instance of the prefab a scene. /// /// /// public static bool IsPrefabAsset(GameObject go) { return PrefabUtility.IsPartOfPrefabAsset(go); } #endregion #region Miscellaneous #endregion }