forked from Shardstone/trail-into-darkness
Added a bunch of utilities and modfief the character data structue
This commit is contained in:
144
Packages/com.jovian.utilties/Editor/CollisionExtractorEditor.cs
Normal file
144
Packages/com.jovian.utilties/Editor/CollisionExtractorEditor.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Jovian.Utilities.Editor {
|
||||
[CustomEditor(typeof(CollisionExtractor))]
|
||||
public class CollisionExtractorEditor : UnityEditor.Editor {
|
||||
|
||||
public override void OnInspectorGUI() {
|
||||
base.OnInspectorGUI();
|
||||
|
||||
serializedObject.Update();
|
||||
if(GUILayout.Button("Extract Colliders")) {
|
||||
ExtractCollidersIntoPrefab();
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractCollidersIntoPrefab() {
|
||||
CollisionExtractor collisionExtractor = (CollisionExtractor)target;
|
||||
GameObject source = collisionExtractor.root;
|
||||
GameObject targetPrefab = collisionExtractor.targetPrefab;
|
||||
|
||||
// Verify input
|
||||
if(source == null) {
|
||||
Debug.LogWarning("[CollisionExtractor] no Root assigned, please assign a root then try again!");
|
||||
return;
|
||||
}
|
||||
if(targetPrefab == null) {
|
||||
Debug.LogWarning("[CollisionExtractor] no TargetPrefab assigned, please assign a root then try again!");
|
||||
return;
|
||||
}
|
||||
else if(targetPrefab.scene.name != null) {
|
||||
Debug.LogWarning("[CollisionExtractor] TargetPrefab is not a prefab instance, please assign a proper prefab instance!");
|
||||
return;
|
||||
}
|
||||
|
||||
string prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(targetPrefab);
|
||||
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
|
||||
|
||||
// Remove any old children and build up a new hierarchy matching the source
|
||||
prefabRoot.transform.DetachChildren();
|
||||
|
||||
// Try to remove all components on the prefab root (several iterations due to components sometime requiring each other, thus requiring a certain order of deletion)
|
||||
for(int i = 0; i < 6; i++) {
|
||||
foreach(var comp in prefabRoot.GetComponents<Component>()) {
|
||||
//Don't remove the Transform component
|
||||
if(!(comp is Transform)) {
|
||||
DestroyImmediate(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We make a copy of the source object to ensure it does not get changed, when accessing the properties using reflection Unity notes some of them as changed even though we only read from it.
|
||||
// For colliders this happens to the physics material, if it is set to "None" it will become an empty value on the source in the editor but properly pick the None value for the target.
|
||||
GameObject sourceCopy = Instantiate(source);
|
||||
try {
|
||||
CopyCollidersRecursive(sourceCopy, prefabRoot);
|
||||
}
|
||||
finally {
|
||||
DestroyImmediate(sourceCopy);
|
||||
}
|
||||
Debug.Log("[CollisionExtractor] extraction into target prefab complete.");
|
||||
|
||||
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
|
||||
PrefabUtility.UnloadPrefabContents(prefabRoot);
|
||||
}
|
||||
|
||||
private void CopyCollidersRecursive(GameObject sourceNode, GameObject targetNode) {
|
||||
// Copy all transform settings
|
||||
targetNode.transform.SetPositionAndRotation(sourceNode.transform.position, sourceNode.transform.rotation);
|
||||
targetNode.transform.localScale = sourceNode.transform.localScale;
|
||||
GameObjectUtility.SetStaticEditorFlags(targetNode, GameObjectUtility.GetStaticEditorFlags(sourceNode));
|
||||
targetNode.tag = sourceNode.tag;
|
||||
targetNode.layer = sourceNode.layer;
|
||||
|
||||
// Copy all collider components
|
||||
Collider[] colliders = sourceNode.GetComponents<Collider>();
|
||||
for(int i = 0; i < colliders.Length; ++i) {
|
||||
switch(colliders[i]) {
|
||||
case BoxCollider sourceBoxCollider:
|
||||
BoxCollider targetBoxCollider = targetNode.AddComponent<BoxCollider>();
|
||||
GetCopyOf(targetBoxCollider, sourceBoxCollider);
|
||||
break;
|
||||
case SphereCollider sourceSphereCollider:
|
||||
SphereCollider targetSphereCollider = targetNode.AddComponent<SphereCollider>();
|
||||
GetCopyOf(targetSphereCollider, sourceSphereCollider);
|
||||
break;
|
||||
case CapsuleCollider sourceCapsuleCollider:
|
||||
CapsuleCollider targetCapsuleCollider = targetNode.AddComponent<CapsuleCollider>();
|
||||
GetCopyOf(targetCapsuleCollider, sourceCapsuleCollider);
|
||||
break;
|
||||
case MeshCollider sourceMeshCollider:
|
||||
MeshCollider targetMeshCollider = targetNode.AddComponent<MeshCollider>();
|
||||
GetCopyOf(targetMeshCollider, sourceMeshCollider);
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"[CollisionExtractor] found unsupported collider type on game object {sourceNode.name}!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Continue with all the child nodes
|
||||
for(int i = 0; i < sourceNode.transform.childCount; ++i) {
|
||||
Transform sourceChildNode = sourceNode.transform.GetChild(i);
|
||||
GameObject targetChildNode = new GameObject();
|
||||
targetChildNode.name = sourceChildNode.name;
|
||||
targetChildNode.transform.parent = targetNode.transform;
|
||||
CopyCollidersRecursive(sourceChildNode.gameObject, targetChildNode);
|
||||
}
|
||||
|
||||
// If we are a leaf node without colliders we are not useful, delete this node
|
||||
if(colliders.Length == 0 && targetNode.transform.childCount == 0) {
|
||||
DestroyImmediate(targetNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from https://answers.unity.com/questions/530178/how-to-get-a-component-from-an-object-and-add-it-t.html?_ga=2.50760041.112217741.1608192858-1956498980.1555671355
|
||||
private T GetCopyOf<T>(Component target, T source) where T : Component {
|
||||
Type type = target.GetType();
|
||||
if(type != source.GetType()) return null; // type mis-match
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default;
|
||||
var pinfos = from property in type.GetProperties(flags)
|
||||
where !property.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(ObsoleteAttribute))
|
||||
select property;
|
||||
foreach(var pinfo in pinfos) {
|
||||
if(pinfo.CanWrite) {
|
||||
try {
|
||||
pinfo.SetValue(target, pinfo.GetValue(source, null), null);
|
||||
}
|
||||
catch { } // In case of NotImplementedException being thrown. For some reason specifying that exception didn't seem to catch it, so I didn't catch anything specific.
|
||||
}
|
||||
}
|
||||
FieldInfo[] finfos = type.GetFields(flags);
|
||||
foreach(var finfo in finfos) {
|
||||
finfo.SetValue(target, finfo.GetValue(source));
|
||||
}
|
||||
return target as T;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user