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);
            }
        }
    }
Esempio n. 4
0
    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);
     }
 }
Esempio n. 6
0
 void DrawPrefabField(PrefabAssembler assembler)
 {
     if (!assembler.prefab)
     {
         EditorGUILayout.LabelField("Target:", "None Assigned");
     }
     else
     {
         EditorGUILayout.ObjectField("Target:", assembler.prefab, typeof(GameObject), false);
     }
 }
Esempio n. 7
0
    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;
    }