Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Set value on GameObject.transform.position.x with C# ?

Discussion in 'Scripting' started by RichardSmith, Nov 11, 2010.

  1. RichardSmith

    RichardSmith

    Joined:
    Oct 19, 2010
    Posts:
    27
    Hi there Unity forum heros...

    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.

    Thanks!

    Richard
     
    chidboy and Snapback-Studios like this.
  2. Nikolay116

    Nikolay116

    Joined:
    Mar 21, 2010
    Posts:
    421
    I do not know an easy way either. I do like this.

    Vector3 temp = new Vector3(7.0f,0,0);
    myGameObject.transform.position += temp;
     
  3. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
  4. RichardSmith

    RichardSmith

    Joined:
    Oct 19, 2010
    Posts:
    27
    Nikolay116 FizixMan,

    OK I understand now, I was just checking I wasn't missing something obvious. Assigning a new Vector3 is no problem now I know this is normal behavior.

    Thank you both very much for your time! This forum has some wonderful members.

    Richard
     
  5. tonyd

    tonyd

    Joined:
    Jun 2, 2009
    Posts:
    1,224
    Another mark against C#.

    Sorry FizixMan...

    :D
     
  6. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    Providing better struct management, proper field encapsulation, and treating properties like properties and not like fields is a mark against C#? Oooook
     
    StephenWebb and mtdrume like this.
  7. tonyd

    tonyd

    Joined:
    Jun 2, 2009
    Posts:
    1,224
    I'll take concise, readable code over 'better' struct management and 'proper' field encapsulation any day. But we've danced this dance before...

    Back to the topic at hand, if you do direct assignments a lot, you could always wrap it in a function:

    Code (csharp):
    1. void SetTransformX(float n){
    2.     transform.position = new Vector3(n, transform.position.y, transform.position.z);
    3. }
    4. void Update (){
    5.     SetTransformX(7.0f);
    6. }
     
  8. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    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.
     
  9. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    Ahhh, the horrors of UnityScript readability:

    CSharp class
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. public class CSharp : MonoBehaviour
    4. {
    5.     private Vector3 m_Position = new Vector3(1, 1, 1);
    6.     public Vector3 Position
    7.     {
    8.         get { return this.m_Position; }
    9.     }
    10. }
    UnityScript class:
    Code (csharp):
    1. function Start()
    2. {
    3.     var csharp = this.gameObject.GetComponent("CSharp");
    4.     csharp.Position.x = 5;
    5.     csharp.Position.y = 3;
    6.     csharp.Position.z = -44;
    7.     Debug.Log("POSITION: " + csharp.Position);
    8. }
    Guess what errors (syntax and/or run-time) are reported and what is Debug.Logged?
     
    rahulk1991 likes this.
  10. RichardSmith

    RichardSmith

    Joined:
    Oct 19, 2010
    Posts:
    27
    Hi again, thanks for the discussion.

    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#

    Thank you!
     
  11. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    We posted the only viable solutions already.

    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.
     
    Last edited: Nov 11, 2010
  12. RichardSmith

    RichardSmith

    Joined:
    Oct 19, 2010
    Posts:
    27
    Wow coolness...

    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.

    Thanks so much
     
  13. KennyW

    KennyW

    Joined:
    Aug 20, 2010
    Posts:
    128
    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.
     
    Last edited: Nov 12, 2010
  14. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    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.

    Some more reading here: http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx

    The reason "position" is a copy is because it's actually a property, not a field. (http://msdn.microsoft.com/en-us/library/w86s7x04(v=VS.90).aspx) 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.
     
    KReguiegReply likes this.
  15. RichardSmith

    RichardSmith

    Joined:
    Oct 19, 2010
    Posts:
    27
    FizixMan,

    Those were super focused extremely helpful links indeed... I'm very grateful for your time!

    I've also bought a C# learning book to supplement my super dry references.

    Thanks also to everyone else who helped me on the thread. KennyW, tonyd, and Nickolay116.

    I love the Unity Community :D

    Cheers Richard
     
    KReguiegReply likes this.
  16. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    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.
     
  17. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    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.
     
  18. Cawas

    Cawas

    Joined:
    Jan 14, 2010
    Posts:
    121
    Yes, that's also what Mr Sinclair have been saying... To me, the reason behind his question is all about those matrices. All rest comes from that.
     
  19. danshampf

    danshampf

    Joined:
    Mar 25, 2014
    Posts:
    11
    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:

    Code (csharp):
    1.  
    2. public static class UtilExtensions
    3. {
    4.     // Transform's vector getters in Unity return vectors by value, so we can
    5.     // only change them by assigning a complete vector and not individual
    6.     // components.  WAR this with a bunch of helper extensions.
    7.     public static void LocalScaleSetX( this Transform t, float s ) { Vector3 v = t.localScale; v.x  = s; t.localScale = v; }
    8.     public static void LocalScaleSetY( this Transform t, float s ) { Vector3 v = t.localScale; v.y  = s; t.localScale = v; }
    9.     public static void LocalScaleSetZ( this Transform t, float s ) { Vector3 v = t.localScale; v.z  = s; t.localScale = v; }
    10.     public static void LocalScaleMulX( this Transform t, float s ) { Vector3 v = t.localScale; v.x *= s; t.localScale = v; }
    11.     public static void LocalScaleMulY( this Transform t, float s ) { Vector3 v = t.localScale; v.y *= s; t.localScale = v; }
    12.     public static void LocalScaleMulZ( this Transform t, float s ) { Vector3 v = t.localScale; v.z *= s; t.localScale = v; }
    13. //  ... and so on for other common vector members and operations
    14. }
    15.  
    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).
     
    Daerst likes this.
  20. Hippiecode

    Hippiecode

    Joined:
    Feb 13, 2012
    Posts:
    110
    gameObject.transform.position=new Vector3(7f,0,0);
     
    Heresy Studios likes this.
  21. Smooth-P

    Smooth-P

    Joined:
    Sep 15, 2012
    Posts:
    214
    For the addition methods I'd just use foo += new Vector3(x, y, z). Extension methods don't make things any simpler, more concise, or more readable than that. And for the multiplication methods I'd probably just have one method that takes a vector rather than 3 separate methods.

    Extension methods are nice and I use them extensively, but they don't end up being as helpful as they would seem in this situation.
     
  22. danshampf

    danshampf

    Joined:
    Mar 25, 2014
    Posts:
    11
    I agree, += new Vector... is clearer. I didn't actually expect that to work because I guess I didn't fully understand what was going on. I had assumed that this would add the values to a copy of the vector (returned by the getter), which is apparently not the case. I think I get it now though: += invokes the setter, not [just?] the getter..

    It's kind of ugly though that this works for addition but not multiplication. Any idea what's the rationale behind not including an operator* for component-wise vector mult? I wonder if it's because they were afraid people would confuse it with dot product (not a real issue in my experience).
     
  23. McCree

    McCree

    Joined:
    Nov 18, 2015
    Posts:
    2
    I have the same problem!!!
     
  24. arfan447

    arfan447

    Joined:
    Oct 26, 2015
    Posts:
    7
    // Script is basically open or close a door But help you in change a game object position on desier location in (x,y,z)
    plane
    public Transform LeftDoor;
    public Transform RightDoor;
    void Start ()
    {
    LeftDoor.position = new Vector3 (13.63912f , 2.47f , -1.460879f);
    RightDoor.position = new Vector3 (12.67912f, 2.47f, -1.460879f);
    }

    // Update is called once per frame
    void Update ()
    {

    }
    void OnTriggerEnter(Collider col)
    {
    if (col.gameObject.tag == "Enemy" )
    {
    LeftDoor.position = new Vector3 (14.66f , 2.47f ,-1.460879f);
    RightDoor.position = new Vector3 (11.67f, 2.47f,-1.460879f);
    }
    }
    void OnTriggerExit(Collider col)
    {
    if (col.gameObject.tag == "Enemy")
    {
    LeftDoor.position = new Vector3 (13.63912f , 2.47f , -1.460879f);
    RightDoor.position = new Vector3 (12.67912f, 2.47f, -1.460879f);
    }
    }
     
  25. Ebonicus

    Ebonicus

    Joined:
    Oct 31, 2016
    Posts:
    158

    Doesn't this contradict Tonyd's post , since he sets the value of the same copy in his wrapper function?
    1. void SetTransformX(float n){
    2. transform.position = new Vector3(n, transform.position.y, transform.position.z);
    3. }

    That actually works, but it shouldn't if I am understanding you correctly.
     
  26. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    What Chris Sinclair said isn't quite right. So to explain it all I'm going to go over a few things that you might already know:
    Code (CSharp):
    1. public struct Vector3
    2. {
    3.     float _x;
    4.     float _y;
    5.     float _z;
    6.  
    7.     public float x
    8.     {
    9.          get { return _x; }
    10.          set { _x = value {;
    11.      }
    12. }
    _x is called a field. Its basically just your run of the mill variable like your use to in any language.
    x is called a property. Its the equivalent of doing this:
    Code (CSharp):
    1. public float GetX()
    2. {
    3.      return _x;
    4. }
    5.  
    6. public void SetX(float value)
    7. {
    8.     _x = value;
    9. }
    And its effectively what the compiler creates. Properties are methods.

    So the transform has this inside of it:
    Code (CSharp):
    1. public class Transform
    2. {
    3.       Vector3 _position;
    4.  
    5.       public Vector3 position
    6.       {
    7.              get { return _position;}
    8.              set { _position = value}
    9.       }
    10. }
    So when you type transform.position you are invoking the getter or the setter depending on the code.. which is effectively calling the functions to get the _position or set the _position. Now it is true that if you type this:
    Code (CSharp):
    1. Vector3 position = transform.position;
    2. position.y = 50f;
    The getter is returning the Vector3 _position, but its a struct so it does return a COPY of the _position. so when you set your local variable positon.y = 50. your not actually setting the transforms position.

    But this code:
    Code (CSharp):
    1. transform.position = new Vector3(1f,2f,3f);
    is invoking the setter. You passing it a copy of the Vector3 (1f,2f,3f) and its actually putting those values into the transform's position. transform.position is not a copy of _position, it is basically a reference to a method to set the internal variable _position. To stress that transform.position is a method (function) not a variable (field).

    So the original problem was you could do this to get at the x,y,z values of a Vector3:
    Code (CSharp):
    1. Vector3 someVector = new Vector3(1f,2f,3f);
    2. Debug.Log(someVector[2]);   <--- this will print out 3f  (z value)
    That is because of an entirely different feature of c# called Indexers:
    https://msdn.microsoft.com/en-us/library/2549tw02.aspx

    Basically you can setup something in a class or a struct so if someone uses the [] indexing it returns specific things like this:
    Code (CSharp):
    1. public struct Vector3
    2. {
    3.      float _x;
    4.      float _y;
    5.      float _z;
    6.  
    7.      public float this[int index]
    8.      {
    9.            get
    10.            {
    11.                  if (index ==0)
    12.                      return _x;
    13.                  else if (index == 1)
    14.                       return _y;
    15.                 else if (index == 2)
    16.                       return _z;
    17.                  else
    18.                        throw new IndexOutOfRangeException();
    19.             }
    20.       }
    21. }
    This is how you can do the code above. Now when you type transform.position your NOT typing a vector 3 variable your typing a reference to a method to set the internal Vector3 _position variable. That is why the index doesn't happen, not because its a copy. The actual _position variable is private (thats why we have the public position property) but if it were public you could do this:
    Code (CSharp):
    1. transform._postion[1];
     
  27. Ebonicus

    Ebonicus

    Joined:
    Oct 31, 2016
    Posts:
    158
    That was explicitly clear, thank you so much!
     
    takatok likes this.
  28. sobanarshad85

    sobanarshad85

    Joined:
    Jan 9, 2018
    Posts:
    5
    vector3 hy = gameobject.position;
    by.position.x = something;
     
    RishabhRyber likes this.
  29. RishabhRyber

    RishabhRyber

    Joined:
    Jun 17, 2017
    Posts:
    1
    Thanks dude. this solved my problem.
     
  30. diliupg

    diliupg

    Joined:
    Jan 23, 2018
    Posts:
    45
    oop(s)... :-D