#if UNITY_EDITOR using FIMSpace.FEditor; using UnityEditor; #endif using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; using System.Reflection; namespace FIMSpace.Generating { public static class FGenerators { public static GameObject InstantiateObject(GameObject obj) { #if UNITY_EDITOR GameObject newObj = null; #if UNITY_2018_4_OR_NEWER if (Application.isPlaying == false && (PrefabUtility.GetPrefabAssetType(obj) == PrefabAssetType.Regular || PrefabUtility.GetPrefabAssetType(obj) == PrefabAssetType.Variant)) newObj = (GameObject)PrefabUtility.InstantiatePrefab(obj); #else if (Application.isPlaying == false && (PrefabUtility.GetPrefabType(obj) == PrefabType.Prefab || PrefabUtility.GetPrefabType(obj) == PrefabType.PrefabInstance)) newObj = (GameObject)PrefabUtility.InstantiatePrefab(obj); #endif if (newObj == null) newObj = GameObject.Instantiate(obj); return newObj; #else return GameObject.Instantiate(obj); #endif } public static void ClearGenerated(List generated) where T : UnityEngine.Object { if (generated == null) return; for (int i = 0; i < generated.Count; i++) { if (generated[i] == null) continue; if (generated[i] is Component) { Component comp = generated[i] as Component; DestroyObject(comp); continue; } DestroyObject(generated[i]); } generated.Clear(); } public static bool CheckIfIsNull(object o) { #if UNITY_2018_1_OR_NEWER if (o is null) return true; #else //if (o == null) return true; if (object.ReferenceEquals(o, null)) return true; #endif return false; } /// Calling CheckIfExist_NOTNULL (just shorter method name) public static bool Exists(object o) { return CheckIfExist_NOTNULL(o); } /// Calling CheckIfExist_NOTNULL (just shorter method name) public static bool NotNull(object o) { return CheckIfExist_NOTNULL(o); } /// Calling CheckIfIsNull (just shorter method name) public static bool IsNull(object o) { return CheckIfIsNull(o); } public static bool CheckIfExist_NOTNULL(object o) { return !CheckIfIsNull(o); } public static bool IsChildOf(Transform child, Transform rootParent) { Transform tParent = child; while (tParent != null && tParent != rootParent) { tParent = tParent.parent; } if (tParent == null) return false; else return true; } public static void DestroyObject(UnityEngine.Object obj, bool allowDestroyAssets = false) { if (obj == null) return; #if UNITY_EDITOR if (Application.isPlaying == false) GameObject.DestroyImmediate(obj, allowDestroyAssets); else GameObject.Destroy(obj); #else GameObject.Destroy(obj); #endif } /// Should be used instead of == null on unity classes to be able for call in async methods public static bool RefIsNull(object varMat) { if (CheckIfIsNull(varMat)) return true; if (ReferenceEquals(varMat, null)) return true; if (varMat == null) return true; if (varMat.Equals(null)) return true; return false; } #region Defined Seed Random Handling /// /// Class to use wirth multiple - separated random seed generators /// public class DefinedRandom { public int Seed { get; private set; } private System.Random random; #region Random Instance public DefinedRandom(int seed) { random = new System.Random(seed); } public void ReInitializeSeed(int seed) { random = new System.Random(seed); } public float GetRandom() { return FGenerators.GetRandom(random); } public float GetRandom(float from, float to) { return FGenerators.GetRandom(from, to, random); } public float GetRandomPlusMinus(float range) { return FGenerators.GetRandomPlusMinus(range, random); } /// To is exclusive, dedicated for arrays public int GetRandom(int from, int to) { return FGenerators.GetRandom(from, to, random); } public int GetRandomInclusive(int from, int to) { return FGenerators.GetRandomInclusive(from, to, random); } public int GetRandom(MinMax minMax) { return FGenerators.GetRandom(minMax, random); } public bool GetRandomFlip() { return FGenerators.GetRandomFlip( random); } #endregion } static System.Random random = new System.Random(); public static System.Random GlobalRandomInstance { get { return random; } } public static int LatestSeed { get; private set; } public static void SetSeed(int seed) { random = new System.Random(seed); LatestSeed = seed; } public static bool GetRandomFlip() { return GetRandomFlip(random); } public static bool GetRandomFlip(System.Random rand) { return GetRandom(0, 2, rand) == 1; } public static float GetRandom() { return GetRandom(random); } public static int GetRandomInclusive(int from, int to) { return GetRandomInclusive(from, to, random); } public static int GetRandomInclusive(int from, int to, System.Random rand) { return rand.Next(from, to + 1); } public static float GetRandom(System.Random rand) { return (float)rand.NextDouble(); } public static Vector2 SwampToBeRising(Vector2 minMax) { if (minMax.y < minMax.x) { float swapX = minMax.x; minMax.x = minMax.y; minMax.y = swapX; } return minMax; } /// Ensuring 'from' is lower value public static float GetRandomSwap(float from, float to, System.Random rand) { if (from > to) { float swapTo = from; from = to; to = swapTo; } return GetRandom(from, to, rand); } /// Ensuring 'from' is lower value public static float GetRandomSwap(float from, float to) { return GetRandomSwap(from, to, random); } public static float GetRandomPlusMinus(float plusminus) { return GetRandomPlusMinus(plusminus, random); } public static float GetRandomPlusMinus(float plusminus, System.Random rand) { return GetRandom(-plusminus, plusminus, rand); } public static float GetRandom(float from, float to) { return GetRandom(from, to, random); } public static float GetRandom(float from, float to, System.Random rand) { return from + (float)rand.NextDouble() * (to - from); } public static Vector3 GetRandom(Vector3 plusMinusRangesPerAxis) { return GetRandom(plusMinusRangesPerAxis, random); } public static Vector3 GetRandom(Vector3 plusMinusRangesPerAxis, System.Random rand) { Vector3 p = plusMinusRangesPerAxis; p.x = GetRandom(-p.x, p.x, rand); p.y = GetRandom(-p.y, p.y, rand); p.z = GetRandom(-p.z, p.z, rand); return p; } public static int GetRandom(int from, int to) { return GetRandom(from, to, random); } public static int GetRandom(int from, int to, System.Random rand) { return rand.Next(from, to); } public static int GetRandom(MinMax minMax) { return GetRandom(minMax, random); } public static int GetRandom(MinMax minMax, System.Random rand) { return (int)(minMax.Min + (float)rand.NextDouble() * ((minMax.Max + 1) - minMax.Min)); } #endregion #region Search Related public static void GetIncrementalTo(List list) where T : UnityEngine.Object { #if UNITY_EDITOR if (list == null) return; if (list.Count == 0) return; T refClip = list[0]; if (refClip == null) return; int indexOf = IndexOfFirstNumber(refClip.name); if (indexOf == -1) return; string path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(refClip)); string[] files = Directory.GetFiles(path); List clips = new List(); for (int i = 0; i < files.Length; i++) { T clip = (T)AssetDatabase.LoadAssetAtPath(files[i], typeof(T)); if (clip) clips.Add(clip); } string nameUntilDigit = refClip.name.Substring(0, indexOf); List foundClips = new List(); for (int i = 0; i < clips.Count; i++) { if (clips[i].name.Contains(nameUntilDigit)) foundClips.Add(clips[i]); } for (int i = 0; i < foundClips.Count; i++) { if (!list.Contains(foundClips[i])) list.Add(foundClips[i]); } #endif } public static void GetSimilarTo(List list) where T : UnityEngine.Object { #if UNITY_EDITOR if (list == null) return; if (list.Count == 0) return; T refClip = list[0]; if (refClip == null) return; string path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(refClip)); string[] files = Directory.GetFiles(path); List clips = new List(); for (int i = 0; i < files.Length; i++) { T clip = (T)AssetDatabase.LoadAssetAtPath(files[i], typeof(T)); if (clip) clips.Add(clip); } for (int i = 0; i < clips.Count; i++) { if (!list.Contains(clips[i])) list.Add(clips[i]); } #endif } public static int IndexOfFirstNumber(string name) { for (int i = 0; i < name.Length; i++) { int outer; if (int.TryParse(name[i].ToString(), out outer)) return i; } return -1; } #endregion #region GUI or Editor GUI Related #if UNITY_EDITOR public static void DrawScriptableField(ref T selected, string exampleFilename = "", string title = "Preset:") where T : ScriptableObject { if (selected == null) return; Color bg = GUI.backgroundColor; EditorGUILayout.BeginHorizontal(); EditorGUIUtility.labelWidth = 60; selected = (T)EditorGUILayout.ObjectField(title, selected, typeof(T), false); if (GUILayout.Button("Create New", GUILayout.Width(94))) selected = (T)GenerateScriptable(GameObject.Instantiate(selected), exampleFilename); EditorGUILayout.EndHorizontal(); } public static void DrawObjectList(List toDraw, GUIStyle style, string title, ref bool foldout, bool moveButtons = false, UnityEngine.Object toDirty = null) where T : UnityEngine.Object { if (toDraw == null) return; EditorGUILayout.BeginVertical(style); EditorGUILayout.BeginHorizontal(); string fold = foldout ? " ▼" : " ►"; if (GUILayout.Button(fold + " " + title + " (" + toDraw.Count + ")", EditorStyles.label, GUILayout.Width(200))) foldout = !foldout; if (foldout) { GUILayout.FlexibleSpace(); if (GUILayout.Button("+")) toDraw.Add(null); } EditorGUILayout.EndHorizontal(); if (foldout) { GUILayout.Space(4); if (toDraw.Count > 0) for (int i = 0; i < toDraw.Count; i++) { EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginHorizontal(); GUIContent lbl = new GUIContent("[" + i + "]"); float wdth = EditorStyles.label.CalcSize(lbl).x; EditorGUILayout.LabelField(lbl, GUILayout.Width(wdth + 2)); toDraw[i] = (T)EditorGUILayout.ObjectField(toDraw[i], typeof(T), false); if (moveButtons) { if (i > 0) if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_ArrowUp, "Move this element to be executed before above one"), GUILayout.Width(24))) { T temp = toDraw[i - 1]; toDraw[i - 1] = toDraw[i]; toDraw[i] = temp; } if (i < toDraw.Count - 1) if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_ArrowDown, "Move this element to be executed after below one"), GUILayout.Width(24))) { T temp = toDraw[i + 1]; toDraw[i + 1] = toDraw[i]; toDraw[i] = temp; } } if (GUILayout.Button("X", GUILayout.Width(24))) { toDraw.RemoveAt(i); break; } EditorGUILayout.EndHorizontal(); if (toDirty != null) if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(toDirty); } } else { EditorGUILayout.LabelField("No object in list", EditorStyles.centeredGreyMiniLabel); } } EditorGUILayout.EndVertical(); } #endif public static List CopyList(List cellsInstructions) { List nList = new List(); for (int i = 0; i < cellsInstructions.Count; i++) nList.Add(cellsInstructions[i]); return nList; } public static bool IsRightMouseButton() { if (UnityEngine.Event.current == null) return false; if (UnityEngine.Event.current.type == UnityEngine.EventType.Used) if (UnityEngine.Event.current.button == 1) return true; return false; } /// Since remembering in which EditorGUI, EditorGUILayout, or EditorGUIUtility, or GUILayoutUtility ahhh... in which if these classes you will find the desired variable is so confusing ¯\_(ツ)_/¯ each time when trying finding it, ending in googling for forums post with it public static float InspectorViewWidth() { #if UNITY_EDITOR return EditorGUIUtility.currentViewWidth; #else return 0f; #endif } /// Since remembering in which EditorGUI, EditorGUILayout, or EditorGUIUtility, or GUILayoutUtility ahhh... in which if these classes you will find the desired variable is so confusing ¯\_(ツ)_/¯ each time when trying finding it, ending in googling for unity doc public static Rect GUILastRect() { #if UNITY_EDITOR return GUILayoutUtility.GetLastRect(); #else return new Rect(); #endif } /// /// Resetting local position, rotation, scale to zero on 1,1,1 (defaults) /// public static void ResetCoords(this Transform t) { t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; } /// /// Working event outside OnGUI /// public static Vector2 GetEventMousePosition() { #if UNITY_EDITOR var field = typeof(Event).GetField("s_Current", BindingFlags.Static | BindingFlags.NonPublic); if (field != null) { Event current = field.GetValue(null) as Event; if (current != null) { return EditorWindow.focusedWindow.position.position + current.mousePosition; } } #endif return Vector2.zero; } #if UNITY_EDITOR public static string Editor_GetActiveProjectBrowserFolderPath() { MethodInfo getActiveFolderPath = typeof(ProjectWindowUtil).GetMethod( "GetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic); string folderPath = (string)getActiveFolderPath.Invoke(null, null); return folderPath; } #endif #if UNITY_EDITOR public static void DropDownMenu(GenericMenu menu) { if (menu == null) return; if (UnityEngine.Event.current == null) return; menu.DropDown(new Rect(UnityEngine.Event.current.mousePosition + Vector2.left * 100, Vector2.zero)); } #endif #endregion #region Scriptable Related public static string lastPath = ""; public static string GetLastPath { get { #if UNITY_EDITOR if (lastPath == "") { if (EditorPrefs.HasKey("LastFGenSaveDir")) { lastPath = EditorPrefs.GetString("LastFGenSaveDir"); if (!System.IO.File.Exists(lastPath)) lastPath = Application.dataPath; } else lastPath = Application.dataPath; } #endif return lastPath; } } public static ScriptableObject GenerateScriptable(ScriptableObject reference, string exampleFilename = "", string playerPrefId = "LastFGenSaveDir") { #if UNITY_EDITOR if (lastPath == "") { if (EditorPrefs.HasKey(playerPrefId)) { lastPath = EditorPrefs.GetString(playerPrefId); if (!System.IO.File.Exists(lastPath)) lastPath = Application.dataPath; } else lastPath = Application.dataPath; } string path = UnityEditor.EditorUtility.SaveFilePanelInProject("Generate Preset File", exampleFilename, "asset", "Enter name of file", lastPath); try { if (path != "") { lastPath = System.IO.Path.GetDirectoryName(path); EditorPrefs.SetString(playerPrefId, lastPath); UnityEditor.AssetDatabase.CreateAsset(reference, path); } else reference = null; } catch (System.Exception) { reference = null; Debug.LogError("Something went wrong when creating scriptable file in your project."); } #endif return reference; } #if UNITY_EDITOR public static string RenamePopup(UnityEngine.Object asset, string startNameIfNull = "", bool assetDatabaseRename = true) { string startName = startNameIfNull; if (asset != null) startName = asset.name; string filename = EditorUtility.SaveFilePanelInProject("Type new name (no file will be created)", startName, "", "Type new name (no file will be created)"); if (!string.IsNullOrEmpty(filename)) { filename = System.IO.Path.GetFileNameWithoutExtension(filename); if (!string.IsNullOrEmpty(filename)) { if (asset != null) { if (assetDatabaseRename) AssetDatabase.RenameAsset(AssetDatabase.GetAssetPath(asset), filename); else asset.name = filename; asset.name = filename; EditorUtility.SetDirty(asset); AssetDatabase.SaveAssets(); } return filename; } } return ""; } public static string GetPathPopup(string windowTitle, string exampleName, string type = "asset", string playerPrefId = "LastFGenSaveDir") { string path = UnityEditor.EditorUtility.SaveFilePanelInProject(windowTitle, exampleName, type, "Enter name of file", GetLastPath); try { if (path != "") { lastPath = System.IO.Path.GetDirectoryName(path); EditorPrefs.SetString(playerPrefId, lastPath); return path; } } catch (System.Exception) { Debug.LogError("Something went wrong when getting path directory in your project."); } return path; } public static string GenerateScriptablePath(ScriptableObject reference, string exampleFilename = "", string playerPrefId = "LastFGenSaveDir") { if (lastPath == "") { if (EditorPrefs.HasKey(playerPrefId)) { lastPath = EditorPrefs.GetString(playerPrefId); if (!System.IO.File.Exists(lastPath)) lastPath = Application.dataPath; } else lastPath = Application.dataPath; } string path = UnityEditor.EditorUtility.SaveFilePanelInProject("Generate Preset File", exampleFilename, "asset", "Enter name of file", lastPath); try { if (path != "") { lastPath = System.IO.Path.GetDirectoryName(path); EditorPrefs.SetString(playerPrefId, lastPath); return path; } } catch (System.Exception) { Debug.LogError("Something went wrong when creating scriptable file in your project."); } return path; } #endif public static void DrawScriptableModificatorList(List toDraw, GUIStyle style, string title, ref bool foldout, bool newButton = false, bool moveButtons = false, UnityEngine.Object toDirty = null, string first = "[Base]", string defaultFilename = "New Scriptable File") where T : ScriptableObject { #if UNITY_EDITOR if (toDraw == null) return; EditorGUILayout.BeginVertical(style); EditorGUILayout.BeginHorizontal(); string fold = foldout ? " ▼" : " ►"; if (GUILayout.Button(fold + " " + title + " (" + toDraw.Count + ")", EditorStyles.label, GUILayout.Width(200))) foldout = !foldout; if (foldout) { GUILayout.FlexibleSpace(); if (GUILayout.Button("+")) toDraw.Add(null); } EditorGUILayout.EndHorizontal(); if (foldout) { GUILayout.Space(4); if (toDraw.Count > 0) for (int i = 0; i < toDraw.Count; i++) { EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginHorizontal(); GUIContent lbl = new GUIContent(i == 0 ? first : "[" + i + "]"); float wdth = EditorStyles.label.CalcSize(lbl).x; EditorGUILayout.LabelField(lbl, GUILayout.Width(wdth + 2)); toDraw[i] = (T)EditorGUILayout.ObjectField(toDraw[i], typeof(T), false); if (newButton) if (toDraw[i] == null) if (GUILayout.Button("N", GUILayout.Width(24))) toDraw[i] = (T)GenerateScriptable(ScriptableObject.CreateInstance(), defaultFilename); if (moveButtons) { if (i > 0) if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_ArrowUp, "Move this element to be executed before above one"), GUILayout.Width(24))) { T temp = toDraw[i - 1]; toDraw[i - 1] = toDraw[i]; toDraw[i] = temp; } if (i < toDraw.Count - 1) if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_ArrowDown, "Move this element to be executed after below one"), GUILayout.Width(24))) { T temp = toDraw[i + 1]; toDraw[i + 1] = toDraw[i]; toDraw[i] = temp; } } if (GUILayout.Button("X", GUILayout.Width(24))) { toDraw.RemoveAt(i); break; } EditorGUILayout.EndHorizontal(); if (toDirty != null) if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(toDirty); } } else { EditorGUILayout.LabelField("No object in list", EditorStyles.centeredGreyMiniLabel); } } EditorGUILayout.EndVertical(); #endif } public static void AddScriptableToSimple(ScriptableObject parent, ScriptableObject toAdd, bool reload = true) { #if UNITY_EDITOR try { AssetDatabase.AddObjectToAsset(toAdd, parent); EditorUtility.SetDirty(parent); if (reload) AssetDatabase.SaveAssets(); } catch (System.Exception e) { UnityEngine.Debug.LogException(e); throw; } #endif } public static bool AssetContainsAsset(UnityEngine.Object subAsset, UnityEngine.Object parentAsset) { #if UNITY_EDITOR if (parentAsset == null) return false; // Container asset is not in asset database then it can't contain any asset if (AssetDatabase.Contains(parentAsset) == false) return false; // Not sub asset then not contained for sure if (AssetDatabase.IsSubAsset(subAsset) == false) return false; string path = AssetDatabase.GetAssetPath(parentAsset); if (!string.IsNullOrEmpty(path)) { foreach (var asset in AssetDatabase.LoadAllAssetRepresentationsAtPath(path)) { if (asset == subAsset) return true; // Found subAsset in parentAsset } } #endif return false; // All assets inside parent asset checked and sub asset was not found } public static void AddScriptableTo(ScriptableObject toAdd, UnityEngine.Object parentAsset, bool checkIfAlreadyContains = true, bool reload = true) { #if UNITY_EDITOR try { if (parentAsset == null) return; if (AssetDatabase.Contains(parentAsset) == false) { UnityEngine.Debug.Log("[FGenerators Warning] '" + parentAsset.name + "' is not recognized by AssetDatabase!"); return; } if (AssetDatabase.IsSubAsset(toAdd)) { UnityEngine.Debug.Log("[FGenerators Warning] '" + toAdd.name + "' is already part of other asset in project!"); return; } if (checkIfAlreadyContains) { string path = AssetDatabase.GetAssetPath(parentAsset); if (!string.IsNullOrEmpty(path)) foreach (var asset in AssetDatabase.LoadAllAssetRepresentationsAtPath(path)) { if (asset == toAdd) { UnityEngine.Debug.Log("[FGenerators Warning] '" + toAdd.name + "' is already part of '" + parentAsset.name + "' asset!"); return; } } } if (AssetDatabase.GetAssetPath(toAdd) == AssetDatabase.GetAssetPath(parentAsset)) return; AssetDatabase.AddObjectToAsset(toAdd, parentAsset); EditorUtility.SetDirty(parentAsset); if (reload) AssetDatabase.SaveAssets(); } catch (System.Exception e) { UnityEngine.Debug.LogException(e); throw; } #endif } public static bool IsAssetSaved(UnityEngine.Object asset) { #if UNITY_EDITOR if (AssetDatabase.Contains(asset) == false) { AssetDatabase.SaveAssets(); if (AssetDatabase.Contains(asset) == true) return true; } else return true; #endif return false; } #endregion #region Utilities public static void SwapElements(List list, int index1, int index2, bool loop = false) { if (index1 == index2) return; if (loop) { if (index1 >= list.Count) index1 -= list.Count; if (index1 < 0) index1 += list.Count; if (index2 >= list.Count) index2 -= list.Count; if (index2 < 0) index2 += list.Count; } T temp = list[index1]; list[index1] = list[index2]; list[index2] = temp; } public static void SwapElements(T[] list, int index1, int index2) { if (index1 == index2) return; T temp = list[index1]; list[index1] = list[index2]; list[index2] = temp; } public static void CheckForNulls(List classes) { for (int i = classes.Count - 1; i >= 0; i--) { if (classes[i] == null) classes.RemoveAt(i); } } public static bool IndexInListRange(List list, int index) { if (list == null) return false; if (index < 0) return false; if (index >= list.Count) return false; return true; } public static T GetListElementOrNull(this List list, int index) where T : class { if (list == null) return null; if (index < 0) return null; if (index >= list.Count) return null; if (FGenerators.CheckIfIsNull(list[index])) return null; return list[index]; } public static void AdjustCount(List list, int targetCount, bool addNulls = false) where T : class, new() { if (list.Count == targetCount) return; if (list.Count < targetCount) { if (addNulls) { while (list.Count < targetCount) list.Add(null); } else { while (list.Count < targetCount) list.Add(new T()); } } else { while (list.Count > targetCount) list.RemoveAt(list.Count - 1); } //if (list.Count < targetCount) //{ // for (int i = 0; i < targetCount - list.Count; i++) // { // if (addNulls) // list.Add(null); // else // list.Add(new T()); // } //} //else //{ // for (int i = 0; i < list.Count - targetCount; i++) // { // list.RemoveAt(list.Count - 1); // } //} } public static void Shuffle(this IList list) { Shuffle(list, random); } public static void Shuffle(this IList list, System.Random random) { int n = list.Count; while (n > 1) { n--; int k = random.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } #if UNITY_EDITOR public static bool DrawAllPropsOf(SerializedObject so, bool addIndent = false) { if (so == null) return false; EditorGUI.BeginChangeCheck(); so.Update(); if (addIndent) EditorGUI.indentLevel++; SerializedProperty sp = so.GetIterator(); sp.Next(true); sp.Next(true); do { EditorGUILayout.PropertyField(sp); } while (sp.NextVisible(false)); if (addIndent) EditorGUI.indentLevel--; so.ApplyModifiedProperties(); if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(so.targetObject); return true; } return false; } public static void DrawAllPropsOf(SerializedProperty sp, bool addIndent = false) { if (addIndent) EditorGUI.indentLevel++; sp.Next(true); do { EditorGUILayout.PropertyField(sp); } while (sp.NextVisible(false)); if (addIndent) EditorGUI.indentLevel--; } public static void DrawSomePropsOf(SerializedProperty sp, int upTo, bool addIndent = false) { int count = 0; if (addIndent) EditorGUI.indentLevel++; sp.Next(true); do { EditorGUILayout.PropertyField(sp); count++; } while (sp.Next(false) && count < upTo); if (addIndent) EditorGUI.indentLevel--; } #endif #region UI Scale DPI private static float _editorUiScaling; public static float EditorUIScale { get { return GetEditorUIScale(); } } public static float GetEditorUIScale() { #if UNITY_EDITOR if (_editorUiScaling > 0.1f) return _editorUiScaling; System.Reflection.PropertyInfo p = typeof(GUIUtility).GetProperty("pixelsPerPoint", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); if (p != null) _editorUiScaling = (float)p.GetValue(null, null); else _editorUiScaling = 1f; return _editorUiScaling; #else return 1f; #endif } #endregion #region Other Editor Related public static readonly Color Color_Remove = new Color(1f, 0.825f, 0.825f, 1f); #if UNITY_EDITOR /// !!! EDITOR ONLY METHOD !!! public static void Editor_IteratorReload(SerializedProperty iterator, bool doNextEnter = true, bool deepCheck = false, bool fullCheck = false) { if (iterator == null) return; var refreshIterator = iterator.Copy(); if (doNextEnter) { if (refreshIterator.Next(true) == false) return; if (refreshIterator.NextVisible(false) == false) return; } Rect drawRect = new Rect(-1000, 0, 2, 2); if (fullCheck) { while (refreshIterator.Next(false)) { EditorGUI.PropertyField(drawRect, refreshIterator, true); var deppIter = refreshIterator.Copy(); if (deppIter.Next(true)) { while (deppIter.Next(false)) { EditorGUI.PropertyField(drawRect, deppIter, true); } } } } else if (deepCheck) { while (refreshIterator.Next(false)) { EditorGUI.PropertyField(drawRect, refreshIterator, true); } } else { while (refreshIterator.NextVisible(false)) { EditorGUI.PropertyField(drawRect, refreshIterator, true); } } } #endif #endregion #endregion } #region Prefab Helper /// /// Class helping drawing prefabs inside inspector window and referencing to them by FGenerators /// [System.Serializable] public class PrefabReference { public GameObject CoreGameObject { get { return Prefab; } } public Collider CoreCollider { get { return MainCollider; } } public GameObject GameObject { get { if (tempReplacePrefab != null) return tempReplacePrefab; return Prefab; } } public Collider Collider { get { if (tempReplaceCollider != null) return tempReplaceCollider; return MainCollider; } } [SerializeField] private GameObject Prefab; private GameObject tempReplacePrefab; [SerializeField] private Collider MainCollider; private Collider tempReplaceCollider; private int id; public int subID; private Texture tex; #region Prefab Ref Setup public Texture Preview { get { if (Prefab == null) { tex = null; return null; } if (tex == null || id != Prefab.GetInstanceID()) { id = Prefab.GetInstanceID(); #if UNITY_EDITOR tex = AssetPreview.GetAssetPreview(Prefab); #endif } return tex; } } protected virtual void DrawGUIWithPrefab(Color color, int previewSize = 72, string predicate = "", Action clickCallback = null, Action removeCallback = null, bool drawThumbnail = true, bool drawPrefabField = true) { #if UNITY_EDITOR if (drawThumbnail) { Color bc = GUI.backgroundColor; GUI.backgroundColor = color; if (GUILayout.Button(new GUIContent(Preview), opt)) if (clickCallback != null) clickCallback.Invoke(); GUI.backgroundColor = bc; } if (predicate != "_") EditorGUILayout.LabelField(predicate + Prefab.name, EditorStyles.centeredGreyMiniLabel, GUILayout.Width(previewSize)); EditorGUILayout.BeginHorizontal(); GameObject prepr = Prefab; if (drawPrefabField) Prefab = (GameObject)EditorGUILayout.ObjectField(Prefab, typeof(GameObject), false, removeCallback != null ? opt3 : opt2); if (Prefab != prepr) OnPrefabChanges(); if (removeCallback != null) if (GUILayout.Button("X", GUILayout.Width(20))) { removeCallback.Invoke(); return; } EditorGUILayout.EndHorizontal(); #endif } protected virtual void DrawGUIWithoutPrefab(int previewSize = 72, string predicate = "", Action removeCallback = null, bool drawPrefabField = true) { #if UNITY_EDITOR EditorGUILayout.BeginHorizontal(); if (drawPrefabField) Prefab = (GameObject)EditorGUILayout.ObjectField(Prefab, typeof(GameObject), false, removeCallback != null ? opt3 : opt2); if (Prefab) OnPrefabChanges(); if (removeCallback != null) if (GUILayout.Button("X", GUILayout.Width(20))) { removeCallback.Invoke(); return; } EditorGUILayout.EndHorizontal(); #endif } public virtual void OnPrefabChanges() { } public static GUILayoutOption[] opt; /// Height 18 public static GUILayoutOption[] opt2; /// Witdh-20 Height 18 public static GUILayoutOption[] opt3; public static bool StopReloadLayoutOptions = false; public static void DrawPrefabField(PrefabReference prefabRef, Color defaultColor, string predicate = "", int previewSize = 72, Action clickCallback = null, Action removeCallback = null, bool drawThumbnail = true, UnityEngine.Object toDiry = null, bool drawPrefabField = true, bool drawAdditionalButtons = true) { #if UNITY_EDITOR Color bc = GUI.backgroundColor; if (StopReloadLayoutOptions == false) { opt = new GUILayoutOption[] { GUILayout.Width(previewSize), GUILayout.Height(previewSize) }; opt2 = new GUILayoutOption[] { GUILayout.Width(previewSize), GUILayout.Height(18) }; opt3 = new GUILayoutOption[] { GUILayout.Width(previewSize - 20), GUILayout.Height(18) }; } EditorGUILayout.BeginVertical(GUILayout.Width(previewSize)); EditorGUI.BeginChangeCheck(); if (prefabRef.Prefab != null) prefabRef.DrawGUIWithPrefab(defaultColor, previewSize, predicate, clickCallback, removeCallback, drawThumbnail, drawPrefabField); else prefabRef.DrawGUIWithoutPrefab(previewSize, predicate, removeCallback, drawPrefabField); if (drawAdditionalButtons) { if (prefabRef != null) if (prefabRef.Prefab != null) { bool prefabIsModel = false; #if UNITY_EDITOR #if UNITY_2019_1_OR_NEWER var pfType = PrefabUtility.GetPrefabAssetType(prefabRef.Prefab); if (pfType == PrefabAssetType.Model) prefabIsModel = true; #endif #endif bool prefabHasCollider = prefabRef.MainCollider != null; if (prefabIsModel) { if (GUILayout.Button(new GUIContent(" Make", EditorGUIUtility.IconContent("Prefab Icon").image, "Generate prefab out of model file"), GUILayout.Width(64), GUILayout.Height(20))) { string path = AssetDatabase.GetAssetPath(prefabRef.Prefab); GameObject toSave = (GameObject)PrefabUtility.InstantiatePrefab(prefabRef.Prefab); toSave.name = "PR_" + Path.GetFileNameWithoutExtension(path); path = Path.GetDirectoryName(path); //path = path.Substring(0, path.LastIndexOf('/')); path = Path.Combine(path, toSave.name + ".prefab"); #if UNITY_2018_4_OR_NEWER PrefabUtility.SaveAsPrefabAsset(toSave, path); #else PrefabUtility.CreatePrefab(path, toSave); #endif if (toSave) GameObject.DestroyImmediate(toSave); prefabRef.Prefab = AssetDatabase.LoadAssetAtPath(path); } } else { if (prefabRef.Prefab.transform.rotation != Quaternion.identity) { EditorGUILayout.BeginVertical(); EditorGUILayout.HelpBox("Prefab rotation is not 0,0,0!", MessageType.None); if (GUILayout.Button(new GUIContent("FIX", "Setting prefab rotation to 0,0,0 but you can use rotation offset for advanced adjustments for spawning\nbut it's recommended to set 0,0,0 to avoid some unwanted not clear rotations"))) { prefabRef.Prefab.transform.rotation = Quaternion.identity; AssetDatabase.SaveAssets(); } EditorGUILayout.EndVertical(); } if (prefabHasCollider == false) { EditorGUILayout.BeginHorizontal(); if (GUILayout.Button(new GUIContent("+", EditorGUIUtility.IconContent("BoxCollider Icon").image, "Automatically add box collider to the prefab"), GUILayout.Width(32), GUILayout.Height(21))) { prefabHasCollider = prefabRef.GetCollider() != null; if (prefabHasCollider == false) { MeshFilter[] f = prefabRef.Prefab.GetComponentsInChildren(); float biggsetSize = 0f; MeshFilter biggest = null; for (int ff = 0; ff < f.Length; ff++) { if (f[ff].sharedMesh == null) continue; float sz = Vector3.Scale(f[ff].transform.lossyScale, f[ff].sharedMesh.bounds.size).magnitude; if (sz > biggsetSize) { biggsetSize = sz; biggest = f[ff]; } } if (biggest) { prefabRef.MainCollider = biggest.gameObject.AddComponent(); } else prefabRef.MainCollider = prefabRef.Prefab.AddComponent(); } AssetDatabase.SaveAssets(); } if (GUILayout.Button(new GUIContent("+", EditorGUIUtility.IconContent("MeshCollider Icon").image, "Automatically add mesh collider to the prefab"), GUILayout.Width(32), GUILayout.Height(21))) { prefabHasCollider = prefabRef.GetCollider() != null; if (prefabHasCollider == false) { MeshFilter[] f = prefabRef.Prefab.GetComponentsInChildren(); float biggsetSize = 0f; MeshFilter biggest = null; for (int ff = 0; ff < f.Length; ff++) { if (f[ff] == null) continue; if (f[ff].sharedMesh == null) continue; float sz = Vector3.Scale(f[ff].transform.lossyScale, f[ff].sharedMesh.bounds.size).magnitude; if (sz > biggsetSize) { biggsetSize = sz; biggest = f[ff]; } } if (biggest) { MeshCollider cl = biggest.gameObject.AddComponent(); cl.sharedMesh = biggest.sharedMesh; cl.convex = true; prefabRef.MainCollider = cl; } } AssetDatabase.SaveAssets(); } EditorGUILayout.EndHorizontal(); } } } } if (EditorGUI.EndChangeCheck()) if (toDiry != null) EditorUtility.SetDirty(toDiry); EditorGUILayout.EndVertical(); #endif } public static void DrawPrefabsList(List list, ref bool foldout, ref int selected, ref bool thumbnails, Color defaultC, Color selectedC, float viewWidth = 500f, int previewSize = 72, bool searchButtons = false, UnityEngine.Object toDirty = null, bool allowAdding = true) where T : PrefabReference, new() { #if UNITY_EDITOR if (list == null) return; EditorGUILayout.BeginVertical(FGUI_Resources.BGInBoxBlankStyle); EditorGUILayout.BeginHorizontal(); string f = FGUI_Resources.GetFoldSimbol(foldout); if (GUILayout.Button(f + " Prefabs (" + list.Count + ")", FGUI_Resources.FoldStyle, GUILayout.Height(20))) foldout = !foldout; if (foldout) { GUILayout.FlexibleSpace(); if (searchButtons) { if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_Rename), new GUILayoutOption[] { GUILayout.MaxWidth(24), GUILayout.Height(20) })) thumbnails = !thumbnails; if (list.Count > 0 && list[0] != null && list[0].Prefab != null) { if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_SearchNumeric, "Search for prefabs with the same name but with numbers"), new GUILayoutOption[] { GUILayout.MaxWidth(48), GUILayout.Height(20) })) { List pfs = new List(); pfs.Add(list[0].Prefab); FGenerators.GetIncrementalTo(pfs); for (int i = 0; i < pfs.Count; i++) { if (pfs[i] == null) continue; if (!list.Any(p => p.Prefab == pfs[i])) { list.Add(new T() { Prefab = pfs[i] }); if (toDirty != null) EditorUtility.SetDirty(toDirty); } } } if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_SearchDirectory, "Get all prefabs inside it's directory"), new GUILayoutOption[] { GUILayout.MaxWidth(48), GUILayout.Height(20) })) { List pfs = new List(); pfs.Add(list[0].Prefab); FGenerators.GetSimilarTo(pfs); for (int i = 0; i < pfs.Count; i++) { if (pfs[i] == null) continue; if (!list.Any(p => p.Prefab == pfs[i])) { list.Add(new T() { Prefab = pfs[i] }); if (toDirty != null) EditorUtility.SetDirty(toDirty); } } } } if (list.Count > 0) { Color prebc = GUI.backgroundColor; GUI.backgroundColor = new Color(1f, 0.635f, 0.635f, 1f); if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_Remove, "Remove all prefabs from list"), FGUI_Resources.ButtonStyle, new GUILayoutOption[] { GUILayout.MaxWidth(24), GUILayout.Height(19) })) list.Clear(); GUI.backgroundColor = prebc; } } if (allowAdding) if (GUILayout.Button("+", GUILayout.Width(24))) { list.Add(new T()); if (toDirty != null) EditorUtility.SetDirty(toDirty); } EditorGUILayout.EndHorizontal(); GUILayout.Space(10); float currWidth = 0f; if (list.Count > 0) EditorGUILayout.BeginHorizontal(); else EditorGUILayout.BeginHorizontal(GUILayout.Height(28)); int sel = selected; int toRemove = -1; for (int i = 0; i < list.Count; i++) { if (i == 1) StopReloadLayoutOptions = true; if (allowAdding) { if (selected == i) DrawPrefabField(list[i], selectedC, "[" + i + "] ", previewSize, () => { if (list[i] != null && list[i].Prefab) EditorGUIUtility.PingObject(list[i].Prefab); }, () => { toRemove = i; }, thumbnails, toDirty); else DrawPrefabField(list[i], defaultC, "[" + i + "] ", previewSize, () => { sel = i; }, () => { toRemove = i; }, thumbnails, toDirty); } else { DrawPrefabField(list[i], defaultC, "[" + i + "] ", previewSize, () => { sel = i; }, null, thumbnails); } currWidth += previewSize; if (currWidth + previewSize > viewWidth * 0.9f) { currWidth = 0f; EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); } } #region Drag & Drop if (allowAdding) { Color preCol = GUI.color; GUI.color = new Color(0.9f, 0.9f, 0.9f, 0.2f); var drop = GUILayoutUtility.GetRect(0f, 0f, new GUILayoutOption[] { GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true) }); GUIStyle d = FGUI_Inspector.Style(new RectOffset(0, 0, 0, 0), new RectOffset(0, 0, 0, 0), new Color(0.2f, 0.7f, 0.3f, 0.5f), Vector4.zero, 0); d = new GUIStyle(d) { alignment = TextAnchor.MiddleCenter }; GUI.Box(drop, new GUIContent(FGUI_Resources.Tex_Drag, "Drag & Drop prefabs here"), d); var dropEvent = UnityEngine.Event.current; if (dropEvent != null) { if (dropEvent.type == UnityEngine.EventType.DragPerform || dropEvent.type == UnityEngine.EventType.DragUpdated) if (drop.Contains(dropEvent.mousePosition)) { DragAndDrop.visualMode = DragAndDropVisualMode.Copy; if (dropEvent.type == UnityEngine.EventType.DragPerform) { DragAndDrop.AcceptDrag(); foreach (var dragged in DragAndDrop.objectReferences) { GameObject draggedPrefab = dragged as GameObject; if (draggedPrefab) { T pf = new T(); pf.Prefab = draggedPrefab; pf.GetMesh(true); list.Add(pf); if (toDirty != null) EditorUtility.SetDirty(toDirty); } } } UnityEngine.Event.current.Use(); } } GUI.color = preCol; } #endregion EditorGUILayout.EndHorizontal(); if (toRemove != -1) { list.RemoveAt(toRemove); if (toDirty != null) { EditorUtility.SetDirty(toDirty); AssetDatabase.SaveAssets(); } } selected = sel; if (selected > list.Count) selected = list.Count - 1; if (sel < 0) sel = 0; } else { GUILayout.FlexibleSpace(); if (list.Count > 0) { Color prebc = GUI.backgroundColor; GUI.backgroundColor = new Color(1f, 0.635f, 0.635f, 1f); if (GUILayout.Button(new GUIContent(FGUI_Resources.Tex_Remove, "Remove all prefabs from list"), FGUI_Resources.ButtonStyle, new GUILayoutOption[] { GUILayout.MaxWidth(24), GUILayout.Height(19) })) list.Clear(); GUI.backgroundColor = prebc; } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); StopReloadLayoutOptions = false; #endif } [HideInInspector][SerializeField] protected Mesh _refMesh; public Mesh GetMesh(bool refresh = false) { if (Prefab == null) return null; if (refresh) _refMesh = null; else if (_refMesh) { if (MainCollider == null) GetCollider(); return _refMesh; } List sk = FTransformMethods.FindComponentsInAllChildren(Prefab.transform); for (int i = 0; i < sk.Count; i++) if (sk[i]) if (sk[i].sharedMesh) { _refMesh = sk[i].sharedMesh; if (MainCollider == null) GetCollider(); return _refMesh; } List fs = FTransformMethods.FindComponentsInAllChildren(Prefab.transform); for (int i = 0; i < fs.Count; i++) if (fs[i]) if (fs[i].sharedMesh) { _refMesh = fs[i].sharedMesh; if (MainCollider == null) GetCollider(); return _refMesh; } if (MainCollider == null) { MainCollider = FTransformMethods.FindComponentInAllChildren(Prefab.transform); } return _refMesh; } [HideInInspector][SerializeField] protected Collider _refCol; public Collider GetCollider() { if (Prefab == null) return null; if (_refCol) { if (MainCollider == null) MainCollider = _refCol; return _refCol; } List sk = FTransformMethods.FindComponentsInAllChildren(Prefab.transform); for (int i = 0; i < sk.Count; i++) if (sk[i]) { _refCol = sk[i]; if (MainCollider == null) MainCollider = _refCol; return _refCol; } if (_refCol == null) _refCol = Prefab.GetComponent(); if (MainCollider == null) MainCollider = _refCol; return _refCol; } #endregion public void SetPrefab(GameObject pf) { Prefab = pf; } public void SetCollider(Collider pf) { MainCollider = pf; } public void TemporaryReplace(GameObject tempRepl) { if (tempRepl == null) { tempReplacePrefab = null; tempReplaceCollider = null; } else { tempReplacePrefab = tempRepl; tempReplaceCollider = tempRepl.GetComponentInChildren(); } } } #endregion #region Scene Draw Helper #endregion }