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.

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;

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

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;
            }
        }