2

I dug around for similar questions, but none seem to have been asked, so for the sake of potential Google users with the same issue (especially people newer to Unity/Editor scripting/C#), I believe this question ought to exist.

I was writing a custom property drawer for a class, but for some reason none of the distinct fields were clickable. I was using separate PropertyFields for float variables, but decided I'd try and use a MultiFloatField to see if it was going to help. Here's what I did:

var fwd = property.FindPropertyRelative("Forwards").floatValue;
var bwd = property.FindPropertyRelative("Backwards").floatValue;
var left = property.FindPropertyRelative("StrafeLeft").floatValue;
var right = property.FindPropertyRelative("StrafeRight").floatValue;
EditorGUI.MultiFloatField(position, new [] {"F", "B", "L", "R"}.Select(x => new GUIContent(x)).ToArray(), new [] {fwd, bwd, left, right});

Can't live without LINQ. However, when attempting to change the values, they'd immediately reset themselves to whatever their initial value used to be. Given that the MultiFloatField method returns nothing, how exactly do you extract the edited values out of it?

Lumos
  • 358
  • 3
  • 10

1 Answers1

3

The answer is deceptively simple: Arrays in C# are reference types, which means that editing something in an array preserves the edited value. To illustrate how one would use a MultiFloatField properly, here's an example from the Unity C# reference:

    // Make an X, Y & Z field for entering a [[Vector3]].
    private static Vector3 Vector3Field(Rect position, Vector3 value)
    {
        s_Vector3Floats[0] = value.x;
        s_Vector3Floats[1] = value.y;
        s_Vector3Floats[2] = value.z;
        position.height = kSingleLineHeight;
        BeginChangeCheck();
        MultiFloatField(position, s_XYZLabels, s_Vector3Floats);
        if (EndChangeCheck())
        {
            value.x = s_Vector3Floats[0];
            value.y = s_Vector3Floats[1];
            value.z = s_Vector3Floats[2];
        }
        return value;
    }

As we can see, there's an array (called s_Vector3Floats) which is passed into the MultiFloatField; the changed values are read directly from the same array.

Ergo, in the example provided above, the issue lies in the fact that we create a new array every single time. A better (and working!) way to do it would be the following:

    var fwd = property.FindPropertyRelative("Forwards");
    var bwd = property.FindPropertyRelative("Backwards");
    var left = property.FindPropertyRelative("StrafeLeft");
    var right = property.FindPropertyRelative("StrafeRight");
    var array = new[] {fwd.floatValue, bwd.floatValue, left.floatValue, right.floatValue};
    EditorGUI.MultiFloatField(position, new [] {"F", "B", "L", "R"}.Select(x => new GUIContent(x)).ToArray(), array);
    fwd.floatValue = array[0];
    bwd.floatValue = array[1];
    left.floatValue = array[2];
    right.floatValue = array[3];

And an added bonus: If you're using a Foldout and you can't click on your contents, that's probably because you're giving the Foldout a Rect that's far too large. Be careful with your rects. They, unlike arrays, are value types.

Lumos
  • 358
  • 3
  • 10