static IEnumerable <PrefabAssembler> GetAssemblerHierarchy( PrefabAssembler assembler, Dictionary <PrefabAssembler, List <PrefabAssembler> > prefabs, HashSet <PrefabAssembler> used) { if (!used.Add(assembler)) { yield break; } yield return(assembler); List <PrefabAssembler> p = null; if (prefabs.TryGetValue(assembler, out p)) { foreach (var a in p) { foreach (var b in GetAssemblerHierarchy(a, prefabs, used)) { yield return(b); } } } }
static IEnumerable <PrefabAssembler> GetAssemblerDependencies(HashSet <PrefabAssembler> found, params PrefabAssembler[] assemblers) { var assemblersByPrefab = GetAllAssemblersByPrefab(); foreach (PrefabAssembler a in assemblers) { if (found.Add(a)) { yield return(a); } foreach (var obj in GetPrefabs(a.transform)) { PrefabAssembler b = null; if (assemblersByPrefab.TryGetValue(obj, out b)) { if (found.Add(b)) { yield return(b); foreach (var p in GetAssemblerDependencies(found, b)) { yield return(p); } } } } } }
/// <summary> /// Performs a search of the scene for any other prefab assemblers which are dependant on the given prefabs /// Uses the recursive GetAssemblerHierarchy with the same name /// </summary> static IEnumerable <PrefabAssembler> GetAssemblerHierarchy(params PrefabAssembler[] assemblers) { var assemblersByPrefab = GetAllAssemblersByPrefab(); var assemblerChildren = new Dictionary <PrefabAssembler, List <PrefabAssembler> >(); foreach (var kvp in assemblersByPrefab) { foreach (var obj in GetPrefabs(kvp.Value.transform)) { PrefabAssembler b = null; if (assemblersByPrefab.TryGetValue(obj, out b)) { List <PrefabAssembler> list = null; if (!assemblerChildren.TryGetValue(b, out list)) { list = new List <PrefabAssembler>(); assemblerChildren.Add(b, list); } list.Add(kvp.Value); } } } var used = new HashSet <PrefabAssembler>(); foreach (var a in assemblers) { foreach (var b in GetAssemblerHierarchy(a, assemblerChildren, used)) { yield return(b); } } }
public override void OnInspectorGUI() { EditorGUIUtility.fieldWidth = 100; EditorGUIUtility.labelWidth = 60; serializedObject.Update(); var assemblers = new PrefabAssembler[targets.Length]; for (int i = 0; i < assemblers.Length; i++) { assemblers[i] = (PrefabAssembler)targets[i]; } GUILayout.Space(5); if (assemblers.Length == 1) { var assembler = assemblers[0]; EditorGUILayout.BeginHorizontal(); GUILayout.Space(14); DrawPrefabField(assembler); if (GUILayout.Button("Browse", GUILayout.Width(60))) { BrowsePrefabPath(assembler); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(12); EditorGUILayout.BeginVertical(); AdvancedSettings(); EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } else { for (int i = 0; i < assemblers.Length; i++) { var assembler = assemblers[i]; EditorGUILayout.BeginHorizontal(); GUILayout.Space(14); DrawPrefabField(assembler); EditorGUILayout.EndHorizontal(); } } serializedObject.ApplyModifiedProperties(); }
/// <summary> /// Assemble a single prefab. /// </summary> /// <param name="assembler">The prefab to assemble.</param> public static void Assemble(PrefabAssembler assembler) { try { AssembleInternal(assembler); } catch (Exception e) { Debug.LogError(e, assembler); } }
void DrawPrefabField(PrefabAssembler assembler) { if (!assembler.prefab) { EditorGUILayout.LabelField("Target:", "None Assigned"); } else { EditorGUILayout.ObjectField("Target:", assembler.prefab, typeof(GameObject), false); } }
static void BrowsePrefabPath(PrefabAssembler assembler) { var curPath = assembler.prefab ? AssetDatabase.GetAssetPath(assembler.prefab) : !string.IsNullOrEmpty(EditorApplication.currentScene) ? EditorApplication.currentScene : "Assets/"; var newPath = EditorUtility.SaveFilePanelInProject("Pick Prefab Path", assembler.name, "prefab", "Pick a location to save the prefab.", curPath); if (newPath != null && newPath != "" && newPath != curPath) { PrefabAssemblerUtility.SetAssemblerTarget(assembler, newPath); } }
static void InstanceAssemblerMenuItem() { if (Selection.objects.Length != 1) { return; } if (!Selection.activeGameObject) { return; } PrefabAssembler assembler = Selection.activeGameObject.GetComponent <PrefabAssembler>(); if (!assembler) { var path = EditorUtility.SaveFilePanelInProject("Pick Prefab Path", Selection.activeGameObject.name, "prefab", "Pick a location to save the prefab."); if (path == null || path == "") { return; } assembler = Selection.activeGameObject.AddComponent <PrefabAssembler>(); var t = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; if (t) { assembler.prefab = t; PrefabAssemblerUtility.Assemble(assembler); } else { var go = new GameObject(); assembler.prefab = PrefabUtility.CreatePrefab(path, go); PrefabAssemblerUtility.Assemble(assembler); GameObject.DestroyImmediate(go); } } var instance = PrefabUtility.InstantiatePrefab(assembler.prefab) as GameObject; instance.transform.position = assembler.transform.position; instance.transform.rotation = assembler.transform.rotation; instance.transform.parent = assembler.transform.parent; Selection.activeObject = assembler.gameObject; }
public static void SetAssemblerTarget (PrefabAssembler assembler, string path) { var t = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; if(t) { assembler.prefab = t; } else { var go = new GameObject(); assembler.prefab = PrefabUtility.CreatePrefab(path, go); PrefabAssemblerUtility.Assemble(assembler); EditorUtility.SetDirty(assembler); AssetDatabase.Refresh(); GameObject.DestroyImmediate(go); } }
public static void SetAssemblerTarget(PrefabAssembler assembler, string path) { var t = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; if (t) { assembler.prefab = t; } else { var go = new GameObject(); assembler.prefab = PrefabUtility.CreatePrefab(path, go); PrefabAssemblerUtility.Assemble(assembler); EditorUtility.SetDirty(assembler); AssetDatabase.Refresh(); GameObject.DestroyImmediate(go); } }
/// <summary> /// Assembles a single prefab, without exception handling /// </summary> /// <param name="assembler">The prefab to assemble.</param> static void AssembleInternal(PrefabAssembler assembler) { var asyncAssemble = AssembleInternalAsync(assembler); bool moveNext = true; while (moveNext) { try { moveNext = asyncAssemble.MoveNext(); } catch (Exception e) { Debug.LogError(e); return; } } }
void DrawPrefabField (PrefabAssembler assembler) { if(!assembler.prefab) { EditorGUILayout.LabelField("Target:", "None Assigned"); } else { EditorGUILayout.ObjectField("Target:", assembler.prefab, typeof(GameObject), false); } }
public override void OnInspectorGUI () { EditorGUIUtility.fieldWidth = 100; EditorGUIUtility.labelWidth = 60; serializedObject.Update(); var assemblers = new PrefabAssembler[targets.Length]; for(int i = 0; i < assemblers.Length; i++) { assemblers[i] = (PrefabAssembler)targets[i]; } GUILayout.Space(5); if(assemblers.Length == 1) { var assembler = assemblers[0]; EditorGUILayout.BeginHorizontal(); GUILayout.Space(14); DrawPrefabField(assembler); if(GUILayout.Button("Browse", GUILayout.Width(60))) { BrowsePrefabPath(assembler); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(12); EditorGUILayout.BeginVertical(); AdvancedSettings(); EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } else { for(int i = 0; i < assemblers.Length; i++) { var assembler = assemblers[i]; EditorGUILayout.BeginHorizontal(); GUILayout.Space(14); DrawPrefabField(assembler); EditorGUILayout.EndHorizontal(); } } serializedObject.ApplyModifiedProperties(); }
static void BrowsePrefabPath (PrefabAssembler assembler) { var curPath = assembler.prefab ? AssetDatabase.GetAssetPath(assembler.prefab) : !string.IsNullOrEmpty(EditorApplication.currentScene) ? EditorApplication.currentScene : "Assets/"; var newPath = EditorUtility.SaveFilePanelInProject("Pick Prefab Path", assembler.name, "prefab", "Pick a location to save the prefab.", curPath); if(newPath != null && newPath != "" && newPath != curPath) { PrefabAssemblerUtility.SetAssemblerTarget(assembler, newPath); } }
/// <summary> /// Assemble a single prefab. /// </summary> /// <param name="assembler">The prefab to assemble.</param> public static void Assemble (PrefabAssembler assembler) { try { AssembleInternal(assembler); } catch(Exception e) { Debug.LogError(e, assembler); } }
/// <summary> /// Assembles a single prefab, without exception handling /// </summary> /// <param name="assembler">The prefab to assemble.</param> static void AssembleInternal (PrefabAssembler assembler) { var asyncAssemble = AssembleInternalAsync(assembler); bool moveNext = true; while(moveNext) { try { moveNext = asyncAssemble.MoveNext(); } catch(Exception e) { Debug.LogError(e); return; } } }
static IEnumerator<PrefabAssembleProgress> AssembleInternalAsync (PrefabAssembler assembler) { if(!assembler.prefab) { yield break; } PrefabAssembler.Current = assembler; yield return new PrefabAssembleProgress(0f, "Pre-assembly"); object[] parameters = new object[0]; var allBehaviours = GetBehavioursHierarchial(assembler.gameObject); foreach(var mb in allBehaviours) { if(mb == null) continue; var mbType = mb.GetType(); if(mbType == null) continue; var method = GetMethod(mbType, "OnPreAssemble"); if(method != null && method.GetParameters().Length == 0) { if(method.ReturnType == typeof(IEnumerator<PrefabAssembleProgress>)) { var asyncMethod = (IEnumerator<PrefabAssembleProgress>)method.Invoke(mb, parameters); if(asyncMethod != null) { while(asyncMethod.MoveNext()) { var progress = asyncMethod.Current; progress.progress = Mathf.Lerp(0f, 0.2f, progress.progress); yield return progress; } } } else { method.Invoke(mb, parameters); } } } yield return new PrefabAssembleProgress(0.2f, "Cloning"); var instance = GameObject.Instantiate(assembler) as PrefabAssembler; instance.name = assembler.prefab.name; var gameObject = instance.gameObject; gameObject.name = assembler.name; yield return new PrefabAssembleProgress(0.3333f, "Assembling"); allBehaviours = GetBehavioursHierarchial(gameObject); foreach(var mb in allBehaviours) { if(mb == null) continue; var mbType = mb.GetType(); if(mbType == null) continue; var method = GetMethod(mbType, "OnAssemble"); if(method != null && method.GetParameters().Length == 0) { if(method.ReturnType == typeof(IEnumerator<PrefabAssembleProgress>)) { var asyncMethod = (IEnumerator<PrefabAssembleProgress>)method.Invoke(mb, parameters); if(asyncMethod != null) { bool moveNext = true; while(moveNext) { try { moveNext = asyncMethod.MoveNext(); } catch(Exception e) { GameObject.DestroyImmediate(gameObject); throw e; // throw e.InnerException; } var progress = asyncMethod.Current; progress.progress = Mathf.Lerp(0.3333f, 0.6f, progress.progress); yield return progress; } } } else { try { method.Invoke(mb, parameters); } catch { GameObject.DestroyImmediate(gameObject); throw; } } } } yield return new PrefabAssembleProgress(0.6f, "Applying"); PrefabUtility.ReplacePrefab(gameObject, assembler.prefab, ReplacePrefabOptions.ReplaceNameBased); yield return new PrefabAssembleProgress(0.8f, "Post-assembly"); allBehaviours = GetBehavioursHierarchial((GameObject)assembler.prefab); foreach(var mb in allBehaviours) { if(mb == null) continue; var mbType = mb.GetType(); if(mbType == null) continue; var method = GetMethod(mbType, "OnPostAssemble"); if(method != null && method.GetParameters().Length == 0) { if(method.ReturnType == typeof(IEnumerator<PrefabAssembleProgress>)) { var asyncMethod = (IEnumerator<PrefabAssembleProgress>)method.Invoke(mb, parameters); if(asyncMethod != null) { while(asyncMethod.MoveNext()) { var progress = asyncMethod.Current; progress.progress = Mathf.Lerp(0.8f, 0.95f, progress.progress); yield return progress; } } } else { method.Invoke(mb, parameters); } } } var labels = AssetDatabase.GetLabels(assembler.prefab); bool found = false; for(int i = 0; i < labels.Length; i++) { var label = labels[i]; if(label.StartsWith("Stage: ")) { labels[i] = GenerateLabel(); AssetDatabase.SetLabels(assembler.prefab, labels); found = true; break; } } if(!found) { var list = new List<string>(labels); list.Add(GenerateLabel()); AssetDatabase.SetLabels(assembler.prefab, list.ToArray()); } yield return new PrefabAssembleProgress(0.95f, "Cleanup"); GameObject.DestroyImmediate(gameObject); PrefabAssembler.Current = null; yield break; }
static IEnumerable<PrefabAssembler> GetAssemblerHierarchy ( PrefabAssembler assembler, Dictionary<PrefabAssembler, List<PrefabAssembler>> prefabs, HashSet<PrefabAssembler> used) { if(!used.Add(assembler)) { yield break; } yield return assembler; List<PrefabAssembler> p = null; if(prefabs.TryGetValue(assembler, out p)) { foreach(var a in p) { foreach(var b in GetAssemblerHierarchy(a, prefabs, used)) { yield return b; } } } }
/// <summary> /// Assemble an array of prefabs. Sorted by priority. /// </summary> /// <param name="assemblers">The array of prefabs to assemble.</param> public static void Assemble (PrefabAssembler[] assemblers) { if(assemblers.Length == 0) { Debug.Log("No prefabs to assemble."); return; } Array.Sort<PrefabAssembler>(assemblers, (x,y) => { return x.priority - y.priority; }); try { var log = new System.Text.StringBuilder("Assembling prefabs: "); var errors = new Dictionary<PrefabAssembler, Exception>(); float assemblyLength = 1f/assemblers.Length; for(int i = 0; i < assemblers.Length; i++) { var assembler = assemblers[i]; if(!assembler) { continue; } if(!assembler.prefab) { continue; } string assemblyDescription = "Assembling " + assembler.name + " into " + assembler.prefab.name + ".prefab ({0})"; float assemblyPos = (float)i/assemblers.Length; string message = null; if(i != 0) log.Append(", "); log.Append(assembler.name); try { var asyncAssembly = AssembleInternalAsync(assembler); while(asyncAssembly.MoveNext()) { var progress = asyncAssembly.Current; if(message == null || progress.message != null) { message = string.Format(assemblyDescription, progress.message); } EditorUtility.DisplayProgressBar("Assembling Prefabs...", message, assemblyPos + (progress.progress * assemblyLength)); } } catch(Exception e) { errors.Add(assembler, e); } } Debug.Log(log.ToString()); if(errors.Count != 0) { foreach(var kvp in errors) { Debug.LogException(kvp.Value, kvp.Key); } Debug.LogError("Prefab assembly completed with errors (see above)"); } } finally { EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); } }
static IEnumerator <PrefabAssembleProgress> AssembleInternalAsync(PrefabAssembler assembler) { if (!assembler.prefab) { yield break; } PrefabAssembler.Current = assembler; yield return(new PrefabAssembleProgress(0f, "Pre-assembly")); object[] parameters = new object[0]; var allBehaviours = GetBehavioursHierarchial(assembler.gameObject); foreach (var mb in allBehaviours) { if (mb == null) { continue; } var mbType = mb.GetType(); if (mbType == null) { continue; } var method = GetMethod(mbType, "OnPreAssemble"); if (method != null && method.GetParameters().Length == 0) { if (method.ReturnType == typeof(IEnumerator <PrefabAssembleProgress>)) { var asyncMethod = (IEnumerator <PrefabAssembleProgress>)method.Invoke(mb, parameters); if (asyncMethod != null) { while (asyncMethod.MoveNext()) { var progress = asyncMethod.Current; progress.progress = Mathf.Lerp(0f, 0.2f, progress.progress); yield return(progress); } } } else { method.Invoke(mb, parameters); } } } yield return(new PrefabAssembleProgress(0.2f, "Cloning")); var instance = GameObject.Instantiate(assembler) as PrefabAssembler; instance.name = assembler.prefab.name; var gameObject = instance.gameObject; gameObject.name = assembler.name; yield return(new PrefabAssembleProgress(0.3333f, "Assembling")); allBehaviours = GetBehavioursHierarchial(gameObject); foreach (var mb in allBehaviours) { if (mb == null) { continue; } var mbType = mb.GetType(); if (mbType == null) { continue; } var method = GetMethod(mbType, "OnAssemble"); if (method != null && method.GetParameters().Length == 0) { if (method.ReturnType == typeof(IEnumerator <PrefabAssembleProgress>)) { var asyncMethod = (IEnumerator <PrefabAssembleProgress>)method.Invoke(mb, parameters); if (asyncMethod != null) { bool moveNext = true; while (moveNext) { try { moveNext = asyncMethod.MoveNext(); } catch (Exception e) { GameObject.DestroyImmediate(gameObject); throw e; // throw e.InnerException; } var progress = asyncMethod.Current; progress.progress = Mathf.Lerp(0.3333f, 0.6f, progress.progress); yield return(progress); } } } else { try { method.Invoke(mb, parameters); } catch { GameObject.DestroyImmediate(gameObject); throw; } } } } yield return(new PrefabAssembleProgress(0.6f, "Applying")); PrefabUtility.ReplacePrefab(gameObject, assembler.prefab, ReplacePrefabOptions.ReplaceNameBased); yield return(new PrefabAssembleProgress(0.8f, "Post-assembly")); allBehaviours = GetBehavioursHierarchial((GameObject)assembler.prefab); foreach (var mb in allBehaviours) { if (mb == null) { continue; } var mbType = mb.GetType(); if (mbType == null) { continue; } var method = GetMethod(mbType, "OnPostAssemble"); if (method != null && method.GetParameters().Length == 0) { if (method.ReturnType == typeof(IEnumerator <PrefabAssembleProgress>)) { var asyncMethod = (IEnumerator <PrefabAssembleProgress>)method.Invoke(mb, parameters); if (asyncMethod != null) { while (asyncMethod.MoveNext()) { var progress = asyncMethod.Current; progress.progress = Mathf.Lerp(0.8f, 0.95f, progress.progress); yield return(progress); } } } else { method.Invoke(mb, parameters); } } } var labels = AssetDatabase.GetLabels(assembler.prefab); bool found = false; for (int i = 0; i < labels.Length; i++) { var label = labels[i]; if (label.StartsWith("Stage: ")) { labels[i] = GenerateLabel(); AssetDatabase.SetLabels(assembler.prefab, labels); found = true; break; } } if (!found) { var list = new List <string>(labels); list.Add(GenerateLabel()); AssetDatabase.SetLabels(assembler.prefab, list.ToArray()); } yield return(new PrefabAssembleProgress(0.95f, "Cleanup")); GameObject.DestroyImmediate(gameObject); PrefabAssembler.Current = null; yield break; }