Net.Like.Xue.Tokyo/Assets/Plugins/MonKey Commander/Editor/MonKey/Sample Commands/PrefabUtilities.cs

379 lines
14 KiB
C#

#if UNITY_EDITOR
using MonKey.Editor.Internal;
using MonKey.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
#if UNITY_2022_1_OR_NEWER
using UnityEditor.SceneManagement;
#else
#endif
using UnityEngine;
using Object = UnityEngine.Object;
namespace MonKey.Editor.Commands
{
public static class PrefabUtilities
{
[Command("Select Prefab Root", QuickName = "SPR",
Help = "Selects the prefab roots of the selected objects",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT,
Category = "Prefab")]
public static void SelectPrefabRoots()
{
List<Object> roots = new List<Object>();
foreach (GameObject o in Selection.gameObjects)
{
roots.Add(PrefabUtility.GetOutermostPrefabInstanceRoot(o));
}
Selection.objects = roots.ToArray();
}
[Command("New Prefab", QuickName = "NPR",
Help = "Create a prefab out of the selected objects in a folder you can specify",
ValidationMethodName = "ValidateCreatePrefabs",
Category = "Prefab")]
public static void CreatePrefabs()
{
CreatePrefabs(Selection.gameObjects);
}
[CommandValidation("You must select at least one GameObject")]
public static bool ValidateCreatePrefabs()
{
return (Selection.activeGameObject != null);
}
[Command("New Prefab Variants", "Creates prefab variants of the selected prefab", Category = "Assets",
QuickName = "PV")]
public static void CreatePrefabVariant(int duplicateAmount = 1)
{
foreach (var gameObject in Selection.gameObjects)
{
GameObject obj = (GameObject)PrefabUtility.InstantiatePrefab(gameObject);
string path = AssetDatabase.GetAssetPath(gameObject);
for (int i = 0; i < duplicateAmount; i++)
{
string newPath = AssetDatabase.GenerateUniqueAssetPath(path);
PrefabUtility.SaveAsPrefabAsset(obj, newPath);
}
Object.DestroyImmediate(obj);
}
}
private static void CreatePrefabs(GameObject[] gameObjects)
{
if (gameObjects == null || gameObjects.Length == 0)
return;
// Ask user for the asset path to store prefabs
string savePath = EditorUtility.SaveFilePanelInProject("Prefab Creation", "Prefab",
"prefab", "Select the prefab's folder", "Assets");
if (savePath.Length <= 0)
return;
if (!savePath.Contains(".prefab"))
savePath += ".prefab";
int i = 0;
foreach (GameObject go in gameObjects)
{
if (EditorUtility.DisplayCancelableProgressBar("Prefab creation", "In Progress!" +
go.name,
(i + .5f) / Selection.gameObjects.Length))
break;
i++;
string savePathArranged = savePath.Insert(savePath.IndexOf(".",
StringComparison.Ordinal), "-" + go.name);
PrefabUtility.SaveAsPrefabAssetAndConnect(go, savePathArranged, InteractionMode.AutomatedAction);
}
EditorUtility.ClearProgressBar();
}
public static GameObject[] CreatePrefabDefaultObjects()
{
return Selection.gameObjects;
}
[Command("Apply Prefab", Help = "Applies the changes on the selected prefabs",
QuickName = "AP",
Category = "Prefab")]
public static void ApplyPrefabs()
{
int i = 0;
List<GameObject> parentApplied = new List<GameObject>();
foreach (GameObject go in Selection.gameObjects.Where(_ => _.scene.IsValid()
&& _.hideFlags == HideFlags.None))
{
if (EditorUtility.DisplayCancelableProgressBar("Applying Prefabs", "Processing..." + go.name,
(i + .5f) / Selection.gameObjects.Length))
break;
i++;
GameObject applied = ApplySinglePrefab(parentApplied, go);
if (applied != null)
parentApplied.Add(applied);
}
EditorUtility.ClearProgressBar();
}
[CommandValidation("Select Some Prefabs First")]
private static bool ValidateApplyPrefabs()
{
return Selection.activeGameObject != null;
}
internal static Object GetPrefabParentPlatformIndependant(GameObject child)
{
#if UNITY_2018_3_OR_NEWER
GameObject prefabParent = PrefabUtility.GetCorrespondingObjectFromSource(child);
#elif UNITY_2018_2_OR_NEWER
Type pUtilityType = typeof(PrefabUtility);
MethodInfo info = pUtilityType.GetMethod("GetCorrespondingObjectFromSource");
//stupid ass reflection because of some method signature change in the last Unity
GameObject prefabParent = info.Invoke(null,new object[]{child}) as GameObject;
#else
GameObject prefabParent = PrefabUtility.GetPrefabParent(child) as GameObject;
#endif
return prefabParent;
}
[Command("Select Instances", "Selects all instances of the specified prefab", QuickName = "SI",
AlwaysShow = true, Order = -5, MenuItemLink = "SelectPrefabInstances",
MenuItemLinkTypeOwner = "MonkeyMenuItems",
Category = "Prefab")]
public static void SelectInstancesOfPrefab(
[CommandParameter(Help = "The prefab to find the instances of on the opened scenes",
ForceAutoCompleteUsage = true,
DefaultValueNameOverride = "Prefab Selected",
AutoCompleteMethodName = "PrefabAutoComplete")]
string prefabName = "")
{
GameObject prefab;
if (prefabName == "")
prefab = Selection.objects.First(_ => _ is GameObject) as GameObject;
else
prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabName);
if (prefab == null)
{
Debug.LogWarning("Monkey Error: the prefab was not recognized");
return;
}
EditorUtility.DisplayProgressBar("Finding Prefab Instances...",
"Please Wait while Monkey is looking for the prefab instances!", 0);
List<Transform> transs = TransformExt.GetAllTransformedOrderUpToDown();
int i = 0;
List<GameObject> selectedInstances = new List<GameObject>();
foreach (Transform transform in transs)
{
if (EditorUtility.DisplayCancelableProgressBar("Finding Prefab Instances...",
"Please Wait while Monkey is looking for the prefab instances!",
((float)i / transs.Count) * .5f + .5f))
break;
if (prefab.IsPrefab())
{
#if UNITY_2018_3_OR_NEWER
GameObject prefabRoot = PrefabUtility.GetNearestPrefabInstanceRoot(transform.gameObject);
#else
GameObject prefabRoot = PrefabUtility.FindPrefabRoot(transform.gameObject);
#endif
// Update the prefab of the game object
Object prefabParent = prefabRoot ? GetPrefabParentPlatformIndependant(prefabRoot) : null;
if (prefabParent == prefab && !selectedInstances.Contains(prefabRoot))
selectedInstances.Add(prefabRoot);
}
else
{
//then it's a model
MeshFilter filter = transform.gameObject.GetComponent<MeshFilter>();
if (filter)
{
MeshFilter[] filters = prefab.GetComponentsInChildren<MeshFilter>();
if (filters.Any(_ => _.sharedMesh == filter.sharedMesh))
selectedInstances.Add(filter.gameObject);
}
}
i++;
}
Selection.objects = selectedInstances.Convert(_ => _ as Object).ToArray();
EditorUtility.ClearProgressBar();
}
public static AssetNameAutoComplete PrefabAutoComplete()
{
return new AssetNameAutoComplete() { CustomType = "GameObject" };
}
public static GameObject ApplySinglePrefab(List<GameObject> excludedPrefabs, GameObject gameObject,
bool forceActive = false, bool resetTransform = false)
{
GameObject prefabRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(gameObject);
// Update the prefab of the game object
GameObject prefabParent = (GameObject)GetPrefabParentPlatformIndependant(prefabRoot);
if (excludedPrefabs.Contains(prefabParent))
return null;
if (prefabParent != null)
{
bool isActive = gameObject.activeSelf;
Transform t = prefabRoot.transform;
Vector3 localPosition = t.localPosition;
Quaternion localRotation = t.localRotation;
if (forceActive)
gameObject.SetActive(true);
if (resetTransform)
{
t.localPosition = Vector3.zero;
t.localRotation = Quaternion.identity;
}
PrefabUtility.SavePrefabAsset(prefabRoot);
if (forceActive)
gameObject.SetActive(isActive);
if (resetTransform)
{
t.localPosition = localPosition;
t.localRotation = localRotation;
}
return prefabParent;
}
return null;
}
[Command("Revert Prefab Instances",
"Reverts all the changes made to the prefab instance to the base prefab",
QuickName = "RPI",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT,
Category = "Prefab")]
public static void RevertToPrefabInstances()
{
foreach (GameObject o in Selection.gameObjects)
{
if (PrefabUtility.GetPrefabInstanceStatus(o) != PrefabInstanceStatus.NotAPrefab)
PrefabUtility.RevertObjectOverride(o, InteractionMode.AutomatedAction);
}
}
#if UNITY_2018_3_OR_NEWER || UNITY_2019
[Command("Open Prefab", "Opens a prefab", AlwaysShow = true, Category = "Prefab", QuickName = "OP",
DefaultValidation = DefaultValidation.IN_EDIT_MODE)]
private static void OpenPrefab([CommandParameter(AutoCompleteMethodName = "PrefabNameAutoComplete",
ForceAutoCompleteUsage = true, Help = "The name of the prefab to open",
OverrideName = "Prefab Name", PreventDefaultValueUsage = true)]
string prefabPath)
{
Type utils = typeof(UnityEditor.SceneManagement.PrefabStageUtility);
var methods = utils.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
if (methods.Length == 0)
Debug.Log("The Open Prefab Method wasn't found: Unity may have changed its API, " +
"please contact the MonKey support");
foreach (var info in methods)
{
if (info.Name == "OpenPrefab" && info.GetParameters().Length == 1)
{
info.Invoke(null, new object[] { prefabPath });
return;
}
}
}
private static AssetNameAutoComplete PrefabNameAutoComplete()
{
return new AssetNameAutoComplete()
{
CustomType = "GameObject",
IncludeDirectories = false,
PopulateOnInit = true,
};
}
[Command("Remove Component Of Type From Prefabs In Folder",
Help =
"Removes all the component of the given type on all the prefabs present in a given folder, recursively")]
public static void RemoveComponentsFromPrefabsInFolder(
[CommandParameter(AutoCompleteMethodName = "FolderAutoComplete", Help = "The name of the folder",
ForceAutoCompleteUsage = true)]
string folderName,
[CommandParameter(Help = "The Type of Component", AutoCompleteMethodName = "ComponentTypeAuto")]
Type componentType)
{
SelectionUtilities.FindFolder(folderName);
string folderPath = AssetDatabase.GetAssetPath(Selection.activeObject);
string[] folders = AssetDatabase.GetSubFolders(folderName);
List<string> foldersList = new List<string>(folders);
foldersList.Add(folderName);
string[] prefabPaths = AssetDatabase.FindAssets("t:GameObject", foldersList.ToArray());
foreach (var path in prefabPaths)
{
var go = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GUIDToAssetPath(path));
if (!go)
return;
var instance = (GameObject)PrefabUtility.InstantiatePrefab(go);
var comps = instance.GetComponentsInChildren(componentType);
var originalComps = go.GetComponentsInChildren(componentType);
for (var index = 0; index < comps.Length; index++)
{
var component = comps[index];
var prefabComponent = originalComps[index];
Object.DestroyImmediate(component);
PrefabUtility.ApplyRemovedComponent(instance, prefabComponent, InteractionMode.AutomatedAction);
}
Object.DestroyImmediate(instance);
}
AssetDatabase.SaveAssets();
}
public static AssetNameAutoComplete FolderAutoComplete()
{
return new AssetNameAutoComplete() { DirectoryMode = true };
}
public static TypeAutoComplete ComponentTypeAuto()
{
return new TypeAutoComplete(false, true, true, false, false);
}
#endif
}
}
#endif