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.

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.

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:

32648-showonlyscreenshot.png

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]

}

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.

If you just want to see the value of a variable without being able to edit, it’s good enough to just leave it private and then put the Inspector in Debug Mode

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;

The solution provided by @Lev-Lukomskyi worked for me!!
I was missing some things though so I added them, for anyone wondering how to do these here they are.

GameObjects:

case SerializedPropertyType.ObjectReference:
                try {
                    valueStr = prop.objectReferenceValue.ToString ();
                } catch (NullReferenceException) {
                    valueStr = "None (Game Object)";
                }
                break;

Vector 2 and 3:

case SerializedPropertyType.Vector2:
                valueStr = prop.vector2Value.ToString ();
                break;
            case SerializedPropertyType.Vector3:
                valueStr = prop.vector3Value.ToString ();
                break;

NotEditable In Inspector

Here is an option in case you want to be able to edit values in the inspector but then have the resulting class/struct be readonly later. Here’s an example of a Translation, Rotation, Scale struct. I’m using the Unity.Mathematics package btw. The idea here is that you create a readonly class/struct and then have a public nested class/struct marked serializable which has an implicit operator to return the readonly version you want in the end.

public readonly struct TRS
{
    public float3 Translation { get; }
    public quaternion Rotation { get; }
    public float3 Scale { get; }
    public float4x4 Matrix => float4x4.TRS(Translation, Rotation, Scale);

    public TRS(float3 translation, quaternion rotation, float3 scale)
    {
        Translation = translation;
        Rotation = rotation;
        Scale = scale;
    }

    public TRS(float3 translation, float3 rotationDegrees, float3 scale)
    {
        Translation = translation;
        Rotation = quaternion.Euler(rotationDegrees);
        Scale = scale;
    }

    [System.Serializable]
    public struct InspectorVersion
    {
        public float3 translation;
        public float3 rotationDegrees;
        public float3 scale;

        public static implicit operator TRS(InspectorVersion i)
        {
            return new TRS(i.translation, i.rotationDegrees, i.scale);
        }
    }
}

public class ExampleBehaviour : MonoBehaviour
        {
            public TRS.InspectorVersion trs;

            public void Start()
            {
                TRS t = trs;
            }
        }