I’m stuck on a basic test trying to set the x value of a game objects transform.position as in…
myGameObject.transform.position.x = 7.0f;
I get the following error using C#…
Assets/Scripts/ScriptSpawnTest.cs(19,40): error CS1612: Cannot modify a value type return value of `UnityEngine.Transform.position’. Consider storing the value in a temporary variable
I can however set the whole transform.position to a new Vector3 fine. Any idea what I’m doing wrong? I don’t understand the temporary variable suggestion in the error message either.
This is simply due to the way C# is handling structs and properties. The only way you can change it (that I know of) is by reassigning the position vector. However, you can usually achieve the same result by using the Transform.Translate method: Unity - Scripting API: Transform.Translate
Providing better struct management, proper field encapsulation, and treating properties like properties and not like fields is a mark against C#? Oooook
Yeah, I’ll give you that. It is more readable just to alter the X value, and the best solution is to either create a wrapper method (as you have there) or use the existing Translate method.
using UnityEngine;
public class CSharp : MonoBehaviour
{
private Vector3 m_Position = new Vector3(1, 1, 1);
public Vector3 Position
{
get { return this.m_Position; }
}
}
I’m new to Unity and C#, but looking up struct access on a Unity Vector3 struct I seem to be able to set x,y,z using struct.this bracket access…
//sets the x value to 7.0
myVector[0] = 7.0f;
This actually worked for me on a seperate Vector3 and I thought I might apply this to the transform.position, but get the same error as mentioned in my first thread post.
//generates the error
myGameObject.transform.position[0] = 7.0f;
Any more ideas? I know it’s not vital, but I’m sure learning a lot about Unity script and C#
The reason you can’t directly alter the position vector is because it’s actually a copy, not the actual position stored on the transform.
transform.position <— returns a copy of the position
transform.position.x <— the “x” value of the copy
transform.position.x = 7.0f <— sets the “x” value on the copy
C# throws an error because you’re setting the “x” value on a copy, which is then destroyed. It’s a pointless line that if the compiler didn’t pick up on it, could cause a tonne of hair pulling.
UnityScript gets around this when it compiles by converting your code and firing the C# setter:
transform.position.x = 7.0f
when compiled in UnityScript, translates (more or less) to:
var tempVector : Vector3 = transform.position;
tempVector.x = 7.0f;
transform.position = tempVector;
You can infer that because if you replicate this with a custom C# class, you can track as the getters/setters are invoked.
I have so much to learn, but I’m guzzling it down. If you don’t find it too irritating, I’m unsure why transform.position is a copy of the struct, not the struct? Does copy mean reference to?
I’m assuming so far…
GameObject is a class. The GameObject class declares a Transform named transform. The Transform class declares a Vector3 struct named position.
I guess (I could be wrong thou) it is a matter of whether to re-use an existing instantiated object/struct, or to re-instantiate a new object/struct. C# tends to re-instantiate “low-level” entities to make them more “object-like” instead of value like. I guess this is to gain consistence and finally convenience in a wider scope. It however appears to be less convenient when comparing to a direct manipulation of an existing “object” (treated as more value-like). Value-like entities give a more flexible accessiblity but they are less encapsulated, so that you don’t need a setter/getter for access. At the same time it somehow lost its protection (less encapsulated).
I am actually new to C#, that’s how I feel about C# and I may well be wrong about it.
Whenever you pass a struct from variable to variable, from method to method, it creates a copy of that struct on the stack. Why use structs/value types? Because they’re faaaaast compared to classes/object types.
The reason “position” is a copy is because it’s actually a property, not a field. (Using Properties (C# Programming Guide) | Microsoft Learn) So when you try to access it, it invokes the getter method, returns a Vector3, which in turn passes back a copy of it on the stack.
Why is the API designed this way? Probably have to ask the Unity team.
A little bird (not in the slightest related to the Unity team) told me the API is designed this way because in the end they need Matrixes to talk to any graphic card / engine and so there’s no other way to abstract that away to Vector3 other than making “copies”. They’re not even really copies, they’re more like “translations”.
But none of that is an excuse, to me, that we simply can’t in C# make it look as good as it does in unity script. At least we couldn’t figure out any good way. I’d love to see if any one can.
Holy necro man… but since you did… this really has nothing to do specifically with C# or Structs in general. This was an API design decision by the Unity team. Most likely, when the position or rotation vectors are assigned, the setter for transform generates / updates the matrices needed to send to the graphics card. The transform object of course can tell when you update “transform.position” but it cannot tell when you update “transform.position.x”. For this reason, these structs were made “immutable”, which means you cannot change the values once they are created. Instead, you have to create a new one and reassign it. It actually makes a lot of sense and since value types like these have no baring on the heap or garbage collection it’s not going to be a performance issue to recreate and reassign.
I’m quite new to Unity (and C#), but this issue was bugging me pretty much immediately when getting started. My current take on it is to use extension methods on Transform, like so:
public static class UtilExtensions
{
// Transform's vector getters in Unity return vectors by value, so we can
// only change them by assigning a complete vector and not individual
// components. WAR this with a bunch of helper extensions.
public static void LocalScaleSetX( this Transform t, float s ) { Vector3 v = t.localScale; v.x = s; t.localScale = v; }
public static void LocalScaleSetY( this Transform t, float s ) { Vector3 v = t.localScale; v.y = s; t.localScale = v; }
public static void LocalScaleSetZ( this Transform t, float s ) { Vector3 v = t.localScale; v.z = s; t.localScale = v; }
public static void LocalScaleMulX( this Transform t, float s ) { Vector3 v = t.localScale; v.x *= s; t.localScale = v; }
public static void LocalScaleMulY( this Transform t, float s ) { Vector3 v = t.localScale; v.y *= s; t.localScale = v; }
public static void LocalScaleMulZ( this Transform t, float s ) { Vector3 v = t.localScale; v.z *= s; t.localScale = v; }
// ... and so on for other common vector members and operations
}
Since I lack C# experience, I wonder if this strikes people as particularly clever or paticularly ugly? (I come from the C++ world which doesn’t have extension methods, so I’m having a hard time judging whether something like this will come back to bite me later or not).