When working with unity and larger scripts that provide a lot of adjustable parameters, the inspector can quickly turn into a very unmanageable wall of text and entry fields.
So lets take a look at what can be done to make inspector navigation a much more enjoyable experience without having to write a custom inspector layout for your script.
In this post I quickly go over the use of HeaderAttributes to help you keep the inspector clean and easy to work with. And finally I explain how you can hide or disable inspector properties based on a bool field within your script.
Improving Basic Readability
Lets take a look at the following example inspector window.
Notice that with even a small script like this it can already be difficult to get a good idea of what is going on.
What we would like is the ability to group our fields in a way that makes sense for the designer. Making it very clear what the role is of the different variables within the functionality of my script. This can be done with HeaderAttributes.
Simply add the attribute in front of your public field and you are good to go. A simple, quick and super effective way to instantly increase the readability of your script within Unity’s editor.
[Header("Auto Aim")] public bool EnableAutoAim = false;
Unity provides a series of additional attributes that can help you in managing the look of your script within the inspector and how the user can interact with them. All without having to write special inspector specifically for this script.
Some useful and commonly used attributes to look into are the space, tooltip and range attributes.
Hide or Disable Fields based on user input
Using HeaderAttributes is all shiny and cool but what if we want to help the designer even further?
In this example most of the properties under the Auto Aim category will only be relevant when EnableAutoAim is set to true.
Say that, to avoid confusion and to keep our inspector nice and clean, we want to hide all the properties that are directly related to the EnableAutoAim if this property is set to false. Or instead of hiding them, we want to at least disable them, preventing user input.
To make this possible we need to create a custom attribute and matching PropertyDrawer.
I’m not going to go into the details on how PropertyDrawers and Attributes fully work. For that I will point you towards https://unity3d.com/learn/tutorials/modules/intermediate/live-training-archive/property-drawers-custom-inspectors.
But I will go over the logic and setup of this specific PropertyDrawer and attribute.
Lets get started 🙂 .
Step 1: Create the scripts
As mentioned we are going to need 2 scripts: ConditionalHideAttribute and ConditionalHidePropertyDrawer.
Important to note is that the ConditionalHidePropertyDrawer needs to be placed inside an Editor folder within the project and the ConditionalHideAttribute scripts needs to be located outside this folder. Otherwise you will not be able to find and use the attribute.
Step 2: ConditionalHideAttribute
First we create our Attribute. We want to be able to define a bool Field that will be in control of the hiding/enabling of the inspector properties and we also want to be able to choose between the hide and enable options.
The ConditionalHideAttribute inherits from the PropertyAttribute class and is nothing more than a simple data class with some constructors. The main purpose of this class is to provide additional data that will be used within the PropertyDrawer. If you want to add extra options/ parameters to this attribute, this is where you would add them.
The AttributeUsage attributes used at the top of the ConditionalHideAttribute class control where you will be able to use this attribute.
using UnityEngine; using System; using System.Collections; [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct, Inherited = true)] public class ConditionalHideAttribute : PropertyAttribute { //The name of the bool field that will be in control public string ConditionalSourceField = ""; //TRUE = Hide in inspector / FALSE = Disable in inspector public bool HideInInspector = false; public ConditionalHideAttribute(string conditionalSourceField) { this.ConditionalSourceField = conditionalSourceField; this.HideInInspector = false; } public ConditionalHideAttribute(string conditionalSourceField, bool hideInInspector) { this.ConditionalSourceField = conditionalSourceField; this.HideInInspector = hideInInspector; } }
Step 3 : ConditionalHidePropertyDrawer
In the PropertyDrawer we need to do a couple of things.
- When Unity wants to draw the property in the inspector we need to:
- Check the parameters that we used in our custom attribute
- Hide and/or disable the property that is being drawn based on the attribute parameters
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { //get the attribute data ConditionalHideAttribute condHAtt = (ConditionalHideAttribute)attribute; //check if the propery we want to draw should be enabled bool enabled = GetConditionalHideAttributeResult(condHAtt, property); //Enable/disable the property bool wasEnabled = GUI.enabled; GUI.enabled = enabled; //Check if we should draw the property if (!condHAtt.HideInInspector || enabled) { EditorGUI.PropertyField(position, property, label, true); } //Ensure that the next property that is being drawn uses the correct settings GUI.enabled = wasEnabled; }
In order to check if the property should be enabled or not we call GetConditionalHideAttributeResult.
private bool GetConditionalHideAttributeResult(ConditionalHideAttribute condHAtt, SerializedProperty property) { bool enabled = true; //Look for the sourcefield within the object that the property belongs to string propertyPath = property.propertyPath; //returns the property path of the property we want to apply the attribute to string conditionPath = propertyPath.Replace(property.name, condHAtt.ConditionalSourceField); //changes the path to the conditionalsource property path SerializedProperty sourcePropertyValue = property.serializedObject.FindProperty(conditionPath); if (sourcePropertyValue != null) { enabled = sourcePropertyValue.boolValue; } else { Debug.LogWarning("Attempting to use a ConditionalHideAttribute but no matching SourcePropertyValue found in object: " + condHAtt.ConditionalSourceField); } return enabled; }
- Calculate the height of our property so that (when the property needs to be hidden) the following properties that are being drawn don’t overlap
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { ConditionalHideAttribute condHAtt = (ConditionalHideAttribute)attribute; bool enabled = GetConditionalHideAttributeResult(condHAtt, property); if (!condHAtt.HideInInspector || enabled) { return EditorGUI.GetPropertyHeight(property, label); } else { //The property is not being drawn //We want to undo the spacing added before and after the property return -EditorGUIUtility.standardVerticalSpacing; } }
The complete ConditionalHidePropertyDrawer script then looks as follows:
using UnityEngine; using UnityEditor; [CustomPropertyDrawer(typeof(ConditionalHideAttribute))] using UnityEngine; using UnityEditor; [CustomPropertyDrawer(typeof(ConditionalHideAttribute))] public class ConditionalHidePropertyDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { ConditionalHideAttribute condHAtt = (ConditionalHideAttribute)attribute; bool enabled = GetConditionalHideAttributeResult(condHAtt, property); bool wasEnabled = GUI.enabled; GUI.enabled = enabled; if (!condHAtt.HideInInspector || enabled) { EditorGUI.PropertyField(position, property, label, true); } GUI.enabled = wasEnabled; } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { ConditionalHideAttribute condHAtt = (ConditionalHideAttribute)attribute; bool enabled = GetConditionalHideAttributeResult(condHAtt, property); if (!condHAtt.HideInInspector || enabled) { return EditorGUI.GetPropertyHeight(property, label); } else { return -EditorGUIUtility.standardVerticalSpacing; } } private bool GetConditionalHideAttributeResult(ConditionalHideAttribute condHAtt, SerializedProperty property) { bool enabled = true; string propertyPath = property.propertyPath; //returns the property path of the property we want to apply the attribute to string conditionPath = propertyPath.Replace(property.name, condHAtt.ConditionalSourceField); //changes the path to the conditionalsource property path SerializedProperty sourcePropertyValue = property.serializedObject.FindProperty(conditionPath); if (sourcePropertyValue != null) { enabled = sourcePropertyValue.boolValue; } else { Debug.LogWarning("Attempting to use a ConditionalHideAttribute but no matching SourcePropertyValue found in object: " + condHAtt.ConditionalSourceField); } return enabled; } }
Step 4: How to use
Using our newly created ConditionalHideAttribute is fairly straightforward and works much like the HeaderAttribute.
[Header("Auto Aim")] public bool EnableAutoAim = false; [ConditionalHide("EnableAutoAim", true)] public float Range = 0.0f; [ConditionalHide("EnableAutoAim", true)] public bool AimAtClosestTarget = true; [ConditionalHide("EnableAutoAim", true)] public DemoClass ExampleDataClass = new DemoClass(); [ConditionalHide("EnableAutoAim", true)] public AnimationCurve AimOffsetCurve = new AnimationCurve();
Leaving out the second parameter or setting it to false draws the property using the enable/disable method.
[Header("Resources")] public bool ConsumeResources = true; [ConditionalHide("ConsumeResources")] public bool DestroyOnResourcesDepleted = true; [ConditionalHide("ConsumeResources")] public float ResourceAmount = 100.0f; [ConditionalHide("ConsumeResources")] public DemoClass AnotherExampleDataClass = new DemoClass();
Some Caveats
This being Unity after all there are a few situations where this custom attribute will fail.
The first situation being Lists and arrays.
In those cases the property drawer will be applied to the content of the list but not on the list itself… .
You will still be able to adjust the number of items as well.
However when the List is part of a Seriazable class everything will behave as expected.
Why is this you ask? Because Unity, that’s why.
Another situation where the use of this attribute will fail is when being combined with different property drawers that change the way the property itself is being drawn such as the TextAreaAttribute.
This makes sense as this attribute performs his own OnGUI drawing and GetPropertyHeight calculation of the same property. The attribute that is called last will be the attribute that determines what is being drawn in this case.
Update 01 [15/03/2016]
Added support for 2 bools instead of only 1.
You can use this option by simply adding the second ConditionalSourceField as parameter.
[ConditionalHide("Condition01", ConditionalSourceField2 = "Condition02")]
Update 02 [13/04/2016]
Based on a suggestion by @VeevTSG via Twitter I have added support for object references to be used as a condition.
You can easily extend the code to add additional condition checks such as integers, floats, strings, etc.
Update 03 [17/12/2016]
Added support for conditional property hiding in nested/encapsulated serializable objects that are used as inspector properties.
Thanks CrashOverride for the great addition in the comments!!
The downloadable file and article above have both been updated with the new addition.
Update 04 [01/08/2018]
It has been a while since I updated the source for this so I have added some functionality that is part of the current iteration that I’m using in my day to day work.
- Added the option to inverse the results of the different ConditionalSourceFields individually as well as inverting the full end result of the conditional check.
- Added the option to use a logic OR check instead of a logic AND check.
- Added the option to define ConditionalSourceField arrays (both for the properties and per property result inverse bool). This will allow you to have as many properties as you want to be considered for the hide.
Also be sure to have a look at tertle’s nice extention of the ConditionalHideAttribute : https://www.brechtos.com/hiding-or-disabling-inspector-properties-using-propertydrawers-within-unity-5/#comment-1359
That’s it
I hope this post was off some use and be sure to let me know if it was :).
You can download the 2 script files directly using the following link: ConditionalHideAttribute.rar
Very useful, have you found a way to make it work alongside [Header] and other custom PD?
It should work just fine with any attribute that doesn’t directly alter the property visualization itself.
So things such as tool tip, space and header will behave as expected.
Unfortunately I haven’t found a way to automatically combine custom PD’s.
This sort of thing would be extremely useful for my project. However, I am writing it entirely in JS and would need to convert these scripts into JS for them to work with my scripts.
Is that even possible? I am having a hard time trying to find the JS equivalent lines online. Would you be able to point me towards the Unity Script Reference if it exists?
Well, I’ve been able to successfully convert the scripts into JS, but the ConditionalHidePropertyDrawer is having a hard time getting the attribute from the ConditionalHideAttribute script, giving me a null reference exception error when I have the object I wish to use this attribute on selected.
Any words of wisdom? I’ve got the script in the Editor folder and even tried directly grabbing the attribute through Resources.Load via its script.
Hi Max.
I personally have no experience with JS inside of unity but I do now that instead of [ConditionalHide] (c#) you need to use @ConditionalHide when using attributes in JS (https://docs.unity3d.com/Manual/Attributes.html)
Technically, as far as I know, you could even keep the custom propertyattribute in c# within the project and still use it inside of your JS scripts with the @ symbol. But I could be wrong on this one.
I hope this was the the missing element to get it to work 🙂
Thanks for your response. While I wasn’t able to get the converted JS scripts to function correctly I did end up realising that I could just use the C# scripts.
I haven’t had much experience with having the two languages work together in Unity so I just assumed it was safer to convert it. I’ve grabbed the attribute with the JS syntax and it all works as it should.
Thanks, Brechtos.
No problem, glad you got it to work 🙂
Hey!
How did you manage to get it to work from a javascript?
The thing works just fine from a CS script with “[ConditionalHide(“test”)]”
But it doesn’t work from unityscript files. I use it like tihs:
@ConditionalHide(“test”)
But I get an Unity error: “No attribute with the name ‘ConditionalHide’ or ‘ConditionalHideAttribute’ was found (attribute names are case insensitive). Did you mean ‘UnityEditor.CanEditMultipleObjects’?”
Hey Beldarak,
As far as I know, if it works in a c# script within your project it should also work for a javascript script.
@ConditionalHide (“PropertyName”)
It seemed to work for Max.
Unfortunatly I have no experience with using Javascript in unity. I’m not sure if the space after the @attribute is important or not but all the unity samples on the site do seem to have that. Let me know if that solves your issue 🙂
Thanks for the quick reply. I just tried that but sadly it doesn’t work.
I opened a question on Reddit/r/Unity3D that explains the issue a little better with the exact code I typed.
https://www.reddit.com/r/Unity3D/comments/56ukl6/use_custom_propertydrawers_from_js_scripts/
I’ll try to remember to post here if anyone comes to a solution 🙂
I found this very useful, thanks.
For my own purposes I have extended it to test against floats/ints/and array size. It defaults to test these as > 0 but I also added TestValue public fields which can be specified to test against instead.
in ConditionalHideAttribute:
public bool TestValueBool = true;
public int TestValueInt = 0;
public float TestValueFloat = 0;
and then in the test in the PropertyDrawer:
if (sourcePropertyValue.isArray)
return sourcePropertyValue.arraySize > condHAtt.TestValueInt;
switch (sourcePropertyValue.propertyType)
{
case SerializedPropertyType.Integer:
return sourcePropertyValue.intValue > condHAtt.TestValueInt;
case SerializedPropertyType.Float:
return sourcePropertyValue.floatValue > condHAtt.TestValueFloat;
case SerializedPropertyType.Boolean:
return sourcePropertyValue.boolValue == condHAtt.TestValueBool;
…
}
I cannot find a way to make it hide arrays properly, OnGUI appears to never receive the actual array property only the individual elements… stupid unity.
Unfortunately there is indeed no clean way to hide arrays. Well… it will hide everything except the index count 🙁 .
So far I haven’t found a way to work around this and I’m not sure if there is. It does hide custom data classes though, so an array wrapped in a custom serializable class will hide as part of the class. But that’s a bit to hacky and overkill for my taste 🙂 .
Nice to see you were able to extend it to fit your needs. I personally also have a variation that allows you to hide based on enums etc. But for those I usually just create a completely new attribute to avoid adding to much functionality to the base ConditionalHideAttribute implementation and making it to overloaded/complex to use.
Good news,
After looking deep into this I managed to tweak something really generic, working for both encapsulated/non encapsulated parameters. The key reside in propertyPath. Here’s how I did:
Thing is serializedObject seem to always refer to the root were the variable exist, therefore being problematic when using subclasses.
Here’s the basic subclass I was trying to use:
Thx for the great addition CrashOverride !
Updated the article and downloadable file with the fix/upgrade.
Hadn’t really needed this myself yet but was probably only a matter of time 🙂
Thank you so much! Absolute lifesaver. Am I free to use this script in an asset?
Ps: any way I could use this on enums?
Nevermind, I adjusted the script 🙂 But I can’t get the ConditionalSourceField2 to work…
I got this code:
[ConditionalHide(“NumberOfRotations”,ConditionalSourceField2 = “RotationType”,true,true)]
RotationType is an enum, that might have something to do with it…
Hi Alexander,
Glad you found use for the attribute 🙂
Yes you can use the script in an asset but it would be nice to keep the originally made by header and perhaps a mention+link in the credits 🙂
As for the enum. Enums are actually just integer values so this will not work out of the box just like that.
I do have an alternate version of this system that works with enums and allows me to hide properties based on the value of an enum.
Below is the code for that. You could easily combine the 2 and create different variations but for our use it works just having both version.
Alright, that script is pretty similar to what I did 🙂
Thanks!
For the credits: your name will be clearly visible and I’ll put a link to your website in the asset description
Have a wonderful day!
Alex
Hey, me again.
How can I make multiple attributes work alongside each other?
for example:
[ConditionalHide(“DrawGizmo”,true)]
[Range(0.0F, 1.0F)]
public float GizmoColorAlpha = 0.25F; //Opacity.
Thanks!
As far as I know this is currently not possible with PropertyDrawers in Unity . I wish it was.
You can however stack decorator attributes as they don’t change the visualization of the data.
Made a custom version so that you can do that:
Nice one. It’s fairly easy to make additions to the drawer 🙂
I wanted to be able to add some more complex logic so I made a quick extension with reflection that let’s you check properties (that unity can’t serialize).
I modified the drawer like so
Then you can make properties with logic in them to conditionally show
This is just a simple enum example, but you can do any combination of logic.
Nice. Thx for sharing 🙂
Modified that slightly, but came to share. Had a case where I was using this on a serialized class in a list of the main class ie:
Sweet. Thx for coming back and giving the update. Will give this one a try when I have some time 🙂
Big thanks to you! Your script make my variable in inspector more clean and beauty! Hope you will share more knowledge about Unity!
Thx and you’re welcome 🙂
I indeed should do more of these but it’s mainly a time management issue. So many things to do! 🙂
Really frustrating that Unity doesn’t allow easier manipulation of array fields 🙁
+1
Very nice. Thanks for sharing.
In case someone is using things and can’t build their project, in C#, you should wrap the PropertyDrawer in a #if UNITY_EDITOR region.
This shouldn’t really be an issue as Unity should should already strip all of this because you are using editor classes 😉
Is it possible to have one true and one false?
also, does order matter?
Thx
Hi Jared,
Sorry for the late response. It seems something went funny with my mail notification settings.
The order doesn’t matter. And yes you can have one true and one false, with some minor code changes. I actually added this in my current version of the script. I shall see if I can find some time to add the updated version later this week.
Great work. Its been very handy for me.
Currently I am trying to hide a list from the inspector but using this method, the list seems to be unaffected. Do you know of a way to hide a list entirely not just is elements?
Unfortunately not 🙁
I have tried to solve this a number of times but so far have had no luck in finding a solution. It annoys me and well and seems to be a limitation of Unity and attribute drawers.
A somewhat nasty workaround is to wrap your list/array in a custom data class as those can be be fully hidden/toggled.
To avoid the maintenance nightmare, use `nameof(MyProperty)` instead of `”MyProperty”`. This way you can rename your properties safely and they update accordingly instead of just not working anymore without telling it.
Correct.
However this will depend on your version of Unity and C# 🙂
Hi Guy and Brechtos I have Update!
1, You can Hide Fields in Subclass,Derivated class
2, now you can enum, object, string… to compare no only bool on source fields
Example 1
Example 2
Modifications:
Thanks for sharing Michal.
I’ve updated your comment so it uses the [codelanguage title=”Code snippet header“] and [/codelanguage] tags to make it easier for people to read the code you posted 😉
Great work! Thank you!
Since it can’t hide the array and lists, is there any replace solution for suggestion?
Unfortunately so far I have had little luck with this. At least to have a solution that is clean and robust.
The only think you could do is wrap your list inside of a helper wrapper class but that always feels so …well …meh for a lack of better words.
If you really want to give it a go you will need to analyze the path of your property, detect that it is pointing to a sub object of an array and then point to the parent array object for hiding as well. That is what I got working but then just reverted that solution as it was causing other issues for me.
The array and list issue is something I have learned to live with until perhaps I can find the time to give it another go.
But besides this, using the attribute saves so much time not having to write custom inspectors for everything 🙂
I’ve been trying to find something exactly like this for a while now and I’m so glad I found this here – I can’t believe it’s not built into Unity! It’s unfortunate this can’t work with lists/arrays but I’ve worked around it.
Also would be awesome if I could add more than one object to check such as:
[ConditionalHide(“variable”, check1, check2, check 3, etc)]
I tried to add an overload method that took another compareObject to ConditionalHideAttribute but I can’t quite figure out how to parse it within the PropertyDrawer script.
Hey Skullflower,
If you check the latest update of the attribute you can actually provide as many variables as you want using the arrays 😉
For each entry you can also change if it should be handled as an inverse of the value or not.
Additionally you can specify if you wish to use AND or OR logic as well.
Hope this helps.
I was wondering if there is a way to also hide other attributes above the variable to conditionally hide. For instance I have 6 variables in a row with a space attribute between each two. I want to conditionally hide all these 6 variables, but when I do I also want to hide the space attributes, because they leave a gap in my inspector.
It has to be doable because unitys own [HideInInspector] attribute can do it too? I just cant seem to find out what that exactly does. The hide in inspector attribute also works for Arrays and List, so there has to be a way right?
Any suggestions on how to handle this?
Hey Tom,
At the moment I sadly have no solution for this.
Thanks. First impression is very good.
Just a quick question. I downloaded your files and the property drawer is working, except not on string type variables. I’m not using any complex attributes like the TextAreaAttribute. Seems to work fine with other simple types. Is this correct or have I missed something?
That seems odd. They seem to work just fine on my end on all kinds of types including strings. (tested in 2017,2018 and 2019)
Thanks a bunch for this tutorial. I’ve always wanted to conditionally hide fields in Unity but never had the patience to properly learn how to do it. I’ve looked up a tutorial in the past but I couldn’t make it work (probably missed something). But your guide worked like a charm! I love that you teach how to create an attribute to do this work instead of having a property drawer for each class in which you want to conditionally hide stuff, like I’ve seen in a project I worked on in the past. The attribute makes for an universal solution! Thanks again! Have a nice weekend!
Glad you like it:) That was exactly my problem as well.
I wanted a more universal, quick to use solution that doesn’t take away time from being able to focus on the important stuff.
Thank you so much for sharing this.
Literally spent a few days trying to find something like this, so thank you for sharing!
When I went through the downloadable scripts and finally understood what they were doing, I did a little refactoring and wound up with something shorter. Hopefully it’ll make the newer code a little easier to understand, so I’ll drop that here. I don’t know how to use code fences here, so my apologies if this looks weird.
Thanks again!
Conditional Drawer Script
“`
using UnityEditor;
using UnityEngine;
// DOES NOT WORK WITH TextAreaAttribute. That property has it’s own show/hide functionality.
[CustomPropertyDrawer(typeof(ConditionalAttribute))]
public class ConditionalDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
ConditionalAttribute conditional = (ConditionalAttribute)attribute;
bool enabled = GetConditionalAttributeResult(conditional, property);
bool wasEnabled = GUI.enabled;
GUI.enabled = enabled;
if (enabled) { EditorGUI.PropertyField(position, property, label, true); }
GUI.enabled = wasEnabled;
}
// Calculates the property hieght so there is no visuaul overlap of properties in the inspector.
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
ConditionalAttribute conditional = (ConditionalAttribute)attribute;
bool enabled = GetConditionalAttributeResult(conditional, property);
if (enabled)
return EditorGUI.GetPropertyHeight(property, label);
else
return -EditorGUIUtility.standardVerticalSpacing; // Property is not being drawn. Undo spacing.
}
private bool GetConditionalAttributeResult(ConditionalAttribute _conditional, SerializedProperty _property)
{
bool enabled = (_conditional.useOrLogic) ? false : true;
// If the conditon is a string.
if (_conditional.conditionalSourceField != null)
{
SerializedProperty _sourcePropertyValue = null;
// Handle single poroperty.
string _propertyPath = _property.propertyPath;
string _conditionPath = _propertyPath.Replace(_property.name, _conditional.conditionalSourceField);
_sourcePropertyValue = _property.serializedObject.FindProperty(_conditionPath);
// If the find failed, hard coded to look for field below.
if (_property == null)
_sourcePropertyValue = _property.serializedObject.FindProperty(_conditional.conditionalSourceField);
if (_sourcePropertyValue != null)
{
enabled = CheckPropertyType(_sourcePropertyValue);
if (_conditional.inverse) enabled = !enabled;
}
else
Debug.LogWarning(“Attempting to use a ConditionalAttribute, but no matching bool variable found in object: ” + _conditional.conditionalSourceField);
}
// if the condition is an array of strings.
else if (_conditional.conditionalSourceFieldArray != null)
{
string[] _conditionalSourceFieldArray = _conditional.conditionalSourceFieldArray;
for (int i = 0; i < _conditionalSourceFieldArray.Length; i++)
{
SerializedProperty _sourcePropertyValueFromArray = null;
if (!_property.isArray)
{
string _propertyPath = _property.propertyPath;
string _conditionPath = _propertyPath.Replace(_property.name, _conditionalSourceFieldArray[i]);
_sourcePropertyValueFromArray = _property.serializedObject.FindProperty(_conditionPath);
// If the find failed, use hard code to get field.
if (_sourcePropertyValueFromArray == null)
_sourcePropertyValueFromArray = _property.serializedObject.FindProperty(_conditionalSourceFieldArray[i]);
}
// Combine results
if (_sourcePropertyValueFromArray != null)
{
bool propertyEnabled = CheckPropertyType(_sourcePropertyValueFromArray);
Debug.Log(_conditionalSourceFieldArray[i]);
if (_conditional.inverse) propertyEnabled = !propertyEnabled;
if (_conditional.useOrLogic) enabled = enabled || propertyEnabled;
else enabled = enabled && propertyEnabled;
}
else
Debug.LogWarning("Attempting to use a conditional attribute, but no matching bool variable found in object: " + _conditionalSourceFieldArray[i] + ".");
}
}
return enabled;
}
private bool CheckPropertyType(SerializedProperty _propertyValue)
{
// Add other data types to handel them here.
SerializedPropertyType _propType = _propertyValue.propertyType;
if (_propType == SerializedPropertyType.Boolean)
return _propertyValue.boolValue;
else if (_propType == SerializedPropertyType.ObjectReference)
return _propertyValue.objectReferenceValue != null;
else
{
Debug.LogError("Data type of the property used for conditional hiding [" + _propertyValue.propertyType + "] is currently not supported");
return true;
}
}
}
“`
Conditional Attributes Script
“`
using UnityEngine;
using System;
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct, Inherited = true)]
public class ConditionalAttribute : PropertyAttribute
{
public string conditionalSourceField = null;
public string[] conditionalSourceFieldArray = null;
public GameObject conditionalGameObject = null;
public GameObject[] conditionalGameObjectArray = null;
public bool inverse = false;
public bool useOrLogic = false;
public ConditionalAttribute(string _conditionalSourceField)
{
this.conditionalSourceField = _conditionalSourceField;
}
public ConditionalAttribute(string _conditionalSourceField, bool _inverse)
{
this.conditionalSourceField = _conditionalSourceField;
this.inverse = _inverse;
}
public ConditionalAttribute(string _conditionalSourceField, bool _useOrLogic, bool _inverse)
{
this.conditionalSourceField = _conditionalSourceField;
this.useOrLogic = _useOrLogic;
this.inverse = _inverse;
}
public ConditionalAttribute(string[] _conditionalSourceFieldArray)
{
this.conditionalSourceFieldArray = new string[] { };
this.conditionalSourceFieldArray = _conditionalSourceFieldArray;
}
public ConditionalAttribute(string[] _conditionalSourceFieldArray, bool _useOrLogic)
{
this.conditionalSourceFieldArray = new string[] { };
this.conditionalSourceFieldArray = _conditionalSourceFieldArray;
this.useOrLogic = _useOrLogic;
}
public ConditionalAttribute(string[] _conditionalSourceFieldArray, bool _useOrLogic, bool _inverse)
{
this.conditionalSourceFieldArray = new string[] { };
this.conditionalSourceFieldArray = _conditionalSourceFieldArray;
this.useOrLogic = _useOrLogic;
this.inverse = _inverse;
}
public ConditionalAttribute(GameObject _conditionalGameObject)
{
this.conditionalGameObject = _conditionalGameObject;
}
public ConditionalAttribute(GameObject _conditionalGameObject, bool _useOrLogic)
{
this.conditionalGameObject = _conditionalGameObject;
this.useOrLogic = _useOrLogic;
}
public ConditionalAttribute(GameObject _conditionalGameObject, bool _useOrLogic, bool _inverse)
{
this.conditionalGameObject = _conditionalGameObject;
this.useOrLogic = _useOrLogic;
this.inverse = _inverse;
}
}
“`
Brilliant!
How would I go on to compare 2 strings and enable the property if they match?
such as:
public string1 = “hi”;
public string2 = “hi”;
[Conditional(“string1”, “string2”)]
public bool enabled;
Appreciate the clear code.
Hey Kamit,
Just as with the check where you check the specific param to see it’s enabled or false. You can just compare the 2 provided parameters instead with whatever logic you want. (This is done in the drawer)
already copy both file from here https://gist.github.com/sebtoun/65cb455eddd2fea00a11614483c84d50#file-conditionalhideattribute-cs in my script dir but didnt work :(, i use unity 2020.2 on ubuntu
pls help what did i miss?
Hi chasni,
Make sure that the scripts are placed in an editor folder.
Otherwise they will not work.
Hello,
This is a great feature I love it, I use especially the enum version!
However I was wondering if it was possible to use the property drawer to conditionally serialize private fields? It bothers me a little to make my variables public because no other class should be able to access them. However when I make them private the serialization becomes impossible, do you have a suggestion?
Hey Dylan,
You can tag your fields as a [SerializeField] (https://docs.unity3d.com/ScriptReference/SerializeField.html)
This will expose your private field to the editor and the attribute should still work just fine 😉
Hi, a few years down the line – is there now a way for it to play nicely with combining headers or if it’s an array? I’ve had to encapsulate my array in a wrapper class to get array hiding to work.
Sadly no. This has to do with how Unity draws and handles their arrays in the editor. It’s an annoying limitation, I must agree.
Thank you for sharing, this is very helpful.
By the way, have you got any progress with array properties?
Sadly not. Unless Unity changes the way they handle arrays and their editor drawing in general to have more control.
I have the two files inside a Editor folder, but if i try to use ConditionalHide unity complains with:
The type or namespace name ‘ConditionalHideAttribute’ could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name ‘ConditionalHide’ could not be found (are you missing a using directive or an assembly reference?)
I am using Unity 2021.2.5f1
Hey AlbatroZz,
Makse sure that ConditionalHideAttribute is not inside of an Editor folder and that ConditionalHidePropertyDrawer is inside one. Then it should work just fine 😉
Wow thanks this is fantastic good job really works well for me. Cant thank you enough.
Hello !
There must be a way for array/list, but it’s not a simple solution and it implies a lot of new classes.
Look at that project:
https://github.com/garettbass/UnityExtensions.ArrayDrawer
They implements custom ArrayDrawer similary to base ListDrawer of Unity.
I think if you are overwrite that drawer and if you retrieve the CondionalAttribute from the field and resolve the logic of the attribute in the GetPropertyHeight of this ArrayDrawer, you could make it invisible.
I did not try yet, but if someone is motivated… 😀
If someone wants to spend the time and headaches I would love to see the final version 🙂
Sadly I’ll pass on this one as well :p
Hello!
I’ve been using your extension for quite a long time and it has been super useful! Thank you again for it 🙂 I do have an issue with the latest version of unity 2021.3.10f1 (which was working fine with 2021.3.5f1).
Here is the issue : https://pasteboard.co/GwI3RUUD78Xq.png
Basically, the values in the array are hidden, but not the array variable itself.
Any idea why?
Hey Laurel,
I’m currently not on the latest Unity version yet (our projects are still on 2019 LTS) so I haven’t been able to check this yet.
If I’m not mistaken Unity changed the way they draw their lists and that is most likely messing with this.
Are you trying to hide the entire list or is this a list with entries that use the attribute?
I do know that the attribute never worked with showing/hiding full lists though. Again due to the way Unity handles this.
Yeah, they changed it, it was bugged for specific versions of 2021 but it was fixed again, so I was able to actually use your extension for a long time 🙂 Like I said hiding the full list (like in my example below) was working with 2021.3.5. I was wondering if you had an idea why 🙂
I am trying to hide the full list.
For example :
[ConditionalHide(“isVisible”, true)]
[SerializeField]
public List myVisibleStrings = new List();
Now in the editor I still see the List myVisibleStrings, but I don’t see the entries (like on the picture).