'Cannot modify .... because it is not a variable' in C#

Discussion in 'Scripting' started by fr0sty, Sep 26, 2007.

  1. fr0sty

    fr0sty

    New Member

    Joined:
    Sep 26, 2007
    Messages:
    1
    Hey guys,

    I'm pretty new to C# and I've decided it's the language I want to mostly script with in Unity, since I'm fairly familiar with C and C++.

    I've been going through doing the tutorials and converting the sample scripts to C# as I go, but I've come across something that's got me stumped. In the racing tutorial, the camera script does this inside FixedUpdate():

    Code (csharp):
    1. transform.position.x += 5;
    2.  
    When I try and do the same thing in my C# script, I get the error:

    "Cannot modify the return value of 'UnityEngine.Transform.position' because it is not a variable."

    and yet this works fine

    Code (csharp):
    1. Vector3 newpos = transform.position;
    2. newpos.x += 5.0f; // why does this work while 'transform.position.x += 5.0f;' doesn't?
    3. transform.position = newpos;
    4.  
    Can someone clue me in on what's going on? A quick google search turned up some stuff about value/reference semantics, but I couldn't quite get my head around it. Am I right that 'position' is a member variable of 'transform' that is of type struct? What's different in the semantics compared to the same thing in C++?

    Thanks for the help. I'm loving Unity btw, it's gotten me back into coding at home and not just at work :)
  2. NicholasFrancis

    NicholasFrancis

    Member

    Joined:
    Apr 8, 2005
    Messages:
    1,587
    It's a bad part of the c# design.

    Internally, stuff like transform.position is implemented as properties (so when you assign a value to position, a function gets called into the Unity engine). When this is done, you can't assign into an individual member of the Vector3 struct, but have to assign the entire struct.

    in our own implementation of Javascript, we've worked around it, but in C#, you need to read the value out into a vector3 and then modify it.

    you could also do:

    transform.position = transform.position + new Vector3 (0, .5f, 0);
  3. bronxbomber92

    bronxbomber92

    New Member

    Joined:
    Nov 11, 2006
    Messages:
    890
    Off topic, sorry.

    Out of curiosity, why did you guys make Vector3 a struct, and not a class? Better performance adding Vector3's to the stack instead of the heap?
  4. Aras

    Aras

    Graphics Plumber Unity Technologies

    Joined:
    Nov 7, 2005
    Messages:
    4,172
    Yes. And Vector3 arrays are actually laid out as arrays in memory (as opposed to being arrays of references to Vector3 objects that are lying somewhere else).
  5. r618

    r618

    Member

    Joined:
    Jan 19, 2009
    Messages:
    105
    hello,
    just out of curiosity ( found this thread via google ) :
    which way of modifying 'struct' property is better for garbage collecting in c# -
    either
    Code (csharp):
    1.  
    2. Vector3 newpos = transform.position;
    3. newpos.x += 5.0f; // why does this work while 'transform.position.x += 5.0f;' doesn't?
    4. transform.position = newpos;
    5.  
    or creating new Vector3 and assigning it to property ?, i.e.
    Code (csharp):
    1.  
    2. transform.position = transform.position + new Vector3 (0, .5f, 0);
    3.  
    does the Vector3 in the first case get created on stack ? If so, it would be definitely better to use like this, since in second case it gets to be created in heap and garbage collector has to deal with it later - am I right ?
    If not, could someone elaborate on this ? :)

    edit: I mean ti mainly regarding the iPhone development, didnt notice which forum the originating question was :)

    thanks!
  6. lordshitzu

    lordshitzu

    New Member

    Joined:
    Jan 6, 2010
    Messages:
    1
    I too just found this via google, so I figured I would answer your question.

    Even though Vector3 uses new to instantiate a new vector, it is a struct, not a class, therefore it will follow value type semantics and will be created on the stack.

    In other words, even if you create a new vector in this way, it will not create garbage, so neither way should be particularly worse than the other.

    Personally I would tend to do:
    transform.position += new Vector3 (0.0f, 0.5f, 0.0f);
  7. czx_main

    czx_main

    New Member

    Joined:
    Feb 18, 2009
    Messages:
    171
    Code (csharp):
    1.  
    2.         Vector3 MyPosition = transform.position;
    3.         MyPosition.x += 5f;
    4.         transform.position = MyPosition;
    5.  
    ok!
  8. r618

    r618

    Member

    Joined:
    Jan 19, 2009
    Messages:
    105
    thanks, lordshitzu ! :D

    btw a little in-depth explanation of value types - as they should be always considered regardless of being stored on stack - is on Lippert's blog "Adventure's in Coding" , e.g. here http://blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx
    but i guess that in case of more restricted environment such as the iPhone's the way of handling types must be considered
    yet another thing is difference between ms and mono clr implementation (if any)
  9. bpatters

    bpatters

    New Member

    Joined:
    Oct 5, 2009
    Messages:
    164
    structs are one of the major reasons IMO that C# has an edge over most other virtual/garbage collection engines. The ability to specify memory to be on the stack and value based is one the key performance benefits of C++. I was very happy to find out that Unity uses this to minimize garbage collection issues that plaque most GC based engines.


    To explain it for a C++ developer think of the following c++ scenario:
    Code (csharp):
    1.  
    2. typedef struct Vector3 {
    3.   int x;
    4.   int y;
    5.   int z;
    6. };
    7.  
    8. class Transform {
    9.    Vector3 mPos;
    10. public:
    11.    Vector3 position() { return mPos; };
    12. };
    13.  
    In C++ if you did something like:
    Code (csharp):
    1.  
    2. Transform myTransform;
    3.  
    4. myTransform.position().x = 5;
    5.  
    it'd actually have no effect on the myTransform class's member variable because the position() method would return a copy of it's mPos Vector3 and you'd modify that copy and throw it away all in one line.

    In C# they have concept of function calls with variable like access syntax. So in C# when you do
    Code (csharp):
    1.  
    2. transform.position.x = 5;
    3.  
    you are actually doing in C# exactly what is done in C++ above and C# is smart enough to warn you that you are going to modify something on the stack and throw it away unintentionally.


    This exact scenario tripped me up the other day and it took me a good 10 minutes to realize WTH the compiler was telling me. Luckily I read a C# book and played with it for a long time a few years ago so was familiar with this concept and finally remembered it.
  10. shotgunemmet

    shotgunemmet

    New Member

    Joined:
    Apr 17, 2012
    Messages:
    2
    .position has a get set that both use a Vector3, in javascript you can create variable struct's like Vector3 without creating a new instance of one, the compiler takes more liberties with what kind of variables you want what you want to use them for.

    In C# you get more control over what variables you want to remain null or when you want to initialise them, as a result the compiler will not create a new instance of Vector3 position then pre-populate the Vector3 with the existing get() function (as that would be an additional function call duplication of a Vector3 object that you may not want), so instead you must create your own new instance of a Vector3 populate it with the data you require.

    It may seem like C# is being a pain, however the power of a mid-level language like C# allows for much more control over memory management and garbage collection than javascript, I would always use C# in unity for optimisation purposes.

    Hope this explains why C# compiles that way =]