最近花了两周时间,终于完成一个很简单的蓝图工具。
其中我觉得比较复杂的,就是数据转换和数据计算,因为蓝图中数据类型是没办法直接获取到的。
计算的时候不可能罗列所有类型的相互计算,因为类型大多了。
由于数据要序列化,所以要保存类中的字段是可在Unity可序列化的,例如:Type 和 MethodInfo 以及 object 就不行
object不能序列化就导致反射函数的参数需要转换,带来很多麻烦
一、定义保存数据的类
[Serializable]
public class SerializableClass
{
public byte bytedata;
public short shortdata;
public int intdata;
public string stringdata = "";
public float floatdata;
public double doubledata;
public bool booldata;
public Color colordata;
public Vector2 Vector2data;
public Vector3 Vector3data;
public Vector4 Vector4data;
public Vector2Int Vector2Intdata;
public Vector3Int Vector3Intdata;
public AnimationCurve animationCurvedata = new AnimationCurve();
public Gradient gradientdata = new Gradient();
public Ease animationType = Ease.Linear;
public UnityEngine.Object data = null;
public CustomType type = CustomType.Int16;
public SerializableClass()
{
}
public SerializableClass(object value)
{
type = (CustomType)Enum.Parse(typeof(CustomType), value.GetType().Name);
FieldInfo[] fieldinfo = typeof(SerializableClass).GetFields();
for (int i = 0; i < fieldinfo.Length; i++)
{
if(fieldinfo[i].FieldType == value.GetType() || value.GetType().IsInstanceOfType(fieldinfo[i].FieldType))
{
fieldinfo[i].SetValue(this, value);
}
}
}
public object GetData()
{
FieldInfo[] fieldinfo = typeof(SerializableClass).GetFields();
for (int i = 0; i < fieldinfo.Length; i++)
{
if (fieldinfo[i].FieldType.Name == type.ToString())
{
return fieldinfo[i].GetValue(this);
}
}
return data;
}
public void SetValue(object value )
{
if (value == null) return;
if (typeof(UnityEngine.Object).IsAssignableFrom(value.GetType()))
type = (CustomType)Enum.Parse(typeof(CustomType), typeof(UnityEngine.Object).Name);
else
type = (CustomType)Enum.Parse(typeof(CustomType), value.GetType().Name);
FieldInfo[] fieldinfo = typeof(SerializableClass).GetFields();
for (int i = 0; i < fieldinfo.Length; i++)
{
if (fieldinfo[i].FieldType == value.GetType() || fieldinfo[i].FieldType.IsAssignableFrom(value.GetType()))
{
fieldinfo[i].SetValue(this, value);
}
}
}
}
public enum CustomType
{
Byte,
Int16,
Int32,
String,
Single,
Double,
Boolean,
Color,
Vector2,
Vector3,
Vector4,
Vector2Int,
Vector3Int,
AnimationCurve,
Gradient,
Object,
Ease
}
首先把数据分成3种,一种是基础的数据类型例如int、float等,第二种是在Unity种定义的没有继承Object的类型,一般是Unity种的结构体,第三种是Unity中定义的继承与Object的类型,例如继承MonoBehaviour的类。
因为C#中的Type不能序列化,所以自定义CustomType,这个通过对比类型名和CustomType中的字段,区分属于哪种类型。
二、计算
public enum MathParameterEnum
{
[CanUsedType(null, "GreaterThan")]
[TipsName("大于")]
Greater = 0,
[CanUsedType(null, "LessThan")]
[TipsName("小于")]
Less,
[CanUsedType(null, "")]
[TipsName("等于")]
Equal,
[CanUsedType(null, "")]
[TipsName("不等于")]
NotEqual,
[CanUsedType(null, "Add")]
[TipsName("加法")]
Addition,
[CanUsedType(null, "Subtract")]
[TipsName("减法")]
Subtraction,
[CanUsedType(null, "Multiply")]
[TipsName("乘法")]
Multiply,
[CanUsedType(null, "Divide")]
[TipsName("除法")]
Division,
[TipsName("绝对值")]
[CanUsedType(typeof(Mathf) , "Abs")]
Abs = 8,
[TipsName("正切")]
[CanUsedType(typeof(Mathf), "Tan")]
Tan,
[TipsName("余弦")]
[CanUsedType(typeof(Mathf), "Cos")]
Cos,
[TipsName("正弦")]
[CanUsedType(typeof(Mathf), "Sin")]
Sin,
[TipsName("长度归一")]
[CanUsedType(null , "Normalize")]
Normalized = 12,
[TipsName("角度" )]
[CanUsedType(null, "Angle")]
Angle,
[TipsName("距离")]
[CanUsedType(null, "Distance")]
Distance,
}
首先通过自定义特性来简化计算的遍历。这里有三种计算类型,一种是基础类型的加减乘除运算,一种是Mathf类或者其他计算类中的函数,还有一种是数据类型自定义的计算。
其中大部分运算都可以通过反射实现,但是这里有两个问题。
1、隐式转换
例如Vector3 可以和float 相乘,也可以和Int相乘,但是Vector3的自定义运算中并没有和int相乘的函数。因为int可以隐式转换乘float,然后再调用float的运算重载。
因此在计算判断的时候,需要考虑类型能否隐式转换为可计算的类型。
//判断类型是否有隐式转换
public static bool HasImplicitConversion(Type baseType, Type targetType)
{
if (IsNumber(baseType, targetType))
{
if(typeof(double) == targetType && (typeof(long) == targetType || typeof(float) == baseType || typeof(int) == baseType|| typeof(short) == baseType|| typeof(byte) == baseType))
{
return true;
}
if (typeof(float) == targetType && (typeof(long) == targetType || typeof(int) == baseType || typeof(short) == baseType || typeof(byte) == baseType))
{
return true;
}
if (typeof(long) == targetType && (typeof(int) == baseType || typeof(short) == baseType || typeof(byte) == baseType))
{
return true;
}
if (typeof(int) == targetType && (typeof(short) == baseType || typeof(byte) == baseType))
{
return true;
}
if (typeof(short) == targetType && (typeof(byte) == baseType))
{
return true;
}
}
return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)
.Any(mi => {
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
return pi != null && pi.ParameterType == baseType;
});
}
因为C#的基础数据类型,并没有靠op_Implicit去写隐式转换,所以只能手写。
2.基本数据的计算
Vector3这种类型的加减乘除可以通过发射寻找计算函数,但是float,int,double等的加减运算就不行了。
public static BinaryExpression CanMath(ParameterExpression type1, ParameterExpression type2, string methodName)
{
try
{
BinaryExpression obj = (BinaryExpression)typeof(Expression).InvokeMember(methodName,
System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Static
| System.Reflection.BindingFlags.Public, null, null,
new object[] { type1, type2 });
return obj;
}
catch (Exception e)
{
return null;
}
}
public static object MathNumber(List
这种方式虽然还是很麻烦,但是比用if else 简单了很多很多。
(T)Convert.ChangeType(data.Value, typeof(T)); 应用为类型转换为范型