0

The signature of the method looks like:

public static Object ObjectField(String label, Object obj, Type objType, Boolean allowSceneObjects, params GUILayoutOption[] options)

Make a field to receive any object type.

Params:

  • label – Optional label in front of the field.
  • obj – The object the field shows.
  • objType – The type of the objects that can be assigned.
  • allowSceneObjects – Allow assigning Scene objects. See Description for more info.
  • options – An optional list of layout options that specify extra layout properties. Any values passed in here will override settings defined by the style. See Also: GUILayout.Width, GUILayout.Height, GUILayout.MinWidth, GUILayout.MaxWidth, GUILayout.MinHeight, GUILayout.MaxHeight, GUILayout.ExpandWidth, GUILayout.ExpandHeight.

But when you use this method, you need to assign the returned value to some object so you can use it later. You also pass the same object as the second parameter. Thus, you have the reference to the variable/field in two places:

_oldFont = (Font)EditorGUILayout.ObjectField("Old font", _oldFont, typeof(Font), false);

But why I even need to use the returned value? Why the reference that I provided as the second parameter can't be used to modify the object? I just can't get the logic.

derHugo
  • 83,094
  • 9
  • 75
  • 115
FogyX
  • 15
  • 4
  • Does this answer your question? [When to pass ref keyword in](https://stackoverflow.com/questions/2193064/when-to-pass-ref-keyword-in) – shingo Jun 06 '23 at 14:55
  • Further explanation of the duplicate vote: Without the `ref` keyword, a method cannot change the reference in it, that's why you need the return value. Unity also cannot just use the `ref` keyword because it requires a certain type, but this method should be used by variant types. Anyway the question is basically about how ref works. – shingo Jun 06 '23 at 14:57
  • @shingo but ref is used only for value types – FogyX Jun 06 '23 at 17:55
  • No, it can be used for any types. – shingo Jun 07 '23 at 04:06
  • @shingo this doesn't really help OP at all when dealing with [built-in code](https://docs.unity3d.com/ScriptReference/EditorGUILayout.ObjectField.html) they can not modify the parameters of ;) – derHugo Jun 07 '23 at 06:45

2 Answers2

0

This is because the reference can be changed via the UI. If you drag a different object into the field, the one that was just now dragged in is returned.

EditorGUILayout.ObjectField("Old font", _oldFont, typeof(Font), false);

// User drags in a different font reference...

// _oldFont references what was there before and not the one dragged in by the user

You are telling Unity what object to display.

If you want to display an object whose reference you already have (outside of dragging it in), then you wouldn't need the return value, because you essentially created a "for display only" object field.

hijinxbassist
  • 3,667
  • 1
  • 18
  • 23
  • So this parameter is a reference to the last object that was dragged in the field? Is there any cases where I can use different reference than the one that I want to get? Like this: var newReference = EditorGUILayout.ObjectField("Old font", _oldFont, typeof(Font), false); – FogyX Jun 06 '23 at 18:04
  • Yeah that is something you can do. However know that if you do not assign "newReference" back to "_oldFont", then the object "_oldFont" will still show and will appear as though the drag and drop did not work. – hijinxbassist Jun 06 '23 at 19:15
0

But why I even need to use the returned value? Why the reference that I provided as the second parameter can't be used to modify the object?

Well because this is how it is implemented an how it works ;)

It returns the currently assigned UnityEngine.Object. If you drag in another one to the according field it will return a different reference afterwards.

Yes, this could have been done using the ref keyword BUT it is not always desired in all the possible use cases that this call directly changes the reference! You could totally have use cases where you assign the result to another variable than the one you are passing in - maybe do some manual change checks (yes there is ChangeCheckScope as well but still) or some validation first.

I think they intentionally did not use ref for that reason to not force everyone to have the passed in variable/field immediately overwritten.

If you want this behavior you can easily implement a custom wrapper around it like

public static class CustomEditorGUILayout
{
    public static void ObjectField<T>(string label, ref T obj, bool allowSceneReferences) where T : UnityEngine.Object
    {
        obj = (T)EditorGUILayout.ObjectField(label, obj, typeof(T), allowSceneReferences);
    }
}

then you can do

CustomEditorGUILayout.ObjectField("Old Font", ref _oldFont, false);

This (and the other directly typed) field is mainly used if you have a specific usecase where you do not directly want to assign the result to a property.

For instance when dealing with an EditorWindow it is sometimes just easier to use those direct fields without the need for any Undo/Redo and serialization management

private Font _oldFont;

...

_oldFont = (Font)EditorGUILayout.ObjectField("Old font", _oldFont, typeof(Font), false);

but it can also used for e.g. validating the input before actually applying it or in other special use cases where you do not want to serialize an object reference (e.g. for having serialized Scene fields were you actually in the background only serialize the path)


Especially when using the upper with serialized fields you have to either make sure to properly handle dirty marking and Undo/Redo yourself or finally apply the value via SerializedProperty and do

var currentFont = youSerializedProperty.objectReferenceValue;
var newFont = (Font)EditorGUILayout.ObjectField("Old font", currentFont, typeof(Font), false);
youSerializedProperty.objectReferenceValue = newFont;

which is of course quite some overhead (see below).


In most use cases you wouldn't use this at all but rather go through e.g.

[SerializeField] private Font _oldFont;

and then in the editor code do

var fontProperty = serializedObject.FindProperty("_oldFont");
EditorGUILayout.PropertyField(_oldFont);

which automatically uses the best matching drawer for the given property and handles all marking dirty and Undo/Redo for you

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • so i can assign the returned value to th temporary variable, validate it and then assign its value to the field that i want to change? – FogyX Jun 18 '23 at 06:53
  • @FogyX for example yes .. or maybe you want even do more things to it and need the reference anyway .. or maybe you serialize the field completely different (for example for having a serialized Scene field where you actually only store the path) – derHugo Jun 20 '23 at 06:43