- Home /
How to make a readonly property in inspector?
I tried this approach:
private var _worldVelocity : Vector3;
public function get worldVelocity () : Vector3 { return _worldVelocity; }
private function set worldVelocity (value : Vector3) { _worldVelocity = value;
but it hasn't worked at all in the inspector.
Answer by It3ration · Oct 01, 2014 at 05:37 PM
This is the end-all answer - it will allow you to mark -any- field as readonly with a single property attribute / property drawer. It's partially based on scottmontgomerie's answer. This version also dispatches the property height based on how "expanded" the property is.
public class ReadOnlyAttribute : PropertyAttribute
{
}
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
public class ReadOnlyDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property,
GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label, true);
}
public override void OnGUI(Rect position,
SerializedProperty property,
GUIContent label)
{
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
}
public class Test
{
[ReadOnly] public string a;
[ReadOnly] public int b;
[ReadOnly] public Material c;
[ReadOnly] public List<int> d = new List<int>();
}
Works like a charm.
Just tried this code - works great:
And you dont need to override GetPropertyHeight - just remove it and code will be simpler.
Why setting GUI.enabled = true;
after setting it to false?
I've worked this before and if you exit the method with GUI.enabled set to false the entire element will be ignored, while if GUI.enabled is false while populating children they will appear disabled.
Did anyone manage to get this to work in Unity 4.6 and 5?
@Ben BearFish put ReadonlyAttribute in its own script in a folder outside of the Editor folder, make sure ReadOnlyDrawer is in the Editor folder.
For anyone doing this on a private/protected field, you need to also serialize the field:
[ReadOnlyAttribute, SerializeField] private float health;
Answer by Lev-Lukomskyi · Sep 19, 2014 at 07:30 PM
Use this:
Assets/Scripts/ShowOnlyAttribute.cs:
using UnityEngine;
public class ShowOnlyAttribute : PropertyAttribute
{
}
Assets/Editor/ShowOnlyDrawer:
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(ShowOnlyAttribute))]
public class ShowOnlyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label)
{
string valueStr;
switch (prop.propertyType)
{
case SerializedPropertyType.Integer:
valueStr = prop.intValue.ToString();
break;
case SerializedPropertyType.Boolean:
valueStr = prop.boolValue.ToString();
break;
case SerializedPropertyType.Float:
valueStr = prop.floatValue.ToString("0.00000");
break;
case SerializedPropertyType.String:
valueStr = prop.stringValue;
break;
default:
valueStr = "(not supported)";
break;
}
EditorGUI.LabelField(position,label.text, valueStr);
}
}
Use in your object:
public class MyClass : MonoBehaviour
{
[ShowOnly] public float aaa = 123.45678f;
[ShowOnly] public int bbb = 234;
[ShowOnly] public bool ccc = false;
[ShowOnly] [SerializeField] bool ddd = true;
}
You will get this:
This worked for me, thanks!
It only works for ints, bools, floats, and strings tho. If anyone needs this code for enumerators, insert these lines into "ShowOnlyDrawer.cs", just before "default:":
case SerializedPropertyType.Enum:
valueStr = prop.enumNames[prop.enumValueIndex];
break;
This worked for me but I wanted to use this to also have GameObjects [ShowOnly].
So after several hours of trying different things and reading documentation, this is what I came up with:
case SerializedPropertyType.ObjectReference:
try {
valueStr = prop.objectReferenceValue.ToString ();
} catch (NullReferenceException) {
valueStr = "None (Game Object)";
}
break;
Hope this helps someone.
This worked for me, thanks! Add for Enum
case SerializedPropertyType.Enum:
valueStr = prop.enumDisplayNames[prop.enumValueIndex];
break;
Works very nicely for me as well in Unity 2019.4.13f1, thanks for this!
Note that many other value types are possible in SerializedProperty
(the prop
param in ShowOnlyDrawer#OnGUI
).
This did help - thank you! I also had to add using: System;
to my class to use the 'NullReferenceException', so VisualStudio didn't yell at me. :)
But this is a nice and simple solution that gives you a lot of flexibility over display per type. Thanks!
Answer by FuzzyLogic · Mar 07, 2018 at 11:10 AM
The solution posted by @It3ration does not work with fields that already have a CustomPropertyDrawer.
.
Using the ReadOnly attribute will cause the field to be displayed with the default property drawer instead of the custom property drawer.
.
This solution expands on the solution by @It3ration to add 2 new attributes ( BeginReadOnlyAttribute
and EndReadOnlyAttribute
) so that any fields in between the two attribute tags will be read-only. Using this method will allow CustomPropertyDrawers to work. The important part is that the new attributes use DecoratorDrawers instead of a PropertyDrawer.
.
For convenience, the ReadOnlyAttribute is also implemented but will only work for a single field with a default property drawer.
.
ReadOnlyAttribute.cs (MUST NOT be in Editor folder)
Using UnityEngine;
/// <summary>
/// Display a field as read-only in the inspector.
/// CustomPropertyDrawers will not work when this attribute is used.
/// </summary>
/// <seealso cref="BeginReadOnlyGroupAttribute"/>
/// <seealso cref="EndReadOnlyGroupAttribute"/>
public class ReadOnlyAttribute : PropertyAttribute { }
/// <summary>
/// Display one or more fields as read-only in the inspector.
/// Use <see cref="EndReadOnlyGroupAttribute"/> to close the group.
/// Works with CustomPropertyDrawers.
/// </summary>
/// <seealso cref="EndReadOnlyGroupAttribute"/>
/// <seealso cref="ReadOnlyAttribute"/>
public class BeginReadOnlyGroupAttribute : PropertyAttribute { }
/// <summary>
/// Use with <see cref="BeginReadOnlyGroupAttribute"/>.
/// Close the read-only group and resume editable fields.
/// </summary>
/// <seealso cref="BeginReadOnlyGroupAttribute"/>
/// <seealso cref="ReadOnlyAttribute"/>
public class EndReadOnlyGroupAttribute : PropertyAttribute { }
ReadOnlyDrawer.cs (MUST be in Editor folder)
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer( typeof( ReadOnlyAttribute ) )]
public class ReadOnlyDrawer : PropertyDrawer {
public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) {
return EditorGUI.GetPropertyHeight( property, label, true );
}
public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
using (var scope = new EditorGUI.DisabledGroupScope(true)) {
EditorGUI.PropertyField( position, property, label, true );
}
}
}
[CustomPropertyDrawer( typeof( BeginReadOnlyGroupAttribute ) )]
public class BeginReadOnlyGroupDrawer : DecoratorDrawer {
public override float GetHeight() { return 0; }
public override void OnGUI( Rect position ) {
EditorGUI.BeginDisabledGroup( true );
}
}
[CustomPropertyDrawer( typeof( EndReadOnlyGroupAttribute ) )]
public class EndReadOnlyGroupDrawer : DecoratorDrawer {
public override float GetHeight() { return 0; }
public override void OnGUI( Rect position ) {
EditorGUI.EndDisabledGroup();
}
}
Example.cs
using UnityEngine;
public class ReadOnlyExample : MonoBehaviour {
[BeginReadOnlyGroup] // tag a group of fields as ReadOnly
public string a;
public int b;
public Material c;
public List<int> d = new List<int>();
public CustomTypeWithPropertyDrawer e; // Works!
[EndReadOnlyGroup]
[ReadOnly] public string a2;
[ReadOnly] public CustomTypeWithPropertyDrawer e2; // DOES NOT USE CustomPropertyDrawer!
[BeginReadOnlyGroup]
public int b2;
public Material c2;
public List<int> d2 = new List<int>();
// Attribute tags apply to the next field of which there are no more so Unity/C# complains.
// Since there are no more fields, we can omit the closing tag.
// [EndReadOnlyGroup]
}
I just tested your solution: It does not work unfortunately.
I cannot add screenshots (since reputation system I guess) but:
ReadOnlyAttribute.cs is NOT in Editor
ReadOnlyDrawer IS in Editor
$$anonymous$$y Example.cs
using System;
using System.Collections.Generic;
using UnityEngine;
public class Examples : $$anonymous$$onoBehaviour
{
[ReadOnly]
[SerializeField]
private Vector3 vector3;
[ReadOnly, SerializeField]
private List<SerializableClass> serializableClassListReadOnly = new List<SerializableClass>()
{
new SerializableClass("Test1", 1),
new SerializableClass("Test2", 2),
new SerializableClass("Test3", 3),
new SerializableClass("Test4", 4)
};
[ReadOnly, SerializeField]
private List<int> intListReadOnly = new List<int>() { 1, 2, 3, 4 };
[BeginReadOnlyGroup]
[SerializeField]
private List<SerializableClass> serializableClassListReadOnlyGroup = new List<SerializableClass>() {
new SerializableClass("Test1", 1),
new SerializableClass("Test2", 2),
new SerializableClass("Test3", 3),
new SerializableClass("Test4", 4)
};
[SerializeField] private List<int> intListReadOnlyGroup = new List<int>() { 1, 2, 3, 4 };
// Since there are no more fields, we can omit the closing tag.
[EndReadOnlyGroup]
[ReadOnly]
[SerializeField]
private Quaternion quaternion;
[Serializable]
public class SerializableClass
{
public string Name;
public int Value;
public SerializableClass(string name, int value)
{
Name = name;
Value = value;
}
}
}
In the Inspector all Variables with [ReadOnly]
just do the same as It3ration's solution. On List still the name and size
are not disabled.
On the lists within the [BeginReadOnlyGroup]
and [EndReadOnlyGroup]
on the name/foldout of the first list is disabled, all the other values are not.
(I just placed another value below the group to see if it makes a differents if the ending attribute is not omitted.)
This solution only addresses creating readonly fields where the field is a type that already has its own CustomPropertyDrawer.
Unfortunately, I don't think there is any simple way to create readonly lists. The ReadOnlyGroup attribute will behave the same as the ReadOnly attribute in this case.
Unity only applies the attribute to the first element of a list. I think you would need to create a property drawer for the elements themselves that specifically handles disabling those fields.
In the case of the List<SeriablizableClass>
, I think you can apply the ReadOnly (or ReadOnlyGroup) attribute to the name
and value
fields within your SerializableClass.
For the List<int>
, it's much trickier. You would have to create your own int type and then create a CustomPropertyDrawer for that type.
The other option is to create a custom list type, which I don't think you want to do either. It gets to the point where you might as well just make a custom inspector.
Should be accepted answer, Works Great, thanks!
Useful in conjunction with [SerializeField] to expose private/protected values.
Duplicating my comment on the other answer, but new EditorGUI.DisabledScope(true) is now recommended over DisabledGroupScope as it's a struct instead of a class and offers a slight advantage on memory usage. Otherwise, works fine! (I only tested the first version for members without custom property drawers)
Answer by Coderdood · Jul 09, 2013 at 09:59 PM
Simple way is to use the Inspector in Debug Mode. Instructions / details are located in the documentation.
Otherwise you may look at creating a custom editor. Documentation here.
Answer by scottmontgomerie · Jul 24, 2014 at 08:43 PM
From http://buildoutput.blogspot.com/2014/01/the-visual-connect-that-unitys.html
It's not a great solution because of all the #ifdef UNITY_EDITOR
lines, but it works and I'm sure with some refinement it could be useful.
ReadOnlyPropertyAttribute.cs:
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public abstract class ReadOnlyPropertyAttribute : PropertyAttribute
{
public string Displayname { get; private set; }
public ReadOnlyPropertyAttribute( string displayName )
{
this.Displayname = displayName;
}
}
public class ReadOnlyObjectAttribute : ReadOnlyPropertyAttribute
{
public ReadOnlyObjectAttribute( string displayName ) : base( displayName )
{
}
}
public class ReadOnlyVector3Attribute : ReadOnlyPropertyAttribute
{
public ReadOnlyVector3Attribute( string displayName ) : base( displayName )
{
}
}
PropertyDrawer.cs:
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
#if UNITY_EDITOR
[ CustomPropertyDrawer( typeof( ReadOnlyObjectAttribute ) ) ]
public class ReadOnlyObjectDrawer : PropertyDrawer
{
public override void OnGUI( Rect position, SerializedProperty prop, GUIContent label )
{
EditorGUI.ObjectField( position, ( attribute as ReadOnlyPropertyAttribute ).Displayname + " (Read Only)", prop.objectReferenceValue, typeof( System.Object ), true );
}
}
[ CustomPropertyDrawer( typeof( ReadOnlyVector3Attribute ) ) ]
public class ReadOnlyVector3Drawer : PropertyDrawer
{
public override void OnGUI( Rect position, SerializedProperty prop, GUIContent label )
{
GUI.enabled = false;
EditorGUI.Vector3Field( position, ( attribute as ReadOnlyPropertyAttribute ).Displayname + " (Read Only)", prop.vector3Value );
GUI.enabled = true;
}
public override float GetPropertyHeight( SerializedProperty property, GUIContent label )
{
return base.GetPropertyHeight( property, label ) * 2.0f;
}
}
#endif
Then you can do this in your classes:
#if UNITY_EDITOR
[ SerializeField ][ ReadOnlyObjectAttribute( "DoF Material" ) ]
#endif
private Material m_material;
Your answer
Follow this Question
Related Questions
Unity editor not saving object changes 4 Answers
How to make public variable read-only during run-time? 3 Answers
How to display computed getter in inspector? 2 Answers
Inspector->Texture: Can't effectively change properties (tiling/offset) 0 Answers
EditorGUILayout.PropertyField cannot draw custom class? 2 Answers