#if UNITY_EDITOR using UnityEngine; using UnityEditor; using System.Collections.Generic; namespace GSpawn { public static class PrefabFactory { private static List _objectBuffer = new List(); public static bool create(List rootObjectGroups, PrefabsFromObjectGroupsCreationSettings settings, List createdPrefabAssets) { if (createdPrefabAssets != null) createdPrefabAssets.Clear(); string errorTitle = "Prefab Creation Failed"; if (string.IsNullOrEmpty(settings.destinationFolder)) { EditorUtility.DisplayDialog(errorTitle, "Invalid prefab destination folder.", "Ok"); return false; } if (!FileSystem.folderExists(settings.destinationFolder)) { EditorUtility.DisplayDialog(errorTitle, "The specified prefab destination folder does not exist.", "Ok"); return false; } PluginProgressDialog.begin("Creating Prefabs"); PluginProgressDialog.updateProgress("Creating prefabs from object groups...", 0.0f); for (int rootIndex = 0; rootIndex < rootObjectGroups.Count; ++rootIndex) { var rootGroup = rootObjectGroups[rootIndex]; GameObject rootClone = cloneObjectGroup(rootGroup.gameObject); cloneObjectGroupChildrenRecurse(rootClone, rootGroup.gameObject); PluginProgressDialog.updateProgress(rootGroup.gameObject.name, (rootIndex + 1) / (float)rootObjectGroups.Count); string prefabPath = settings.destinationFolder + "/" + rootClone.name + ".prefab"; GameObject prefab = AssetDbEx.loadPrefab(prefabPath); if (prefab != null) { if (!handlePrefabAlreadyExists(prefabPath, prefab, rootClone, true)) continue; } GameObject prefabAsset = tryCreatePrefab(prefabPath, rootClone, true); if (prefabAsset != null) { if (createdPrefabAssets != null) createdPrefabAssets.Add(prefabAsset); } } PluginProgressDialog.updateProgress("Done.", 1.0f); PluginProgressDialog.end(); return true; } public static GameObject create(List parents, PrefabFromSelectedObjectsCreationSettings settings) { string errorTitle = "Prefab Creation Failed"; if (string.IsNullOrEmpty(settings.prefabName)) { EditorUtility.DisplayDialog(errorTitle, "No prefab name was specified.", "Ok"); return null; } if (string.IsNullOrEmpty(settings.destinationFolder)) { EditorUtility.DisplayDialog(errorTitle, "Invalid prefab destination folder.", "Ok"); return null; } if (!FileSystem.folderExists(settings.destinationFolder)) { EditorUtility.DisplayDialog(errorTitle, "The specified prefab destination folder does not exist.", "Ok"); return null; } string prefabPath = settings.destinationFolder + "/" + settings.prefabName + ".prefab"; GameObject prefab = AssetDbEx.loadPrefab(prefabPath); if (prefab != null) { if (!handlePrefabAlreadyExists(prefabPath, prefab, null, false)) return null; } _objectBuffer.Clear(); bool searchForPivot = !string.IsNullOrEmpty(settings.pivotObjectName); PluginProgressDialog.begin("Creating Prefab"); PluginProgressDialog.updateProgress("Cloning objects...", 0.0f); List clonedParents = new List(); GameObject pivotObject = null; foreach (GameObject parent in parents) { GameObject parentClone = GameObject.Instantiate(parent); parentClone.name = parent.name; parentClone.transform.localScale = parent.transform.lossyScale; clonedParents.Add(parentClone); if (searchForPivot && pivotObject == null) { parent.getAllChildrenAndSelf(false, false, _objectBuffer); foreach (var c in _objectBuffer) { if (c.name == settings.pivotObjectName) { pivotObject = c; break; } } } } PluginProgressDialog.updateProgress("Calculating positions...", 0.33f); AABB pivotAABB = pivotObject != null ? ObjectBounds.calcWorldAABB(pivotObject, ObjectBounds.QueryConfig.defaultConfig) : ObjectBounds.calcHierarchiesWorldAABB(parents, ObjectBounds.QueryConfig.defaultConfig); GameObject root = new GameObject(settings.prefabName); if (settings.pivot == PrefabCreationPivot.Center || (pivotObject == null && settings.pivot == PrefabCreationPivot.FromPivotObject)) root.transform.position = pivotAABB.center; else if (settings.pivot == PrefabCreationPivot.CenterBottom) root.transform.position = Box3D.calcFaceCenter(pivotAABB.center, pivotAABB.size, Quaternion.identity, Box3DFace.Bottom); else if (settings.pivot == PrefabCreationPivot.CenterTop) root.transform.position = Box3D.calcFaceCenter(pivotAABB.center, pivotAABB.size, Quaternion.identity, Box3DFace.Top); else if (settings.pivot == PrefabCreationPivot.CenterBack) root.transform.position = Box3D.calcFaceCenter(pivotAABB.center, pivotAABB.size, Quaternion.identity, Box3DFace.Back); else if (settings.pivot == PrefabCreationPivot.CenterFront) root.transform.position = Box3D.calcFaceCenter(pivotAABB.center, pivotAABB.size, Quaternion.identity, Box3DFace.Front); else if (settings.pivot == PrefabCreationPivot.CenterLeft) root.transform.position = Box3D.calcFaceCenter(pivotAABB.center, pivotAABB.size, Quaternion.identity, Box3DFace.Left); else if (settings.pivot == PrefabCreationPivot.CenterRight) root.transform.position = Box3D.calcFaceCenter(pivotAABB.center, pivotAABB.size, Quaternion.identity, Box3DFace.Right); else if (settings.pivot == PrefabCreationPivot.FromPivotObject) root.transform.position = pivotObject.transform.position; else if (settings.pivot == PrefabCreationPivot.TileRule) { if (pivotObject == null) { // Pick the parent with the largest volume. This will be treated as the // main object (e.g. a platform/floor or something like that). The rest // of the objects are assumed to be decorations. AABB mainAABB = AABB.getInvalid(); float largestVolume = float.MinValue; foreach (GameObject parent in parents) { AABB parentAABB = ObjectBounds.calcWorldAABB(parent, ObjectBounds.QueryConfig.defaultConfig); if (!parentAABB.isValid) continue; if (parentAABB.volume > largestVolume) { largestVolume = parentAABB.volume; mainAABB = parentAABB; } } if (mainAABB.isValid) { Vector3 aabbCenter = mainAABB.center; root.transform.position = new Vector3(aabbCenter.x, mainAABB.min.y, aabbCenter.z); } else root.transform.position = parents[0].transform.position; } else root.transform.position = new Vector3(pivotAABB.center.x, pivotAABB.min.y, pivotAABB.center.z); } foreach (var clonedParent in clonedParents) clonedParent.transform.parent = root.transform; root.transform.position = Vector3.zero; PluginProgressDialog.updateProgress("Creating prefab asset...", 0.66f); GameObject prefabAsset = tryCreatePrefab(prefabPath, root, true); PluginProgressDialog.updateProgress("Done.", 1.0f); PluginProgressDialog.end(); return prefabAsset; } private static GameObject cloneObjectGroup(GameObject objectGroup) { GameObject clone = new GameObject(objectGroup.name); clone.layer = objectGroup.layer; clone.tag = objectGroup.tag; clone.transform.position = objectGroup.transform.position; clone.transform.rotation = objectGroup.transform.rotation; clone.transform.localScale = objectGroup.transform.localScale; return clone; } private static void cloneObjectGroupChildrenRecurse(GameObject clonedParentGroup, GameObject originalParentGroup) { int numChildren = originalParentGroup.transform.childCount; for (int i = 0; i < numChildren; ++i) { GameObject child = originalParentGroup.transform.GetChild(i).gameObject; if (ObjectGroupDb.instance.isObjectGroup(child)) { var clonedChild = cloneObjectGroup(child); clonedChild.transform.SetParent(clonedParentGroup.transform); cloneObjectGroupChildrenRecurse(clonedChild, child); } } } private static GameObject tryCreatePrefab(string prefabPath, GameObject sourceObject, bool destroySource) { GameObject prefabAsset = null; try { prefabAsset = sourceObject.saveAsPrefabAsset(prefabPath); if (destroySource) GameObject.DestroyImmediate(sourceObject); } catch { if (destroySource) GameObject.DestroyImmediate(sourceObject); } return prefabAsset; } private static bool handlePrefabAlreadyExists(string prefabPath, GameObject prefabAsset, GameObject sourceObject, bool destroySource) { if (!EditorUtility.DisplayDialog("Overwrite Prefab?", "A prefab with the same name (" + prefabAsset.name + ") already exists " + "in the specified folder. Would you like to overwrite it?", "Yes", "No")) { if (destroySource && sourceObject != null) GameObject.DestroyImmediate(sourceObject); return false; } prefabAsset.disconnectPrefabInstances(); AssetDatabase.DeleteAsset(prefabPath); return true; } } } #endif