Follow

(Adapted from Bond, p. 483.)

Suppose you have a Unity GameObject obj and want to set its x position to 5.

You'd like to say:

obj.transform.position.x = 5;

but this doesn't work!

The reason is that position is a property, so obj.transform.position returns a new Vector3, not the one stored in the transform.

You therefore have to do this:

pos = obj.transform.position;
pos.x = 5;
obj.transform.position = pos;

This bothers me because you can't tell by looking at something whether it's a field or a property. .position is accessing a property, but .x is accessing a field.

@peterdrake That's not quite accurate. It's because position is a property *and* a struct. This is actually a C# thing moreso than a Unity thing. You can change members of class properties or struct variables just fine. Unity made the choice to have core math structures like Vectors and Quaternions be structs, presumably to impart a degree of immutability.

@LouisIngenthron I think that makes sense.

If I understand correctly,

obj.transform.position.x = 5;

is effectively expanded into:

Transform t = obj.transform;
Vector3 p = t.position;
p.x = 5;

The first assignment (=) makes an alias, because a Transform is an object.

The second assignment makes a copy, because a Vector3 is a struct. This is the hidden culprit.

The third assignment technically "copies" the 5, but since it's an immutable primitive value, that doesn't matter.

If this is accurate, would the same thing happen if position was a field rather than a property?

(It may also be that the "getter" for position creates a new Vector3 for more complicated reasons involving global vs local positions, which seems like it would create this problem even if a Vector3 was an object rather than a struct. If that's true, my original complaint about properties holds.)

I'm guessing Unity made Vector3 and Quaternion structs for efficiency reasons (to avoid a level of indirection), but I don't know. It *might* be the right choice given Unity's goals, but it feels like another of the many, many gotchas in Unity.

@peterdrake Your characterization is correct.

"If this is accurate, would the same thing happen if position was a field rather than a property?"

No, if it was a field, you would be able to set x directly, because you'd be operating directly on the actual position's values in memory. But when using an implicit property getter for a non-reference value (like a struct), you get a copy instead.

"It may also be that the "getter" for position creates a new Vector3 for more complicated reasons involving global vs local positions"

Pretty sure this is true, but it does have both a getter and a setter, so something like "transform.position += Vector3.right;" works as intended.

"I'm guessing Unity made Vector3 and Quaternion structs for efficiency reasons"

I think it's more about data integrity. Remember, if they made it a class, then I stored a reference to that position as a local variable, that reference would still be pointing to the transform's position. So, assigning a vertex to a transform's position would inadvertently link the two, and changing one would change the other. Forcing the copy behaviour ensures that you only change the actual position when you really mean to and prevents much harder-to-find bugs from popping up.

Sign in to participate in the conversation
Qoto Mastodon

QOTO: Question Others to Teach Ourselves
An inclusive, Academic Freedom, instance
All cultures welcome.
Hate speech and harassment strictly forbidden.