forked from Shardstone/trail-into-darkness
changed directory structure
This commit is contained in:
144
Packages/com.jovian.utilities/Editor/CollisionExtractorEditor.cs
Normal file
144
Packages/com.jovian.utilities/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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 945d56cc182956e4a9dfb70d9051c0d1
|
||||
@@ -0,0 +1,84 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Jovian.Utilities.Editor {
|
||||
[CustomEditor(typeof(CustomRenderQueueMaterialList))]
|
||||
public class CustomRenderQueueMaterialListEditor : UnityEditor.Editor {
|
||||
|
||||
private const int MAXNAMELENGTH = 30;
|
||||
|
||||
public override void OnInspectorGUI() {
|
||||
if(((CustomRenderQueueMaterialList)target).updateMaterialsInEditor) {
|
||||
EditorGUILayout.HelpBox("Enabling 'updateMaterialsInEditor' will cause the Editor to change material files. Only use in debug and please review your changelist before submitting.", MessageType.Warning);
|
||||
}
|
||||
DrawAddGUI();
|
||||
base.OnInspectorGUI();
|
||||
DrawAddGUI();
|
||||
}
|
||||
|
||||
private void DrawAddGUI() {
|
||||
GUILayout.BeginHorizontal();
|
||||
if(GUILayout.Button("Add Empty")) {
|
||||
AddEmpty();
|
||||
}
|
||||
if(Selection.activeGameObject) {
|
||||
if(GUILayout.Button($"Add from '{GetNiceName(Selection.activeGameObject)}'")) {
|
||||
AddGameObject(Selection.activeGameObject);
|
||||
}
|
||||
}
|
||||
if(Selection.activeObject is Material) {
|
||||
if(GUILayout.Button($"Add '{GetNiceName(Selection.activeObject)}'")) {
|
||||
AddMaterial(Selection.activeObject as Material);
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private string GetNiceName(Object obj) {
|
||||
var objectName = obj.name;
|
||||
if(objectName.Length > MAXNAMELENGTH) {
|
||||
objectName = objectName.Substring(0, MAXNAMELENGTH - 2) + "..";
|
||||
}
|
||||
return objectName;
|
||||
}
|
||||
|
||||
private void AddEmpty() {
|
||||
AddElement(null, -1);
|
||||
}
|
||||
|
||||
private void AddGameObject(GameObject gameObject) {
|
||||
var renderer = gameObject.GetComponentInChildren<Renderer>();
|
||||
if(renderer == null) {
|
||||
Debug.LogError($"Cannot add Renderer from {gameObject}, there is none.");
|
||||
return;
|
||||
}
|
||||
AddMaterial(renderer.sharedMaterial);
|
||||
}
|
||||
|
||||
private void AddMaterial(Material material) {
|
||||
if(material == null) {
|
||||
Debug.LogError($"Cannot add Material, it is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(((CustomRenderQueueMaterialList)target).DoesMaterialExistInList(material)) {
|
||||
Debug.LogError($"Cannot add Material, it is already listed.");
|
||||
return;
|
||||
}
|
||||
|
||||
AddElement(material, material.renderQueue);
|
||||
}
|
||||
|
||||
private void AddElement(Material material, int renderQueue) {
|
||||
var listProperty = serializedObject.FindProperty("materialRenderQueueList");
|
||||
var count = listProperty.arraySize;
|
||||
listProperty.arraySize = count + 1;
|
||||
var elementProperty = listProperty.GetArrayElementAtIndex(count);
|
||||
var materialProperty = elementProperty.FindPropertyRelative("material");
|
||||
var renderQueueProperty = elementProperty.FindPropertyRelative("renderQueue");
|
||||
materialProperty.objectReferenceValue = material;
|
||||
renderQueueProperty.intValue = renderQueue;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe8e754720c57ef4089250664029c499
|
||||
@@ -0,0 +1,26 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Jovian.Utilities.Editor {
|
||||
[CustomPropertyDrawer(typeof(CustomRenderQueueMaterialList.MaterialRenderQueue))]
|
||||
public class CustomRenderQueueMaterialListPropertyDrawer : PropertyDrawer {
|
||||
private const float RENDERQUEUE_PROPERTY_WIDTH = 45f;
|
||||
private const float GUI_PADDING = 2f;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
|
||||
var indent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
var materialRect = new Rect(position.x, position.y, position.width - RENDERQUEUE_PROPERTY_WIDTH, position.height);
|
||||
var renderQueueRect = new Rect(materialRect.xMax + GUI_PADDING, position.y, RENDERQUEUE_PROPERTY_WIDTH - GUI_PADDING, position.height);
|
||||
|
||||
EditorGUI.PropertyField(materialRect, property.FindPropertyRelative("material"), new GUIContent(string.Empty, "Material"));
|
||||
EditorGUI.PropertyField(renderQueueRect, property.FindPropertyRelative("renderQueue"), new GUIContent(string.Empty, "RenderQueue"));
|
||||
|
||||
EditorGUI.indentLevel = indent;
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5ca9116ca3b8724dad5a329325ff93d
|
||||
@@ -0,0 +1,428 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using Debug = UnityEngine.Debug;
|
||||
using Object = UnityEngine.Object;
|
||||
using Type = System.Type;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Jovian.Utilities.Editor {
|
||||
/// <summary>
|
||||
/// Helper class for serializing objects in the editor
|
||||
/// </summary>
|
||||
public static class EditorSerializationUtility {
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property type for cases when it is needed, like in the case of trying to get the type of SerializedPropertyType.ObjectReference
|
||||
/// Solution found on https://answers.unity.com/questions/929293/get-field-type-of-serializedproperty.html
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
public static Type GetTypeFromProperty(SerializedProperty property) {
|
||||
//gets parent type info
|
||||
string[] slices = property.propertyPath.Split('.');
|
||||
Type type = property.serializedObject.targetObject.GetType();
|
||||
|
||||
for (int i = 0; i < slices.Length; i++) {
|
||||
string slice = slices[i];
|
||||
if (slice == "Array") {
|
||||
i++; //skips "data[x]"
|
||||
if (type.IsArray) // e.g Type[]
|
||||
{
|
||||
type = type.GetElementType();
|
||||
}
|
||||
else if (type.IsGenericType) // e.g. List<Type>
|
||||
{
|
||||
type = type.GetGenericArguments()[0];
|
||||
}
|
||||
else {
|
||||
throw new NotSupportedException("Unsupported array type. Type[] or List<Type> are only supported array types");
|
||||
}
|
||||
}
|
||||
else {
|
||||
//gets info on field and its type
|
||||
type = type.GetField(slice, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance)?.FieldType;
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
throw new NullReferenceException(
|
||||
$"Type is null, something is not working correctly. Path={property.propertyPath}, Slice={slice}");
|
||||
}
|
||||
}
|
||||
|
||||
//type is now the type of the property
|
||||
return type;
|
||||
}
|
||||
|
||||
private static readonly Regex isArrayElementRegex = new Regex("Array.data\\[\\d+\\]$");
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the property is an array element
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsPropertyAnArrayElement(SerializedProperty property) {
|
||||
return isArrayElementRegex.IsMatch(property.propertyPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the parent array property for the given property, which is a member of the array
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
public static SerializedProperty GetArrayPropertyWithElementProperty(SerializedProperty property) {
|
||||
SerializedObject serializedObject = property.serializedObject;
|
||||
string propertyPath = property.propertyPath;
|
||||
int arrayDataIndex = propertyPath.LastIndexOf(".Array.data", StringComparison.Ordinal);
|
||||
string propertyPathWithoutArray = propertyPath.Substring(0, arrayDataIndex);
|
||||
int pathDividerIndex = propertyPathWithoutArray.LastIndexOf(".", StringComparison.Ordinal);
|
||||
|
||||
string parentPropertyName = propertyPathWithoutArray;
|
||||
if (pathDividerIndex != -1) {
|
||||
parentPropertyName = propertyPathWithoutArray.Substring(pathDividerIndex);
|
||||
}
|
||||
|
||||
return serializedObject.FindProperty(parentPropertyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will save specifically the property args passed in the form new object[] { "propertyName1", "propertyName2", etc. } to the specified targetObject
|
||||
/// </summary>
|
||||
/// <param name="targetObject"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static void SaveObjectProperties(Object targetObject, params object[] args) {
|
||||
SerializedObject serializedObject = new SerializedObject(targetObject);
|
||||
|
||||
for (int i = 0; i < args.Length; i += 2) {
|
||||
object keyArg = args[i];
|
||||
Type keyType = keyArg.GetType();
|
||||
|
||||
if ((keyType == typeof(string)) == false) {
|
||||
throw new NotSupportedException($"Key must be string. {args[i]} is {keyType}");
|
||||
}
|
||||
|
||||
SerializedProperty property = serializedObject.FindProperty((string)keyArg);
|
||||
object argValue = args[i + 1];
|
||||
|
||||
if (property == null) {
|
||||
throw new Exception($"No property found for key {keyArg}");
|
||||
}
|
||||
|
||||
if (argValue == null) {
|
||||
property.objectReferenceValue = null;
|
||||
}
|
||||
else {
|
||||
SetSerializedPropertyValue(property, argValue);
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will auto-detect the value type and save it to the property. It only supports the serializable types that the Unity serialization system supports
|
||||
/// </summary>
|
||||
/// <param name="fromProperty"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public static void SetSerializedPropertyValue(SerializedProperty fromProperty, object value) {
|
||||
if (fromProperty == null) {
|
||||
Debug.LogError("SetSerializedPropertyValue failed, property is null");
|
||||
return;
|
||||
}
|
||||
|
||||
// Strings are counted as arrays but should be handled separately
|
||||
if (fromProperty.isArray && fromProperty.propertyType != SerializedPropertyType.String) {
|
||||
fromProperty.arraySize = 0;
|
||||
var argArray = (IEnumerable)value;
|
||||
var enumerator = argArray.GetEnumerator();
|
||||
|
||||
int index = 0;
|
||||
while (enumerator.MoveNext()) {
|
||||
fromProperty.InsertArrayElementAtIndex(index);
|
||||
var arrayProperty = fromProperty.GetArrayElementAtIndex(index);
|
||||
SetSerializedPropertyValue(arrayProperty, enumerator.Current);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (fromProperty.propertyType) {
|
||||
case SerializedPropertyType.AnimationCurve:
|
||||
fromProperty.animationCurveValue = (AnimationCurve)value;
|
||||
break;
|
||||
case SerializedPropertyType.Boolean:
|
||||
fromProperty.boolValue = (bool)value;
|
||||
break;
|
||||
case SerializedPropertyType.BoundsInt:
|
||||
fromProperty.boundsIntValue = (BoundsInt)value;
|
||||
break;
|
||||
case SerializedPropertyType.Character:
|
||||
fromProperty.intValue = (int)(char)value;
|
||||
break;
|
||||
case SerializedPropertyType.Color: {
|
||||
if (value is Color32 color32) {
|
||||
fromProperty.colorValue = (Color)color32;
|
||||
}
|
||||
else {
|
||||
fromProperty.colorValue = (Color)value;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SerializedPropertyType.ExposedReference:
|
||||
case SerializedPropertyType.ObjectReference:
|
||||
fromProperty.objectReferenceValue = (Object)value;
|
||||
break;
|
||||
case SerializedPropertyType.Float:
|
||||
fromProperty.floatValue = (float)value;
|
||||
break;
|
||||
case SerializedPropertyType.Integer:
|
||||
fromProperty.intValue = (int)value;
|
||||
break;
|
||||
case SerializedPropertyType.LayerMask:
|
||||
fromProperty.intValue = ((LayerMask)value).value;
|
||||
break;
|
||||
case SerializedPropertyType.Quaternion:
|
||||
fromProperty.quaternionValue = (Quaternion)value;
|
||||
break;
|
||||
case SerializedPropertyType.Rect:
|
||||
fromProperty.rectValue = (Rect)value;
|
||||
break;
|
||||
case SerializedPropertyType.RectInt:
|
||||
fromProperty.rectIntValue = (RectInt)value;
|
||||
break;
|
||||
case SerializedPropertyType.String:
|
||||
fromProperty.stringValue = (string)value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector2:
|
||||
fromProperty.vector2Value = (Vector2)value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector2Int:
|
||||
fromProperty.vector2IntValue = (Vector2Int)value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector3:
|
||||
fromProperty.vector3Value = (Vector3)value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector3Int:
|
||||
fromProperty.vector3IntValue = (Vector3Int)value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector4:
|
||||
fromProperty.vector4Value = (Vector4)value;
|
||||
break;
|
||||
case SerializedPropertyType.Enum:
|
||||
fromProperty.enumValueIndex = Array.IndexOf(Enum.GetValues(value.GetType()), value);
|
||||
// flags???
|
||||
break;
|
||||
case SerializedPropertyType.Generic:
|
||||
SaveGenericProperty(fromProperty, value);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"PropertyType {fromProperty.propertyType} is not supported - yet. Array? {fromProperty.isArray} Path: {fromProperty.propertyPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will copy fromProperty to toProperty. It only supports the serializable types that the Unity serialization system supports
|
||||
/// </summary>
|
||||
/// <param name="fromProperty"></param>
|
||||
/// <param name="toProperty"></param>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public static void CopyPropertyValue(SerializedProperty fromProperty, SerializedProperty toProperty) {
|
||||
Assert.IsNotNull(fromProperty, "fromProperty == null");
|
||||
Assert.IsNotNull(toProperty, "toProperty == null");
|
||||
Assert.AreEqual(fromProperty.propertyType, toProperty.propertyType, $"Properties do not match types. fromType={fromProperty.propertyType}, toType={toProperty.propertyType}");
|
||||
switch (fromProperty.propertyType) {
|
||||
case SerializedPropertyType.AnimationCurve:
|
||||
toProperty.animationCurveValue = fromProperty.animationCurveValue;
|
||||
break;
|
||||
case SerializedPropertyType.Boolean:
|
||||
toProperty.boolValue = fromProperty.boolValue;
|
||||
break;
|
||||
case SerializedPropertyType.BoundsInt:
|
||||
toProperty.boundsIntValue = fromProperty.boundsIntValue;
|
||||
break;
|
||||
case SerializedPropertyType.Character:
|
||||
toProperty.intValue = fromProperty.intValue;
|
||||
break;
|
||||
case SerializedPropertyType.Color: {
|
||||
toProperty.colorValue = fromProperty.colorValue;
|
||||
break;
|
||||
}
|
||||
case SerializedPropertyType.ExposedReference:
|
||||
case SerializedPropertyType.ObjectReference:
|
||||
toProperty.objectReferenceValue = fromProperty.objectReferenceValue;
|
||||
break;
|
||||
case SerializedPropertyType.Float:
|
||||
toProperty.floatValue = fromProperty.floatValue;
|
||||
break;
|
||||
case SerializedPropertyType.Integer:
|
||||
toProperty.intValue = fromProperty.intValue;
|
||||
break;
|
||||
case SerializedPropertyType.LayerMask:
|
||||
toProperty.intValue = fromProperty.intValue;
|
||||
break;
|
||||
case SerializedPropertyType.Quaternion:
|
||||
toProperty.quaternionValue = fromProperty.quaternionValue;
|
||||
break;
|
||||
case SerializedPropertyType.Rect:
|
||||
toProperty.rectValue = fromProperty.rectValue;
|
||||
break;
|
||||
case SerializedPropertyType.RectInt:
|
||||
toProperty.rectIntValue = fromProperty.rectIntValue;
|
||||
break;
|
||||
case SerializedPropertyType.String:
|
||||
toProperty.stringValue = fromProperty.stringValue;
|
||||
break;
|
||||
case SerializedPropertyType.Vector2:
|
||||
toProperty.vector2Value = fromProperty.vector2Value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector2Int:
|
||||
toProperty.vector2IntValue = fromProperty.vector2IntValue;
|
||||
break;
|
||||
case SerializedPropertyType.Vector3:
|
||||
toProperty.vector3Value = fromProperty.vector3Value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector3Int:
|
||||
toProperty.vector3IntValue = fromProperty.vector3IntValue;
|
||||
break;
|
||||
case SerializedPropertyType.Vector4:
|
||||
toProperty.vector4Value = fromProperty.vector4Value;
|
||||
break;
|
||||
case SerializedPropertyType.Enum:
|
||||
toProperty.intValue = fromProperty.intValue;
|
||||
break;
|
||||
case SerializedPropertyType.Generic:
|
||||
CopyGenericProperty(fromProperty, toProperty);
|
||||
break;
|
||||
case SerializedPropertyType.ManagedReference:
|
||||
toProperty.managedReferenceValue = Activator.CreateInstance(fromProperty.managedReferenceValue.GetType());
|
||||
CopyGenericProperty(fromProperty, toProperty);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"PropertyType {fromProperty.propertyType} is not supported - yet. Array? {fromProperty.isArray} Path: {fromProperty.propertyPath}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyGenericProperty(SerializedProperty fromProperty, SerializedProperty toProperty) {
|
||||
IEnumerator fromPropertyEnumerator = fromProperty.GetEnumerator();
|
||||
IEnumerator toPropertyEnumerator = toProperty.GetEnumerator();
|
||||
while (toPropertyEnumerator.MoveNext() && fromPropertyEnumerator.MoveNext()) {
|
||||
if (toPropertyEnumerator.Current is SerializedProperty toChildProperty &&
|
||||
fromPropertyEnumerator.Current is SerializedProperty fromChildProperty) {
|
||||
CopyPropertyValue(fromChildProperty, toChildProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveGenericProperty(SerializedProperty property, object instance) {
|
||||
Type type = instance.GetType();
|
||||
IEnumerable<FieldInfo> fields = type.GetRuntimeFields();
|
||||
foreach (FieldInfo field in fields) {
|
||||
if (field.IsNotSerialized || field.IsStatic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SerializedProperty fieldProperty = property.FindPropertyRelative(field.Name);
|
||||
if (fieldProperty != null) {
|
||||
SetSerializedPropertyValue(fieldProperty, field.GetValue(instance));
|
||||
}
|
||||
else {
|
||||
Debug.Log($"SaveGenericProperty cannot field serializedProperty named '{field.Name}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetAttribute<TAttribute>(
|
||||
this SerializedProperty serializedProperty,
|
||||
out TAttribute attribute,
|
||||
bool includeAttributesFromParentProperties = false,
|
||||
bool includeInheritedAttributes = true
|
||||
)
|
||||
where TAttribute : Attribute {
|
||||
if (serializedProperty == null) {
|
||||
throw new ArgumentNullException(nameof(serializedProperty));
|
||||
}
|
||||
|
||||
Type targetObjectType = serializedProperty.serializedObject.targetObject.GetType();
|
||||
|
||||
if (targetObjectType == null) {
|
||||
throw new ArgumentException($"Could not find the {nameof(targetObjectType)} of {nameof(serializedProperty)}");
|
||||
}
|
||||
|
||||
string[] pathSegments = includeAttributesFromParentProperties ? serializedProperty.propertyPath.Split('.') : new[] { serializedProperty.propertyPath };
|
||||
|
||||
foreach (string pathSegment in pathSegments) {
|
||||
FieldInfo fieldInfo = targetObjectType.GetField(pathSegment, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
|
||||
if (fieldInfo != null) {
|
||||
attribute = fieldInfo.GetCustomAttribute<TAttribute>(includeInheritedAttributes);
|
||||
if (attribute != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo = targetObjectType.GetProperty(pathSegment, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
|
||||
if (propertyInfo != null) {
|
||||
attribute = propertyInfo.GetCustomAttribute<TAttribute>(includeInheritedAttributes);
|
||||
if (attribute != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attribute = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetAttributes<TAttribute>(
|
||||
SerializedProperty serializedProperty,
|
||||
out List<TAttribute> attributes,
|
||||
bool includeAttributesFromParentProperties = false,
|
||||
bool includeInheritedAttributes = true
|
||||
)
|
||||
where TAttribute : Attribute {
|
||||
if (serializedProperty == null) {
|
||||
throw new ArgumentNullException(nameof(serializedProperty));
|
||||
}
|
||||
|
||||
Type targetObjectType = serializedProperty.serializedObject.targetObject.GetType();
|
||||
|
||||
if (targetObjectType == null) {
|
||||
throw new ArgumentException($"Could not find the {nameof(targetObjectType)} of {nameof(serializedProperty)}");
|
||||
}
|
||||
|
||||
attributes = new List<TAttribute>();
|
||||
|
||||
string[] pathSegments = includeAttributesFromParentProperties ? serializedProperty.propertyPath.Split('.') : new[] { serializedProperty.propertyPath };
|
||||
|
||||
foreach (string pathSegment in pathSegments) {
|
||||
FieldInfo fieldInfo = targetObjectType.GetField(pathSegment, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
|
||||
if (fieldInfo != null) {
|
||||
IEnumerable<TAttribute> foundAttributes = fieldInfo.GetCustomAttributes<TAttribute>(includeInheritedAttributes);
|
||||
foreach (TAttribute foundAttribute in foundAttributes) {
|
||||
attributes.Add(foundAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo = targetObjectType.GetProperty(pathSegment, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
|
||||
if (propertyInfo != null) {
|
||||
IEnumerable<TAttribute> foundAttributes = propertyInfo.GetCustomAttributes<TAttribute>(includeInheritedAttributes);
|
||||
foreach (TAttribute attribute in foundAttributes) {
|
||||
attributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attributes.Count > 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13a6cb5c52b6ad744a90547f102f5296
|
||||
@@ -0,0 +1,29 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Jovian.Utilities.Editor {
|
||||
public class NumberRangePropertyDrawer : PropertyDrawer {
|
||||
private const float PADDING = 1f;
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
|
||||
|
||||
float width = position.width * 0.5f - PADDING;
|
||||
Rect minRect = new Rect(position.x, position.y, width, position.height);
|
||||
Rect maxRect = new Rect(minRect.xMax + PADDING * 2f, position.y, width, position.height);
|
||||
|
||||
int indentLevel = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
EditorGUI.PropertyField(minRect, property.FindPropertyRelative("min"), GUIContent.none);
|
||||
EditorGUI.PropertyField(maxRect, property.FindPropertyRelative("max"), GUIContent.none);
|
||||
EditorGUI.indentLevel = indentLevel;
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(FloatRange))]
|
||||
public class FloatRangePropertyDrawer : NumberRangePropertyDrawer { }
|
||||
|
||||
[CustomPropertyDrawer(typeof(IntRange))]
|
||||
public class IntRangePropertyDrawer : NumberRangePropertyDrawer { }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 225b3cfe03be1ff41b3f72b06ae2fa23
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "JovianUtilities.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"JovianUtilities"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9747d4f21927cf4c9f4a1c9c0680d86
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user