using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; public class DeleteDuplicatedObjectsEditorWindow : EditorWindow { private int deleted; private int skipped; private int overallObjects; [MenuItem("Window/Delete Duplicates")] private static void ShowWindow() { var window = GetWindow(); window.titleContent = new GUIContent("Delete Duplicates"); window.Show(); } private void OnGUI() { if (GUILayout.Button("Delete Duplicates")) { OnDeleteDuplicates(); } if (GUILayout.Button("Delete Everything")) { OnDeleteEverything(); } GUILayout.Label($"Total number objects {overallObjects}"); GUILayout.Label($"Deleted items: {deleted}"); GUILayout.Label($"Skipped items: {skipped}"); //TODO: maybe show ref to skipped items } private void OnDeleteEverything() { var objects = FindObjectsOfType(); foreach (var meshRenderer in objects) { if (meshRenderer == null) continue; var prefab = PrefabUtility.GetOutermostPrefabInstanceRoot(meshRenderer.gameObject); DestroyImmediate(prefab != null ? prefab : meshRenderer.gameObject); } } private void OnDeleteDuplicates() { skipped = 0; deleted = 0; List toDelete = new List(); var objects = FindObjectsOfType(); var objectsAtPos = new Dictionary>(); overallObjects = objects.Length; Debug.Log($"Found {overallObjects} GameObjects"); foreach (var o in objects) { var pos = o.transform.position; if (!objectsAtPos.ContainsKey(pos)) { var l = new List {o.gameObject}; objectsAtPos[pos] = l; } else { objectsAtPos[pos].Add(o.gameObject); } } Debug.Log($"Done sorting"); var objs = objectsAtPos.Values.Where(l => l.Count > 1); var enumerable = objs as List[] ?? objs.ToArray(); Debug.LogWarning($"{enumerable.Count()} gameobjects at exact same position"); Debug.Log("Deleting objects.."); foreach (var o in enumerable) { var rMap = new Dictionary>(); foreach (var d in o) { if (d == null) continue; var rot = d.transform.rotation; if (rMap.ContainsKey(rot)) { rMap[rot].Add(d); } else { rMap[rot] = new List {d}; } //Destroy(d); } var samePosAndRot = rMap.Values.Max(v => v.Count); Debug.Log($"max same pos and rot = {samePosAndRot}"); if (samePosAndRot < 2) continue; var resultsWithMax = rMap.Values.Where(l => l.Count == samePosAndRot); foreach (var r in resultsWithMax) { Debug.Log($"Names: {string.Join(",", r.Select(x => x.name))}"); if (r.Aggregate((result, item) => { if (result == null) return null; else { var rIndex = result.name.IndexOf('('); var iIndex = item.name.IndexOf('('); var rBase = rIndex > 0 ? result.name.Substring(0, rIndex) : result.name; var iBase = iIndex > 0 ? item.name.Substring(0, iIndex) : item.name; return rBase.Equals(iBase) ? item : null; } }) != null) { for (var index = 1; index < r.Count; index++) { var gameObject1 = r[index]; if (gameObject1.transform.childCount == 0) { var prefabParent = PrefabUtility.GetOutermostPrefabInstanceRoot(gameObject1); if (prefabParent != null) { Debug.LogWarning($"Destroyed prefab {prefabParent.name}!"); //DestroyImmediate(prefabParent); toDelete.Add(prefabParent); } else { toDelete.Add(gameObject1); //DestroyImmediate(gameObject1); } } else { Debug.LogError($"Did not destroy {gameObject1.name}"); skipped++; } deleted++; } } } } foreach (var d in toDelete) { if (d != null) { DestroyImmediate(d); } } Debug.LogWarning($"Deleted {deleted} items!"); Debug.LogWarning($"Skipped {skipped} items!"); } }