Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Streamlined data entry

Discussion in 'General Discussion' started by BinaryCats, Apr 4, 2016.

  1. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    Last edited: Apr 6, 2016
    elmar1028, zoran404, Kiwasi and 2 others like this.
  2. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    Very good. I have wanted to do this type of thing a few times (the simpler bool check) and thought it would be a nice thing to be built into Unity.

    Btw, did you know there's an attribute named Conditional in System.Diagnotics?
     
    Last edited: Apr 5, 2016
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Looks pretty cool. Not a problem I've ever encountered, but cool none the less.

    You should also post this on the scripting forum. There will be some solid interest there.
     
  4. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    This is exactly what I was looking for!
    It was annoying me that my melee units had field for gun recoil and bullet prefab when they don't use those and writing a custom inspector just because of that was really not worth the effort.
    I'd really like to see this in the wiki!

    Anyway it would have been nice if you mentioned in your blog post that enums are passed as strings, so I wouldn't have to waste 20 minutes figuring it out by looking at your code d
    Also that caused an error which made the rest of the properties not show, so I'd like to suggest you use try-catch so when people pass wrong argument type (like an enum value instead of name, like I did) it would break the inspector.

    Code (csharp):
    1.   public override void OnGUI(Rect position,
    2.                  SerializedProperty property,
    3.                  GUIContent label)
    4.    {
    5.      bute.ShouldBeShown = true;
    6.      try {
    7.        bute.ShouldBeShown = ShouldShow(property);
    8.      }
    9.      catch(Exception e)
    10.      {
    11.        Debug.LogError(e.ToString());
    12.      }
    13.  
    14.      if (bute.ShouldBeShown)
    15.        EditorGUI.PropertyField(position, property, label, false);
    16.    }
    And one more change, you did not account for padding in GetPropertyHeight function. If you change the return value from 0 to -2f you'll get the exact position for the next property.

    Then I'd like to add that when you have a enum for which you test for multiple values the condition becoms needlesly long, so I wrote another constructor for your ConditionalAttribute class that tests for multiple values:

    Code (csharp):
    1. public ConditionalAttribute(string LHSVaribleName, object[] RHSValues)
    2.    {
    3.      m_RHSValues = RHSValues;
    4.      m_LHSVaribleNames = new string[m_RHSValues.Length];
    5.      m_Operator = new OperatorEnum[m_RHSValues.Length];
    6.      m_BetweenElementsLogic = new LogicEnum[m_RHSValues.Length - 1];
    7.  
    8.      for (int i = 0; i < m_RHSValues.Length; i++)
    9.      {
    10.        m_LHSVaribleNames[i] = LHSVaribleName;
    11.        m_Operator[i] = (int)OperatorEnum.Equals;
    12.      }
    13.      for (int i = 0; i < m_RHSValues.Length - 1; i++)
    14.      {
    15.        m_BetweenElementsLogic[i] = LogicEnum.OR;
    16.      }
    17.    }
    Code (csharp):
    1. // Now this:
    2. [Conditional(new[]{"attackType", "attackType"}, new int[]{}, new[]{"Gun", "Canon"}, new[]{(int)ConditionalAttribute.LogicEnum.OR})]
    3.  
    4. // Becomes this:
    5. [Conditional("attackType", new[]{"Gun", "Canon"})]

    Edit:
    Just a suggestion in case you have time: I really don't like that the constructor takes array of properties then array of comparators, then array of values and also array of logical operators. Instead I think it would look more readable if you were taking an array (as params) of objects (struct-s) that took those individual values.
     
    Last edited: Apr 5, 2016
  5. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    Yeah, I thought unity should really include something like this by default. hopfully they will someday. :)

    and thanks for the shout, I should really rename it. now to decide what :)

    Thanks for the words. I don't really use this forum much, I tried in another forum, but he too suggested to move it somewhere else haha, any suggestions of which forum to use?

    Thanks, it annoyed us too :). hmm, I must have forgot. I could have sworn I said about having to cast enum values to a int. If you cast to an int its better than a string as you can do > enumvalue and a like. I was working on trying to get enum values working (i.e. no casting necessary), but I kind of ran out of time :).

    I kind of expect the person to put the correct values in and originally I was checking this. I think your tryCatch is a good solution, ill add it to the code tomorrow. (ill paste updated code on this thread)

    I saw this today :) I didn't really have many conditional varibles before testing this, when you have a bunch the 2px gap is noticeable. This is fixed :)

    I like this, I don't like the constructors being long. I think I will add 'betweenelements' default param to the constructor to


    I did think about this. and I do agree with you, it is a little messy. Its a little bit scary, however, because with attributes you are only allowed certain values taken into the constructor (https://msdn.microsoft.com/en-us/library/aa664615(v=vs.71).aspx) it would need to be of type object (as you suggested), however that means anything could be passed in. And it would be prone for people to put in wrong data. I would like to do this suggestion, I just need to think about it more :)
     
  6. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    I should have thought about that problem.
    In that case what about multiple attributes?

    I might experiment with this and the enum thing when I get some free time.
     
  7. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    by multiple attributes do you mean:
    Code (csharp):
    1.  
    2. [atribute1]
    3. [atribute2]
    4. public var variable;
    5.  
    ?

    If so do you know how to get the different attributes associated with the variable that is being drawn in the drawer (does that make sense)? I couldn't figure out how to do it.

    I tried:
    Code (csharp):
    1.  
    2.  
    3. FieldInfo[] fields = this.GetType ().GetFields (BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);foreach (FieldInfo field in fields) {
    4.     object[] attributes = field.GetCustomAttributes (true);
    5. [LIST=1]
    6.  
    [/LIST]

    1. on the property, and the property.getobject. nada.


    1. If you know how to do this it would be a big help. At the moment I have to create multiple different attributes, for say the Range attribute. the latest revision of code (which I will post shortly) makes it pretty easy to do this (you just inherit from conditionalatributedrawer and override a function)) but it is still annoying
     
  8. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    @BinaryCats You can iterate through all the attributes in PropertyDrawer.OnGUI using the PropertyDrawer.fieldInfo property.

    Code (csharp):
    1. foreach(ConditionalAttribute atr in fieldInfo.GetCustomAttributes(false)) //false because inheritance will never happen for this
    2. {
    3.    Debug.Log(property.name +": "+ atr.m_RHSValues.Length +" => "+ atr.m_RHSValues[0]); //m_RHSValues has to be public
    4. }
    Note: You should check if the attribute returned by GetCustomAttributes is ConditionalAttribute. I was just lazy.

    Also when you do things this way then the attribute could hold single variables only, instead of arrays.

    Note: Add this above ConditionalAttribute: [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
     
    Last edited: Apr 6, 2016
  9. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    nice, Thanks! I will see what I can do with this information.

    I have updated the code, please note I have changed the enum names.

    I also changed how it handles drawing of the property, I added two functions, drawproperty and dontdrawproperty. this allows easier inheritance of the Conditional Attribute, if you wanted to change it to say a readonly toggle instead of a draw/don't draw toggle. I can supply code for that if someone wants.

    I also handle enums better, you can now pass in the enum value, an int or the string of the enum value as the RHS param

    Atribute:
    Code (csharp):
    1.  
    2.  
    3. publicclassConditionalAttribute : PropertyAttribute
    4.  
    5. {
    6.  
    7. publicenumComparisonOperators { Equals, NotEqualTo, GreaterThan, LessThan, EqualsOrGreaterThan, EqualsOrLessThan, AND, OR }
    8.  
    9. publicenumLogicOperators { AND, OR }
    10.  
    11.  
    12.  
    13. protectedstring[] m_LHSVaribleNames;
    14.  
    15. protectedComparisonOperators[] m_Operator;
    16.  
    17. protectedobject[] m_RHSValues;
    18.  
    19. protectedLogicOperators[] m_BetweenElementsLogic;
    20.  
    21. protectedbool m_ShouldBeShown = true;
    22.  
    23. publicbool ShouldBeShown
    24.  
    25. {
    26.  
    27. get { return m_ShouldBeShown; }
    28.  
    29. set { m_ShouldBeShown = value; }
    30.  
    31. }
    32.  
    33. publicstring[] LHSVaribleNames
    34.  
    35. {
    36.  
    37. get { return m_LHSVaribleNames; }
    38.  
    39. }
    40.  
    41. publicComparisonOperators[] Operator
    42.  
    43. {
    44.  
    45. get { return m_Operator; }
    46.  
    47. }
    48.  
    49. publicobject[] RHSValues
    50.  
    51. {
    52.  
    53. get { return m_RHSValues; }
    54.  
    55. }
    56.  
    57. publicLogicOperators[] BetweenElementsLogic
    58.  
    59. {
    60.  
    61. get { return m_BetweenElementsLogic; }
    62.  
    63. }
    64.  
    65.  
    66.  
    67. //LHS, is the varible name to check which are got through reflection
    68.  
    69. //Opperator, is the operator to use for the check
    70.  
    71. //RHS, is the value to check against
    72.  
    73. //BetweenElements, and/or to use between the last element and this element
    74.  
    75. public ConditionalAttribute(string[] LHS, int[] Operator, object[] RHS, int[] BetweenElements)
    76.  
    77. {
    78.  
    79. int maxsize = LHS.Length;
    80.  
    81. m_LHSVaribleNames = newstring[maxsize];
    82.  
    83. m_Operator = newComparisonOperators[maxsize];
    84.  
    85. m_RHSValues = newobject[maxsize];
    86.  
    87. m_BetweenElementsLogic = newLogicOperators[maxsize - 1];
    88.  
    89. m_LHSVaribleNames = LHS;
    90.  
    91. for (int i = 0; i < maxsize; i++)
    92.  
    93. {
    94.  
    95. if (i < Operator.Length)
    96.  
    97. {
    98.  
    99. m_Operator[i] = (ComparisonOperators)Operator[i];
    100.  
    101. }
    102.  
    103. else
    104.  
    105. m_Operator[i] = (int)ComparisonOperators.Equals;//Set it to == if no value was given
    106.  
    107. }
    108.  
    109. for (int i = 0; i < maxsize; i++)
    110.  
    111. {
    112.  
    113. if (i < RHS.Length)
    114.  
    115. {
    116.  
    117. m_RHSValues[i] = RHS[i];
    118.  
    119. }
    120.  
    121. else
    122.  
    123. m_RHSValues[i] = RHS[RHS.Length - 1];//Set it to the last value if no value was given
    124.  
    125. }
    126.  
    127. for (int i = 0; i < maxsize - 1; i++)
    128.  
    129. {
    130.  
    131. if (i < BetweenElements.Length)
    132.  
    133. {
    134.  
    135. m_BetweenElementsLogic[i] = (LogicOperators)BetweenElements[i];
    136.  
    137. }
    138.  
    139. else
    140.  
    141. m_BetweenElementsLogic[i] = LogicOperators.AND;//Set it to && if no value was given
    142.  
    143. }
    144.  
    145. }
    146.  
    147. public ConditionalAttribute(string LHSVaribleName, object RHSValue)
    148.  
    149. {
    150.  
    151. m_LHSVaribleNames = newstring[] { LHSVaribleName };
    152.  
    153. m_RHSValues = newobject[] { RHSValue };
    154.  
    155. m_Operator = newComparisonOperators[] { ComparisonOperators.Equals };
    156.  
    157. }
    158.  
    159. public ConditionalAttribute(string LHSVaribleName, object[] RHSValues, LogicOperators BetweenElements = LogicOperators.OR)
    160.  
    161. {
    162.  
    163. m_RHSValues = RHSValues;
    164.  
    165. m_LHSVaribleNames = newstring[m_RHSValues.Length];
    166.  
    167. m_Operator = newComparisonOperators[m_RHSValues.Length];
    168.  
    169. m_BetweenElementsLogic = newLogicOperators[m_RHSValues.Length - 1];
    170.  
    171. for (int i = 0; i < m_RHSValues.Length; i++)
    172.  
    173. {
    174.  
    175. m_LHSVaribleNames[i] = LHSVaribleName;
    176.  
    177. m_Operator[i] = (int)ComparisonOperators.Equals;
    178.  
    179. }
    180.  
    181. for (int i = 0; i < m_RHSValues.Length - 1; i++)
    182.  
    183. {
    184.  
    185. m_BetweenElementsLogic[i] = BetweenElements;
    186.  
    187. }
    188.  
    189. }
    190.  
    191. public ConditionalAttribute()
    192.  
    193. { }
    194.  
    195. }
    196.  
    197.  
    198.  
    199.  
    200.  
    Drawer
    Code (csharp):
    1.  
    2.  
    3. //Conditional
    4.  
    5. //Decides if a varible should be shown in the inspector depending on another (boolean) varible
    6.  
    7. [CustomPropertyDrawer(typeof(ConditionalAttribute))]
    8.  
    9. publicclassConditionalDrawer : PropertyDrawer
    10.  
    11. {
    12.  
    13. publicoverridefloat GetPropertyHeight(SerializedProperty property,
    14.  
    15. GUIContent label)
    16.  
    17. {
    18.  
    19. if (!bute.ShouldBeShown)
    20.  
    21. return -2.0f;
    22.  
    23. returnEditorGUI.GetPropertyHeight(property, label, true);
    24.  
    25. }
    26.  
    27.  
    28.  
    29. SerializedProperty ConditionalProperty;
    30.  
    31. publicoverridevoid OnGUI(Rect position,
    32.  
    33. SerializedProperty property,
    34.  
    35. GUIContent label)
    36.  
    37. {
    38.  
    39. bute.ShouldBeShown = true;
    40.  
    41. try
    42.  
    43. {
    44.  
    45. bute.ShouldBeShown = ShouldShow(property);
    46.  
    47. }
    48.  
    49. catch (Exception e)
    50.  
    51. {
    52.  
    53. Debug.LogError(e.ToString());
    54.  
    55. }
    56.  
    57. if (bute.ShouldBeShown)
    58.  
    59. DrawProperty(position, property, label);
    60.  
    61. else
    62.  
    63. DontDrawProperty(position, property, label);
    64.  
    65.  
    66.  
    67.  
    68.  
    69. }
    70.  
    71. virtualpublicvoid DrawProperty(Rect position,
    72.  
    73. SerializedProperty property,
    74.  
    75. GUIContent label)
    76.  
    77. {
    78.  
    79. EditorGUI.PropertyField(position, property, label, false);
    80.  
    81. }
    82.  
    83. virtualpublicvoid DontDrawProperty(Rect position,
    84.  
    85. SerializedProperty property,
    86.  
    87. GUIContent label)
    88.  
    89. {
    90.  
    91. }
    92.  
    93. privatestring GetPath(SerializedProperty property)
    94.  
    95. {
    96.  
    97. string path = property.propertyPath;
    98.  
    99. int index = path.LastIndexOf(".");
    100.  
    101. return path.Substring(0, index + 1);
    102.  
    103. }
    104.  
    105. protectedvirtualConditionalAttribute bute
    106.  
    107. {
    108.  
    109. get { return (ConditionalAttribute)attribute; }
    110.  
    111. }
    112.  
    113. protectedbool ShouldShow(SerializedProperty property)
    114.  
    115. {
    116.  
    117. string path = GetPath(property);
    118.  
    119.  
    120.  
    121. bool previous = false;
    122.  
    123. for (int i = 0; i < bute.LHSVaribleNames.Length; i++)
    124.  
    125. {
    126.  
    127. string conditionname = path + bute.LHSVaribleNames[i];
    128.  
    129. ConditionalProperty = property.serializedObject.FindProperty(conditionname);
    130.  
    131. bool test = false;
    132.  
    133. object lhsValue;
    134.  
    135. if (ConditionalProperty.propertyType == SerializedPropertyType.Integer)
    136.  
    137. {
    138.  
    139. lhsValue = ConditionalProperty.intValue;
    140.  
    141. }
    142.  
    143. else
    144.  
    145. if (ConditionalProperty.propertyType == SerializedPropertyType.Float)
    146.  
    147. {
    148.  
    149. lhsValue = ConditionalProperty.floatValue;
    150.  
    151. }
    152.  
    153. else
    154.  
    155. if (ConditionalProperty.propertyType == SerializedPropertyType.Boolean)
    156.  
    157. {
    158.  
    159. lhsValue = ConditionalProperty.boolValue;
    160.  
    161. }
    162.  
    163. elseif (ConditionalProperty.propertyType == SerializedPropertyType.Enum)
    164.  
    165. {
    166.  
    167. //It would be nice to use the enum values. However, you cant get the enum value for LHS, only string value.
    168.  
    169. //And as we dont know enum type (can we find it?), we must change the RHS to match the type of LHS (string)
    170.  
    171. //If both where of type Enum, we could simply do LHS == RHS
    172.  
    173. if (bute.RHSValues[i].GetType().IsEnum)
    174.  
    175. {
    176.  
    177. bute.RHSValues[i] = bute.RHSValues[i].ToString(); // If an enum was passed in, change it to a string
    178.  
    179. lhsValue = ConditionalProperty.enumNames[ConditionalProperty.enumValueIndex];
    180.  
    181. }
    182.  
    183. elseif (bute.RHSValues[i].GetType() == typeof(string) )
    184.  
    185. {
    186.  
    187. lhsValue = ConditionalProperty.enumNames[ConditionalProperty.enumValueIndex];//if it is a string, grab the name
    188.  
    189. }
    190.  
    191. else
    192.  
    193. lhsValue = ConditionalProperty.enumValueIndex;//Otherwise just use the index (if a number was passed in)
    194.  
    195. }
    196.  
    197. else
    198.  
    199. thrownewException("Type needs implementing");
    200.  
    201.  
    202.  
    203. test = Check(lhsValue, bute.Operator[i], bute.RHSValues[i]);
    204.  
    205. if (bute.BetweenElementsLogic == null)
    206.  
    207. {
    208.  
    209. return test;
    210.  
    211. }
    212.  
    213. if (i != 0 && test && bute.BetweenElementsLogic[i - 1] == ConditionalAttribute.LogicOperators.AND)
    214.  
    215. {
    216.  
    217. if (!previous)
    218.  
    219. {
    220.  
    221. test = false;
    222.  
    223. }
    224.  
    225. }
    226.  
    227. if (test && i < bute.BetweenElementsLogic.Length && bute.BetweenElementsLogic[i] == ConditionalAttribute.LogicOperators.OR)
    228.  
    229. {
    230.  
    231. returntrue;
    232.  
    233. }
    234.  
    235. previous = test;
    236.  
    237. }
    238.  
    239. return previous;
    240.  
    241. }
    242.  
    243. protectedbool Check(object LHS, ConditionalAttribute.ComparisonOperators op, object RHS)
    244.  
    245. {
    246.  
    247. if (!(LHS isIComparable) || !(RHS isIComparable))
    248.  
    249. thrownewException("Check using non basic type");
    250.  
    251. switch (op)
    252.  
    253. {
    254.  
    255. caseConditionalAttribute.ComparisonOperators.Equals:
    256.  
    257. return ((IComparable)LHS).CompareTo(RHS) == 0;
    258.  
    259. caseConditionalAttribute.ComparisonOperators.NotEqualTo:
    260.  
    261. return ((IComparable)LHS).CompareTo(RHS) != 0;
    262.  
    263. caseConditionalAttribute.ComparisonOperators.EqualsOrGreaterThan:
    264.  
    265. return ((IComparable)LHS).CompareTo(RHS) >= 0;
    266.  
    267. caseConditionalAttribute.ComparisonOperators.EqualsOrLessThan:
    268.  
    269. return ((IComparable)LHS).CompareTo(RHS) <= 0;
    270.  
    271. caseConditionalAttribute.ComparisonOperators.GreaterThan:
    272.  
    273. return ((IComparable)LHS).CompareTo(RHS) > 0;
    274.  
    275. caseConditionalAttribute.ComparisonOperators.LessThan:
    276.  
    277. return ((IComparable)LHS).CompareTo(RHS) < 0;
    278.  
    279. default:
    280.  
    281. break;
    282.  
    283. }
    284.  
    285. returnfalse;
    286.  
    287. }
    288.  
    289. }
    290.  
    291.  
    292.  
    good job copy/paste, loosing the format :|

    Is there a better way to copy/paste from VS?
     
    Last edited: Apr 6, 2016
  10. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    I Fixed enum parsing, you can now pass a enum value though as the right hand side value (RHS), rather than having to cast it to string or an int (these still work). I also concatenated the 3 arrays (LHS, operator and RHS) into one object called "Statement" (maybe there is a better name)

    attribute
    Code (csharp):
    1.  
    2. publicclassConditionalAttribute : PropertyAttribute
    3.  
    4. {
    5.  
    6. publicenumComparisonOperators { Equals, NotEqualTo, GreaterThan, LessThan, EqualsOrGreaterThan, EqualsOrLessThan}
    7.  
    8. publicenumLogicOperators { AND, OR }
    9.  
    10. publicstructStatement
    11.  
    12. {
    13.  
    14. privatestring m_LHS;
    15.  
    16. privateComparisonOperators m_ComparasonOp;
    17.  
    18. privateobject m_RHS;
    19.  
    20. publicstring LHS
    21.  
    22. {
    23.  
    24. get { return m_LHS; }
    25.  
    26. }
    27.  
    28. publicobject RHS
    29.  
    30. {
    31.  
    32. get { return m_RHS; }
    33.  
    34. }
    35.  
    36. publicComparisonOperators ComparasonOperator
    37.  
    38. {
    39.  
    40. get { return m_ComparasonOp; }
    41.  
    42. }
    43.  
    44. public Statement(string lhs, ComparisonOperators comparator, object rhs) : this()
    45.  
    46. {
    47.  
    48. m_LHS = lhs;
    49.  
    50. m_ComparasonOp = comparator;
    51.  
    52. m_RHS = rhs;
    53.  
    54. }
    55.  
    56. }
    57.  
    58. protectedStatement[] m_Statements;
    59.  
    60. protectedLogicOperators[] m_BetweenElementsLogic;
    61.  
    62. protectedbool m_ShouldBeShown = true;
    63.  
    64. publicbool ShouldBeShown
    65.  
    66. {
    67.  
    68. get { return m_ShouldBeShown; }
    69.  
    70. set { m_ShouldBeShown = value; }
    71.  
    72. }
    73.  
    74. publicLogicOperators[] BetweenElementsLogic
    75.  
    76. {
    77.  
    78. get { return m_BetweenElementsLogic; }
    79.  
    80. }
    81.  
    82. publicStatement[] Statements
    83.  
    84. {
    85.  
    86. get { return m_Statements; }
    87.  
    88. }
    89.  
    90. //LHS, is the varible name to check which are got through reflection
    91.  
    92. //Opperator, is the operator to use for the check
    93.  
    94. //RHS, is the value to check against
    95.  
    96. //BetweenElements, and/or to use between the last element and this element
    97.  
    98. public ConditionalAttribute(string[] LHS, int[] Operator, object[] RHS, int[] BetweenElements)
    99.  
    100. {
    101.  
    102. int maxsize = LHS.Length;
    103.  
    104. m_Statements = newStatement[maxsize];
    105.  
    106. m_BetweenElementsLogic = newLogicOperators[maxsize - 1];
    107.  
    108. for (int i = 0; i < maxsize; i++)
    109.  
    110. {
    111.  
    112. ComparisonOperators op;
    113.  
    114. if (i < Operator.Length)
    115.  
    116. {
    117.  
    118. op = (ComparisonOperators)Operator[i];
    119.  
    120. }
    121.  
    122. else
    123.  
    124. op = ComparisonOperators.Equals;//Set it to == if no value was given
    125.  
    126. object rhs;
    127.  
    128. if (i < RHS.Length)
    129.  
    130. {
    131.  
    132. rhs = RHS[i];
    133.  
    134. }
    135.  
    136. else
    137.  
    138. rhs = RHS[RHS.Length - 1];//Set it to the last value if no value was given
    139.  
    140. m_Statements[i] = newStatement(LHS[i], op, rhs);
    141.  
    142. }
    143.  
    144. for (int i = 0; i < maxsize - 1; i++)
    145.  
    146. {
    147.  
    148. if (i < BetweenElements.Length)
    149.  
    150. {
    151.  
    152. m_BetweenElementsLogic[i] = (LogicOperators)BetweenElements[i];
    153.  
    154. }
    155.  
    156. else
    157.  
    158. m_BetweenElementsLogic[i] = LogicOperators.AND;//Set it to && if no value was given
    159.  
    160. }
    161.  
    162. }
    163.  
    164. public ConditionalAttribute(string LHSVaribleName, object RHSValue)
    165.  
    166. {
    167.  
    168. m_Statements = newStatement[] { newStatement(LHSVaribleName, ComparisonOperators.Equals, RHSValue) };
    169.  
    170. }
    171.  
    172. public ConditionalAttribute(string LHSVaribleName, object[] RHSValues, LogicOperators BetweenElements = LogicOperators.OR)
    173.  
    174. {
    175.  
    176. m_Statements = newStatement[RHSValues.Length];
    177.  
    178. m_BetweenElementsLogic = newLogicOperators[RHSValues.Length - 1];
    179.  
    180. for (int i = 0; i < m_Statements.Length; i++)
    181.  
    182. {
    183.  
    184. m_Statements[i] = newStatement(LHSVaribleName, ComparisonOperators.Equals, RHSValues[i]);
    185.  
    186. }
    187.  
    188. for (int i = 0; i < m_BetweenElementsLogic.Length; i++)
    189.  
    190. {
    191.  
    192. m_BetweenElementsLogic[i] = BetweenElements;
    193.  
    194. }
    195.  
    196. }
    197.  
    198. public ConditionalAttribute()
    199.  
    200. { }
    201.  
    202. }
    203.  
    204.  


    Drawer
    Code (csharp):
    1.  
    2.  
    3. //Conditional
    4.  
    5. //Decides if a varible should be shown in the inspector depending on another (boolean) varible
    6.  
    7. [CustomPropertyDrawer(typeof(ConditionalAttribute))]
    8.  
    9. publicclassConditionalDrawer : PropertyDrawer
    10.  
    11. {
    12.  
    13. publicoverridefloat GetPropertyHeight(SerializedProperty property,
    14.  
    15. GUIContent label)
    16.  
    17. {
    18.  
    19. if (!bute.ShouldBeShown)
    20.  
    21. return -2.0f;
    22.  
    23. returnEditorGUI.GetPropertyHeight(property, label, true);
    24.  
    25. }
    26.  
    27. SerializedProperty ConditionalProperty;
    28.  
    29. publicoverridevoid OnGUI(Rect position,
    30.  
    31. SerializedProperty property,
    32.  
    33. GUIContent label)
    34.  
    35. {
    36.  
    37. bute.ShouldBeShown = true;
    38.  
    39. try// catch any parsing errors
    40.  
    41. {
    42.  
    43. bute.ShouldBeShown = ShouldShow(property);
    44.  
    45. }
    46.  
    47. catch (Exception e)
    48.  
    49. {
    50.  
    51. Debug.LogError(e.ToString());
    52.  
    53. }
    54.  
    55. if (bute.ShouldBeShown)
    56.  
    57. DrawProperty(position, property, label);
    58.  
    59. else
    60.  
    61. DontDrawProperty(position, property, label);
    62.  
    63. }
    64.  
    65. virtualpublicvoid DrawProperty(Rect position,
    66.  
    67. SerializedProperty property,
    68.  
    69. GUIContent label)
    70.  
    71. {
    72.  
    73. EditorGUI.PropertyField(position, property, label, false);
    74.  
    75. }
    76.  
    77. virtualpublicvoid DontDrawProperty(Rect position,
    78.  
    79. SerializedProperty property,
    80.  
    81. GUIContent label)
    82.  
    83. {
    84.  
    85. }
    86.  
    87. privatestring GetPath(SerializedProperty property)
    88.  
    89. {
    90.  
    91. string path = property.propertyPath;
    92.  
    93. int index = path.LastIndexOf(".");
    94.  
    95. return path.Substring(0, index + 1);
    96.  
    97. }
    98.  
    99. protectedvirtualConditionalAttribute bute
    100.  
    101. {
    102.  
    103. get { return (ConditionalAttribute)attribute; }
    104.  
    105. }
    106.  
    107. protectedbool ShouldShow(SerializedProperty property)
    108.  
    109. {
    110.  
    111. string path = GetPath(property);
    112.  
    113.  
    114.  
    115. bool previous = false;
    116.  
    117. for (int i = 0; i < bute.Statements.Length; i++)
    118.  
    119. {
    120.  
    121. string conditionname = path + bute.Statements[i].LHS;
    122.  
    123. ConditionalProperty = property.serializedObject.FindProperty(conditionname);
    124.  
    125. bool test = false;
    126.  
    127. object lhsValue;
    128.  
    129. if (ConditionalProperty.propertyType == SerializedPropertyType.Integer)
    130.  
    131. {
    132.  
    133. lhsValue = ConditionalProperty.intValue;
    134.  
    135. }
    136.  
    137. else
    138.  
    139. if (ConditionalProperty.propertyType == SerializedPropertyType.Float)
    140.  
    141. {
    142.  
    143. lhsValue = ConditionalProperty.floatValue;
    144.  
    145. }
    146.  
    147. else
    148.  
    149. if (ConditionalProperty.propertyType == SerializedPropertyType.Boolean)
    150.  
    151. {
    152.  
    153. lhsValue = ConditionalProperty.boolValue;
    154.  
    155. }
    156.  
    157. elseif (ConditionalProperty.propertyType == SerializedPropertyType.Enum)
    158.  
    159. {
    160.  
    161. //Depending on what RHS param was passed in, parse the enum differently
    162.  
    163. if (bute.Statements[i].RHS.GetType().IsEnum)
    164.  
    165. {
    166.  
    167. lhsValue = Enum.Parse(bute.Statements[i].RHS.GetType(), ConditionalProperty.enumNames[ConditionalProperty.enumValueIndex]);//Grab the enum value from the enum type
    168.  
    169. }
    170.  
    171. elseif (bute.Statements[i].RHS.GetType() == typeof(string))
    172.  
    173. {
    174.  
    175. lhsValue = ConditionalProperty.enumNames[ConditionalProperty.enumValueIndex];//if it is a string, grab the name
    176.  
    177. }
    178.  
    179. else
    180.  
    181. lhsValue = ConditionalProperty.enumValueIndex;//Otherwise just use the index (if a number was passed in)
    182.  
    183. }
    184.  
    185. else
    186.  
    187. thrownewException("Type needs implementing");
    188.  
    189.  
    190.  
    191. test = Check(lhsValue, bute.Statements[i].ComparasonOperator, bute.Statements[i].RHS);
    192.  
    193. if (bute.BetweenElementsLogic == null)
    194.  
    195. {
    196.  
    197. return test;
    198.  
    199. }
    200.  
    201. if (i != 0 && test && bute.BetweenElementsLogic[i - 1] == ConditionalAttribute.LogicOperators.AND)
    202.  
    203. {
    204.  
    205. if (!previous)
    206.  
    207. {
    208.  
    209. test = false;
    210.  
    211. }
    212.  
    213. }
    214.  
    215. if (test && i < bute.BetweenElementsLogic.Length && bute.BetweenElementsLogic[i] == ConditionalAttribute.LogicOperators.OR)
    216.  
    217. {
    218.  
    219. returntrue;
    220.  
    221. }
    222.  
    223. previous = test;
    224.  
    225. }
    226.  
    227. return previous;
    228.  
    229. }
    230.  
    231. protectedbool Check(object LHS, ConditionalAttribute.ComparisonOperators op, object RHS)
    232.  
    233. {
    234.  
    235. if (!(LHS isIComparable) || !(RHS isIComparable))
    236.  
    237. thrownewException("Check using non basic type");
    238.  
    239. switch (op)
    240.  
    241. {
    242.  
    243. caseConditionalAttribute.ComparisonOperators.Equals:
    244.  
    245. return ((IComparable)LHS).CompareTo(RHS) == 0;
    246.  
    247. caseConditionalAttribute.ComparisonOperators.NotEqualTo:
    248.  
    249. return ((IComparable)LHS).CompareTo(RHS) != 0;
    250.  
    251. caseConditionalAttribute.ComparisonOperators.EqualsOrGreaterThan:
    252.  
    253. return ((IComparable)LHS).CompareTo(RHS) >= 0;
    254.  
    255. caseConditionalAttribute.ComparisonOperators.EqualsOrLessThan:
    256.  
    257. return ((IComparable)LHS).CompareTo(RHS) <= 0;
    258.  
    259. caseConditionalAttribute.ComparisonOperators.GreaterThan:
    260.  
    261. return ((IComparable)LHS).CompareTo(RHS) > 0;
    262.  
    263. caseConditionalAttribute.ComparisonOperators.LessThan:
    264.  
    265. return ((IComparable)LHS).CompareTo(RHS) < 0;
    266.  
    267. default:
    268.  
    269. break;
    270.  
    271. }
    272.  
    273. returnfalse;
    274.  
    275. }
    276.  
    277. }
    278.  
    279.  
     
    Last edited: Apr 6, 2016
  11. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    My version is in the attachment, so we would avoid formatting problem like in the previous post.

    Use case:
    Code (csharp):
    1. //This is from BinaryCats original version
    2. //If otherVar is equal to value then thisVar will be shown in inspector
    3. [Conditional("otherVarName", value)]
    4. public float thisVar;
    5.  
    6. //I added the option to check if otherVar is equal to any of values
    7. [Conditional("otherVarName", value1, value2, value3)]
    8.  
    9. //Or not equal to any
    10. [Conditional("otherVarName", ConditionalAttribute.IsAnyOf.No, value1, value2, value3)]
    11.  
    12. //And here's how you do other comparison operations
    13. [Conditional("otherVarName", ConditionalAttribute.Comparison.EqualsOrGreaterThan, value)]
    14.  
    15. //I also added the check for is between 2 values
    16. //Yes if it should be between; No if it shouldn't
    17. //You can also use Exclusive or Inclusive check ( < , > or <= , >= )
    18. [Conditional("otherVarName", ConditionalAttribute.IsBetween.ExclusiveYes, value1, value2)]
    19.  
    20. //You can now also chain multiple of those attributes by using the logical operator (default is AND)
    21. [Conditional("otherVarName1", value1, (int)ConditionalAttribute.Logical.OR)]
    22. [Conditional("otherVarName2", value2)] // AND is the default
    23. [Conditional("otherVarName3", value3)]
    24. //This would mean: otherVar1 == value1 || otherVar2 == value2 && otherVar3 == value3
    25.  
    26. //In BinaryCats original version you couldn't chain the attributes and would instead pass arrays to it
    27. [Conditional(new string[]{"otherVarName1", "otherVarName2"},
    28.         new int[]{(int)ConditionalAttribute.Comparison.Equals,(int)ConditionalAttribute.Comparison.GreaterThan},
    29.         new object[]{value1, value2}
    30.         new int[]{(int)ConditionalAttribute.Logical.And}
    31. )]
    32. //Which would mean: otherVar1 == value1 && otherVar2 > value2
    33. //This style is still supported

    Note 1: Using other attributes (eg. Range(0f,1f) ) in combination with ConditionalAttribute might cause ConditionalDrawer to not be called, because the drawer is only called for one attribute, thus making this script ineffective. To solve this change the order of the attributes.

    Note 2: Also because the drawer is only called for one attribute, when you include attributes like Range then their drawer wont be called and you wont get a slider. We solved this by creating the slider in ConditionalDrawer if the Range attribute is present, but other attributes, are not handled in this version and will give you a warning about this.
    Tooltip attribute is not affected by this script, because it's not drawn in the inspector window.
     

    Attached Files:

    Last edited: Apr 7, 2016