10using System.Reflection;
11using System.Linq.Expressions;
27 public struct PropertyAccessor {
28 private static MethodInfo GetFloatMethod;
29 private static MethodInfo GetColorMethod;
30 private static MethodInfo GetVectorMethod;
32 static PropertyAccessor() {
33 Type[] intArr =
new Type[] { typeof(
int) };
34 GetFloatMethod = typeof(Material).GetMethod(
"GetFloat", intArr);
35 GetColorMethod = typeof(Material).GetMethod(
"GetColor", intArr);
36 GetVectorMethod = typeof(Material).GetMethod(
"GetVector", intArr);
39 private Func<float> _accessor;
48 public PropertyAccessor(GameObject root, EditorCurveBinding binding,
bool failureIsZero =
false) {
51 GameObject target = getTargetObject(root, binding);
53 throw new InvalidOperationException(
"Target object cannot be null");
57 Component component = target.GetComponent(binding.type);
58 if (component ==
null) {
59 throw new InvalidOperationException(
"Could not find a component of type " + binding.type +
" on object " + target.name);
63 Expression propertyExpr;
65 string[] propertyPath = binding.propertyName.Split(
'.');
68 if (propertyPath[0] ==
"material" && component is Renderer) {
69 propertyExpr = buildMaterialExpression(component, propertyPath);
71 propertyExpr = buildFieldExpression(component, propertyPath, binding);
76 _accessor = Expression.Lambda<Func<float>>(propertyExpr).Compile();
77 }
catch (Exception e) {
81 Debug.LogError(
"Exception when trying to construct PropertyAccessor, curves will be incorrect.");
82 Debug.LogException(e);
93 public float Access() {
101 private static GameObject getTargetObject(GameObject root, EditorCurveBinding binding) {
102 string[] names = binding.path.Split(
'/');
103 GameObject target = root;
105 foreach (var name
in names) {
106 for (
int i = 0; i < target.transform.childCount; i++) {
107 var child = target.transform.GetChild(i);
108 if (child.gameObject.name == name) {
109 target = child.gameObject;
122 private static Expression buildMaterialExpression(
Component component,
string[] propertyPath) {
124 Material material = (component as Renderer).sharedMaterial;
125 if (material ==
null) {
126 throw new InvalidOperationException(
"Could not record property because material was null");
129 Shader shader = material.shader;
130 if (shader ==
null) {
131 throw new InvalidOperationException(
"Could not record property because shader was null");
136 string propertyName = propertyPath[1];
139 ShaderUtil.ShaderPropertyType? propertyType =
null;
140 int shaderPropCount = ShaderUtil.GetPropertyCount(shader);
141 for (
int i = 0; i < shaderPropCount; i++) {
142 if (ShaderUtil.GetPropertyName(shader, i) == propertyName) {
143 propertyType = ShaderUtil.GetPropertyType(shader, i);
148 if (!propertyType.HasValue) {
149 throw new InvalidOperationException(
"Could not find property " + propertyName +
" in shader " + shader);
153 var idExpr = Expression.Constant(Shader.PropertyToID(propertyName));
154 var matExpr = Expression.Property(Expression.Constant(component),
"sharedMaterial");
158 Expression propertyExpr;
159 switch (propertyType.Value) {
160 case ShaderUtil.ShaderPropertyType.Float:
161 case ShaderUtil.ShaderPropertyType.Range:
162 propertyExpr = Expression.Call(matExpr, GetFloatMethod, idExpr);
164 case ShaderUtil.ShaderPropertyType.Color:
165 propertyExpr = Expression.Call(matExpr, GetColorMethod, idExpr);
167 case ShaderUtil.ShaderPropertyType.Vector:
168 propertyExpr = Expression.Call(matExpr, GetVectorMethod, idExpr);
171 throw new NotImplementedException(
"Can not handle property type " + propertyType.Value);
178 for (
int i = 2; i < propertyPath.Length; i++) {
179 propertyExpr = Expression.PropertyOrField(propertyExpr, propertyPath[i]);
189 private static Expression buildFieldExpression(
Component component,
string[] propertyPath, EditorCurveBinding binding) {
193 Expression fieldExpr = Expression.PropertyOrField(Expression.Constant(component),
194 getClosestPropertyName(binding.type, propertyPath[0]));
197 for (
int i = 1; i < propertyPath.Length; i++) {
198 fieldExpr = Expression.PropertyOrField(fieldExpr, propertyPath[i]);
211 private static string getClosestPropertyName(Type type,
string bindingProperty) {
213 BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
216 var members = type.GetFields(flags).
219 Concat(type.GetProperties(flags).
225 MemberInfo exactMatch = members.Query().FirstOrDefault(f => f.Name == bindingProperty);
226 if (exactMatch !=
null) {
227 return exactMatch.Name;
232 MemberInfo minusPrefix = members.Query().FirstOrDefault(f => bindingProperty.Substring(2).ToLower() == f.Name.ToLower());
233 if (minusPrefix !=
null) {
234 return minusPrefix.Name;
239 MemberInfo containsMatch = members.Query().FirstOrDefault(f => bindingProperty.ToLower().Contains(f.Name.ToLower()));
240 if (containsMatch !=
null) {
241 return containsMatch.Name;
245 throw new InvalidOperationException(
"Cannot find a field or property with a name close to " + bindingProperty +
" for type " + type);
UnityEngine.Component Component