popcorn/Editor/EditorUtils.cs

350 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEditorInternal;
using UnityEngine;
public static class EditorUtils {
public static void OpenAllScene(Func<float, EditorBuildSettingsScene, GameObject[], bool> editFunc){
var currentScenePath = EditorSceneManager.GetActiveScene().path;
var scenes = EditorBuildSettings.scenes.Where(s => s.enabled).ToArray();
for(int i = 0; i < scenes.Length; ++i){
var scene = scenes[i];
EditorSceneManager.OpenScene(scene.path);
var gameObjectArray = Resources.FindObjectsOfTypeAll<GameObject>().Where(go => string.IsNullOrEmpty(AssetDatabase.GetAssetPath(go)) && go.hideFlags == HideFlags.None).ToArray();
var isChanged = editFunc((float)i / scenes.Length, scene, gameObjectArray);
if(isChanged){
EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene());
}
}
EditorSceneManager.OpenScene(currentScenePath);
}
public static GameObject[] GetAllAssets(){
var pathArray = AssetDatabase.GetAllAssetPaths().Where(path => path.StartsWith("Assets/")).ToArray();
return pathArray.Select(a => AssetDatabase.LoadAssetAtPath(a, typeof(GameObject)) as GameObject).Where(a => a != null).ToArray();
}
public static void FindAllPrefabPath(string folderPath, Action<float, string> action){
var pathArray = Directory.GetFiles(folderPath, "*.prefab");
for(int i = 0; i < pathArray.Length; ++i){
action((float)i / pathArray.Length, pathArray[i].Replace(Application.dataPath, "Assets"));
}
}
public static void FindAllChildrensPrefabPath(string folderPath, Action<float, string> action){
FindAllPrefabPath(folderPath, action);
foreach(var childPath in Directory.GetDirectories(folderPath)){
FindAllChildrensPrefabPath(childPath, action);
}
}
public static void EditPrefab(string folderPath, Func<GameObject, bool> editFunc){
var prefab = PrefabUtility.LoadPrefabContents(folderPath);
var isChanged = editFunc(prefab);
if(isChanged){
PrefabUtility.SaveAsPrefabAsset(prefab, folderPath);
}
PrefabUtility.UnloadPrefabContents(prefab);
}
public static void FindAllChildrensPrefab(Transform transform, Action<Transform> action){
for(int i = transform.childCount - 1; i >= 0; --i){
Transform child = transform.GetChild(i);
if(PrefabUtility.IsAnyPrefabInstanceRoot(child.gameObject)){
action(child);
}else{
FindAllChildrensPrefab(child, action);
}
}
}
public static void SwapPrefab(Transform target, GameObject swapPrefab){
var to = (UnityEditor.PrefabUtility.InstantiatePrefab(swapPrefab) as GameObject).transform;
to.parent = target.parent;
to.localPosition = target.localPosition;
to.localRotation = target.localRotation;
to.localScale = target.localScale;
GameObject.DestroyImmediate(target.gameObject);
}
public static string MigratePrefabToPrefabVariant(GameObject basePrefab, GameObject migrationPrefab){
var basePath = AssetDatabase.GetAssetPath(basePrefab);
var path = AssetDatabase.GetAssetPath(migrationPrefab).Replace(".prefab", " Variant.prefab");
var instance = PrefabUtility.InstantiatePrefab(basePrefab) as GameObject;
PrefabUtility.SaveAsPrefabAsset(instance, path);
GameObject.DestroyImmediate(instance);
var prefab = PrefabUtility.LoadPrefabContents(path);
var result = Migrate(prefab.transform, migrationPrefab.transform);
if(!result){
FixField(prefab.transform, migrationPrefab.transform);
PrefabUtility.SaveAsPrefabAsset(prefab, path);
}
PrefabUtility.UnloadPrefabContents(prefab);
return path;
}
private static bool Migrate(Transform targetTransform, Transform migrationTransform){
var targetChildCount = targetTransform.childCount;
for(int i = 0; i < migrationTransform.childCount; ++i){
var migrationChild = migrationTransform.GetChild(i);
Transform targetChild;
if(i < targetChildCount){
targetChild = targetTransform.GetChild(i);
if(migrationChild.name != targetChild.name){
var containsNameCount = ChildContaintsNameCount(targetTransform, migrationChild.name);
if(containsNameCount == 0){
targetChild = GameObject.Instantiate(migrationChild.gameObject, targetTransform).transform;
targetChild.name = migrationChild.name;
targetChild.SetSiblingIndex(migrationChild.GetSiblingIndex());
++targetChildCount;
}else if(containsNameCount == 1){
for(int j = 0; j < targetChildCount; ++j){
if(targetTransform.GetChild(j).name == migrationChild.name){
targetChild = targetTransform.GetChild(j);
targetChild.SetSiblingIndex(migrationChild.GetSiblingIndex());
break;
}
}
}else{
Debug.LogError("同一階層に同じ名前のオブジェクトがある為中止します。");
return true;
}
}
}else{
targetChild = GameObject.Instantiate(migrationChild.gameObject, targetTransform).transform;
targetChild.name = migrationChild.name;
++targetChildCount;
}
CopyComponents(targetChild, migrationChild);
var result = Migrate(targetChild, migrationChild);
if(result){
return true;
}
targetChild.gameObject.SetActive(migrationChild.gameObject.activeSelf);
}
for(int i = 0; i < targetChildCount; ++i){
var toChild = targetTransform.GetChild(i);
var containsNameCount = ChildContaintsNameCount(migrationTransform, toChild.name);
if(containsNameCount == 0){
toChild.gameObject.SetActive(false);
Debug.LogWarning("Change disable " + toChild.GetPath());
}
}
return false;
}
private static void FixField(Transform targetTransform, Transform migrationTransform){
var targetChildCount = targetTransform.childCount;
for(int i = 0; i < migrationTransform.childCount; ++i){
var migrationChild = migrationTransform.GetChild(i);
var targetChild = targetTransform.GetChild(i);
if(migrationChild.name != targetChild.name){
for(int j = 0; j < targetChildCount; ++j){
if(targetTransform.GetChild(j).name == migrationChild.name){
targetChild = targetTransform.GetChild(j);
break;
}
}
}
FixField(targetChild, migrationChild);
ProcessReflection(migrationChild, targetChild, targetTransform);
migrationChild.FindComponent<ParticleSystem>(fromPS => {
if(fromPS.subEmitters.enabled){
var fromSub = fromPS.subEmitters;
FindChildrensByPath(targetTransform, migrationChild.GetPath(), t => {
t.FindComponent<ParticleSystem>(toPS => {
var toSub = toPS.subEmitters;
for(int index = 0; index < fromSub.subEmittersCount; ++index){
var fromTarget = fromSub.GetSubEmitterSystem(index);
FindChildrensByPath(targetTransform, fromTarget.transform.GetPath(), toTarget => {
toSub.SetSubEmitterSystem(index, toTarget.GetComponent<ParticleSystem>());
});
}
});
});
}
});
}
}
private static int ChildContaintsNameCount(Transform transform, string name){
var counter = 0;
foreach(Transform child in transform){
if(child.name == name){
++counter;
}
}
return counter;
}
private static void CopyComponents(Transform to, Transform from){
var components = from.gameObject.GetComponents<Component>();
var targetComponents = to.gameObject.GetComponents<Component>();
var currentComponentCount = new Dictionary<System.Type, int>();
foreach(var component in components){
var componentType = component.GetType();
var componentCount = targetComponents.Count(c => c.GetType() == componentType);
ComponentUtility.CopyComponent(component);
if(componentCount == 0){
ComponentUtility.PasteComponentAsNew(to.gameObject);
}else if(componentCount == 1){
var targetComponent = targetComponents.First(c => c.GetType() == componentType);
ComponentUtility.PasteComponentValues(targetComponent);
}else{
if(!currentComponentCount.ContainsKey(componentType)){
currentComponentCount.Add(componentType, 0);
}
var count = currentComponentCount[componentType];
var targetComponentsWithType = targetComponents.Where(c => c.GetType() == componentType);
if(count < targetComponentsWithType.Count()){
var targetComponent = targetComponents.Where(c => c.GetType() == componentType).ElementAt(count);
currentComponentCount[componentType] += 1;
ComponentUtility.PasteComponentValues(targetComponent);
}else{
ComponentUtility.PasteComponentAsNew(to.gameObject);
}
}
}
foreach(var component in targetComponents){
var componentType = component.GetType();
var componentCount = components.Count(c => c.GetType() == componentType);
if(componentCount == 0){
Debug.LogWarning("Deestroy component " + to.GetPath() + " " + componentType);
GameObject.DestroyImmediate(component);
}
}
}
private static void ProcessReflection(Transform from, Transform to, Transform parent){
var fromComponents = from.GetComponents<Component>();
for(int i = 0; i < fromComponents.Length; ++i){
var fromType = fromComponents[i].GetType();
var toComponent = to.GetComponent(fromType);
if(toComponent == null){
continue;
}
MemberInfo[] members = fromType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach(var m in members){
if(m.MemberType != MemberTypes.Field){
continue;
}
System.Type fieldType = ((FieldInfo)m).FieldType;
if(fieldType == typeof(Transform) || fieldType == typeof(GameObject)){
string targetPath;
FieldInfo field = fromType.GetField(m.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if(fieldType == typeof(Transform)){
Transform t = (Transform)field.GetValue(fromComponents[i]);
if(t == null){
continue;
}
targetPath = t.GetPath();
}else if(fieldType == typeof(GameObject)){
GameObject obj = (GameObject)field.GetValue(fromComponents[i]);
if(obj == null){
continue;
}
targetPath = obj.transform.GetPath();
}else{
continue;
}
FindChildrensByPath(parent, targetPath, targetTransform => {
FieldInfo setField = fromType.GetField(m.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if(setField == null){
Debug.Log("setField is null " + m.Name);
}else{
if(fieldType == typeof(Transform)){
setField.SetValue(toComponent, targetTransform);
}else if(fieldType == typeof(GameObject)){
setField.SetValue(toComponent, targetTransform.gameObject);
}
}
});
}else if(fieldType == typeof(Transform[])){
FieldInfo field = fromType.GetField(m.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
Transform[] t = (Transform[])field.GetValue(fromComponents[i]);
if(t == null){
continue;
}
var targetPathArray = new string[t.Length];
for(int j = 0; j < t.Length; ++j){
if(t[j] == null){
targetPathArray[j] = null;
}else{
targetPathArray[j] = t[j].GetPath();
}
}
var newTransformArray = new Transform[t.Length];
for(int j = 0; j < t.Length; ++j){
if(targetPathArray[j] == null){
continue;
}
FindChildrensByPath(parent, targetPathArray[j], targetTransform => {
newTransformArray[j] = targetTransform;
});
}
FieldInfo setField = fromType.GetField(m.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if(setField == null){
Debug.Log("setField is null " + m.Name);
}else{
setField.SetValue(toComponent, newTransformArray);
}
}else if(fieldType == typeof(GameObject[])){
FieldInfo field = fromType.GetField(m.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
GameObject[] t = (GameObject[])field.GetValue(fromComponents[i]);
if(t == null){
continue;
}
var targetPathArray = new string[t.Length];
for(int j = 0; j < t.Length; ++j){
if(t[j] == null){
targetPathArray[j] = null;
}else{
targetPathArray[j] = t[j].transform.GetPath();
}
}
var newArray = new GameObject[t.Length];
for(int j = 0; j < t.Length; ++j){
if(targetPathArray[j] == null){
continue;
}
FindChildrensByPath(parent, targetPathArray[j], targetTransform => {
newArray[j] = targetTransform.gameObject;
});
}
FieldInfo setField = fromType.GetField(m.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if(setField == null){
Debug.Log("setField is null " + m.Name);
}else{
setField.SetValue(toComponent, newArray);
}
}
}
}
}
private static bool FindChildrensByPath(Transform t, string path, Action<Transform> callback){
for(int i = 0; i < t.childCount; ++i){
Transform child = t.GetChild(i);
if(child.GetPath().Replace(" Variant", "") == path){
callback(child);
return true;
}
}
for(int i = 0; i < t.childCount; ++i){
Transform child = t.GetChild(i);
if(FindChildrensByPath(child, path, callback)){
return true;
}
}
return false;
}
}