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

Setting values for a Vector3 not working

Discussion in 'Scripting' started by Industry4O, Jun 19, 2019.

  1. Industry4O

    Industry4O

    Joined:
    Oct 30, 2018
    Posts:
    15
    I have a Vector3 List which I initiate with 8 Vector3 elements in the start() method:
    Code (CSharp):
    1.  
    2. private List<Vector3> opcPositions = new List<Vector3>();
    3. void Start()
    4.     {
    5.         for (int i = 0; i < 8; i++)
    6.         {
    7.             opcPositions.Add(new Vector3(0.0f, 0.0f, 1.0f));
    8.         }
    9. }
    In another function I got this:
    Code (CSharp):
    1.  
    2. for (int i = 0; i < 8; i++)
    3.                     {
    4.                             Debug.Log("In Pojo:");
    5.                             Debug.Log(carrierCollection.carriers[i].X);
    6.                             //doesn't work, for simplifying I will just set the opcPositions[0]
    7.                             // opcPositions[i].Set(carrierCollection.carriers[i].X, carrierCollection.carriers[i].Y, 0);
    8.                             opcPositions[0].Set(333.0f, 666.0f, 0);
    9.  
    10.                             Debug.Log("in the List:");
    11.                             Debug.Log(opcPositions[0]);
    12.  
    13.                     }
    The output is:

    Code (CSharp):
    1. in Pojo:
    2. 14.5392
    3.  
    4. in the List:
    5. (0.0, 0.0, 1.0)

    What am I doing wrong here? Why the values of the Vector3 element in my list can't be set?
     
    Last edited: Jun 19, 2019
  2. Kwinten

    Kwinten

    Joined:
    Jan 25, 2015
    Posts:
    49
    You are setting the values for
    opcPositions[0]
    but logging the values of
    opcPositions[i]
    .

    You'll want to do
    opcPositions[i].Set(...)
    instead.
     
  3. Industry4O

    Industry4O

    Joined:
    Oct 30, 2018
    Posts:
    15
    No I just edited it for the forum, I wanted only Debug 1 element (the first one)...
    but like you see the line
    // opcPositions[i].Set(carrierCollection.carriers[i].X, carrierCollection.carriers[i].Y, 0);
    which is commented was already there with opcPositions ...

    It is for all elements in the List
    (0.0, 0.0, 1.0)
    .
     
  4. Kwinten

    Kwinten

    Joined:
    Jan 25, 2015
    Posts:
    49
    Do you get the same results if you assign the array elements a new Vector3 instead of setting the values in-place? E.g:

    Code (CSharp):
    1. opcPositions[i] = new Vector3(100, 100, 100);
    I don't see any reason for this to fail.
     
  5. Industry4O

    Industry4O

    Joined:
    Oct 30, 2018
    Posts:
    15
    Yes that is working. But I thought its not optimized, because I have multiple messages in the second, which are used to set the opcPositions, and if you create every time a new Vector3, than the Unity App will be full of garbage? The Hololens is not so strong.
     
  6. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,745
    Vector3 is a structure. It is passed by value. This code
    Code (CSharp):
    1. opcPositions[0].Set(333.0f, 666.0f, 0);
    effectively copies Vector3 from array to background variable and calls .Set method on that background instance. Values in array left intact.
    It's the same as
    Code (CSharp):
    1. Vector3 vector = new Vector3(opcPositions[i].x, opcPositions[i].y, opcPositions[i].z);
    2. vector.Set(333.0f, 666.0f, 0);
     
    Bunny83 and lordofduct like this.
  7. Industry4O

    Industry4O

    Joined:
    Oct 30, 2018
    Posts:
    15
    Ok so the App won't get slower and slower?
    I still don't know why it's not working with
    vector.Set() 
    but with
    vector = new Vector3();
     
  8. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,745
    Because you call Vector.Set on the copy of vector in your function local variables stack. Your code do not modifies any values in the list.
     
    lordofduct likes this.
  9. Kwinten

    Kwinten

    Joined:
    Jan 25, 2015
    Posts:
    49
    @palex-nx is right.

    And since Vector3's are structs, you really do not need to worry about creating new instances too much at all. That is how structs are meant to be used.
     
  10. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,193
    A couple of things:
    • Generally, structs don't generate garbage. They're allocated differently. Often, optimizing some code just comes down to changing a class to a struct.
    • Garbage generation doesn't really result in the app getting "slower and slower". In most cases, garbage that gets generated is cleaned up fairly soon after being created. For example, if you had a 'for' loop that (for some reason) generated a bunch of strings, but didn't keep the strings after the end of the loop, you'd create a bunch of string garbage which would be cleaned up very soon after the loop finished. Garbage collection passes can certainly cause slowdowns and hiccups, but usually it's not a "slower and slower" feel, unless you've got a memory leak or something.
     
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,506
    This

    Or... almost this.

    Only thing I want to say is that the problem is because OP is using a List, not an array.

    If they used an array their code would work just fine because Arrays support direct memory access of its members.

    But List is a class that wraps around an array... the [index] accessor of List does not behave the same way as of an array. It instead acts like a getter method and returns a copy, like palex-nx states.

    ...

    To get more specific about the memory of it all.

    While you're in your function/method that is doing your for loop you're in stack frame A.

    You then say opcPositions[index], this calls the List<T>.this[index] method, this allocates stack from B.

    Stack frame B does work, accessing the array inside of List<T>. It copies the value out of its array (which exists on the heap), it then returns. When returning stack frame B is purged from the stack and the "return value" is placed on the top of the stack (at the end of stack frame A) for direct access by stack frame A.

    This is its real location in memory, the top of stack frame A... not the Vector3 that's located over in the heap that's inside the array that's inside List<T>.

    You are now calling Set on this Vector3 sitting on the end of stack frame A.

    ...

    This is opposed to if opcPositions were an array.

    When you say opcPositions[index] and it's an array. Really that's just saying "address of opcPositions + sizeOfMember * index". It's pointing directly into the heap.
     
    Last edited: Jun 19, 2019
    Bunny83 and Kwinten like this.
  12. GLA1234

    GLA1234

    Joined:
    May 26, 2023
    Posts:
    6
    I know this thread is old, and I realize there's a way to just use 'new Vector3()' to handle the OP's question. But on GitHub, the Unity code shows this:

    Code (CSharp):
    1.  // Set x, y and z components of an existing Vector3.
    2.  [MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
    3.  public void Set(float newX, float newY, float newZ) { x = newX; y = newY; z = newZ; }
    Doesn't this suggest that the Set function should've set the values as the OP was expecting? If not, then what's the purpose of the Set method? If you can't write:

    Code (CSharp):
    1. transform.position.Set(1,2,3);
    And have it set the transform's x,y,z -- then why would you ever call this method? And why call it "set" ? How can you use the thing it sets?

    https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Math/Vector3.cs
     
    Last edited: Dec 4, 2023
  13. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,506
    Because 'transform.position' is a property that returns a copy of the position vector. You're not modifying a direct reference to the underlying property field, but rather a copy of it.

    It's related to the same reason as my last post from 2019.

    But instead of the stack frame accessing the underlying array for the list and returning the copy. In this case the underlying stack frame calls into the internal unity engine and returns a copy of the position.

    Code (csharp):
    1. var pos = transform.position; //copies position
    2. pos.Set(1,2,3); //set the values of the position
    3. transform.position = pos; //set it back to the transform
    While the property 'position' may LOOK like its a field on Transform... it's not. It's a property. A property is sort of kind of like a method that is shaped like a field.
    https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties

    It exists for use in contexts where you're not copying properties in place. For example I could use it here:
    Code (csharp):
    1. public class SomeScript : MonoBehaviour
    2. {
    3.     public Vector3 force;
    4.  
    5.     void Update()
    6.     {
    7.         if (Input.GetKeyDown(KeyCode.Space)) //pressing space applies force
    8.         {
    9.             this.GetComponent<RigidBody>().ApplyForce(force);
    10.         }
    11.         if (Input.GetKeyDown(KeyCode.Enter)) //pressing enter resets the force to 0,0,0
    12.         {
    13.             force.Set(0,0,0);
    14.         }
    15.         if (Input.GetKeyDown(KeyCode.UpArrow)) //pressing up increase the force in the y-direction
    16.         {
    17.             force.y += 1f;
    18.         }
    19.     }
    20. }
     
    Last edited: Dec 4, 2023
    Bunny83 likes this.
  14. GLA1234

    GLA1234

    Joined:
    May 26, 2023
    Posts:
    6
    OK, I get it, thanks.

    I'm an ASM and C programmer, but occasionally I dabble with C# and Java as I used to with PHP, Perl, and Tk/Tcl.

    Always interesting to learn how they work.
     
  15. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,506
    Basically C# and Java and these higher level Object Oriented languages have lots of features that promote 'encapsulation'. Properties are one of those things. By restricting access to private fields in a manner where you can't get a direct reference to it can allow the class to control flow of its data.

    From a C perspective... giving direct reference would be like letting someone have a pointer to the 'position' field. This could be a problem because that pointer could be handed off to some logic which modifies it, but the class doesn't know it was modified, and therefore doesn't update dependent systems based on that. For example... maybe setting the position is what causes the hierarchy to be recalculated before the draw pass at end of frame (I don't know if this is true, just an example for example sake). Resulting in an inaccurate results.

    Honestly though in this case it's less to do with those implications and more to do with the fact that the 'Transform' class is really just a binder class that translates between the internal Unity unmanaged engine logic (written in C/C++) and the scripting environment running in the .Net runtime (C#). To return a reference to the vector would require a pointer across those domains... and not only that it would have required a C# feature of 'ref properties' which didn't exist back when Unity was first created.
     
    GLA1234 and Harry-Wells like this.
  16. GLA1234

    GLA1234

    Joined:
    May 26, 2023
    Posts:
    6
    Properties reminds me of accessing ports.

    Reading or writing a port can cause a cascade of operations but superficially looks like you're just reading or writing a single byte.

    Like reading an IRQ port and having it not only return the bits, but also clear pending interrupts.

    If you have no documentation for a class at hand, is there a way to detect if a symbol name is a property or just a 'regular' variable?
     
    Last edited: Dec 5, 2023
  17. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,266
    An analogy I recall all those years back was that for reading a value-type from a property, it's like asking for some information through the post and being given a photocopy of the original. It doesn't matter what you write on the photocopy you get, the original is intact. If you do the same again, you've got another photocopy i.e. two copies but the original is still intact. Setting a value-type is like when you've modifed the photocopy and you send it back through the post to replace the original copy.

    Insert any "copy related analogy through the post" you like. :)
     
    Harry-Wells likes this.
  18. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,506
    Just by looking at it in text form? No. Fields ("'regular' variable") and Properties are specifically shaped the same so there is not textual difference between them when typing them out for access. (there is an argument to be made that this is bad design on the part of MS/C#... I sort of agree to an extent. Some would argue MS's style/design guide suggests keeping fields private always, but you know... we don't all do that. IF I'm going to make a field public I keep it lower-case, and make the first letter of my properties upper-case. But that's just my style and you can't rely on that when using 3rd parties libs)

    If you're in an IDE though, such as Visual Studio, it will tell you. While typing the floating hint box will display icons and text that tells you what it is:
    upload_2023-12-5_12-2-6.png

    The little blue box is a field, it also says "(field)" to the right.
    The wrench is a property.

    Or if you mouse over an already typed out member it will do the same:
    upload_2023-12-5_12-3-16.png

    Note that properties will also include the if it is get/set, get only, or set only. And to demonstrate that this isn't because this 'Blargh' class is my own... here is List<int>:
    upload_2023-12-5_12-4-44.png

    And if it's a ref property it'll also show that:
    upload_2023-12-5_12-7-53.png

    Note that a ref property returns the underlying field by reference and therefore will behave more like a field.

    ...

    Personally, I've been writing C# for going on 2 decades soon. How I personally deal with it, since looking at these little hover-overs is not exactly conducive to conserving my time. I just treat all things as properties while in C#.

    See I came to C# from an enterprise setting with OOP in mind. As well as LONG before the 'ref property' was introduced (honestly I often forget 'ref property' is a thing these days). Even if a library makes their fields public I mostly stick to the MS style guide and just assume it's all properties all the way down.

    If I see:
    Code (csharp):
    1. blargh.boo.herpaderp.x= 6;
    It doesn't read "correct" to me. It'd feel like reading, a, sentence, with, punctuation, scattered, around it in English. Like yes it's readable (hell I scatter punctuation around my English), but it doesn't read "correct"... my gut says its informal, and informal code doesn't run (even if it may technically run).

    Mainly because the idea of setting a field member of an object directly in C# "smells" to me. Because we're in a language that was originally designed to be heavily OO oriented (it can be argued C# has dispatched wildly from its original design years ago).

    This isn't to say I stick to this in all my programming. Different languages are designed with different paradigms in mind. C or assembly convey very different intent and therefore the things you might write in it don't "smell" because that's just how you do there (although obviously this concept for fields/properties just isn't a thing in assembly). Although I would say passing around repeatedly nested structs in C would be a smell to me as well in most, but not all, cases.

    But yeah... this factoid about properties kind of just disappears when you just assume it's likely a property.

    It's also why I don't think I've EVER used the 'Set' method on Vector3 (aside from example scenarios like this thread).
     
    Last edited: Dec 5, 2023
    CodeRonnie and GLA1234 like this.
  19. GLA1234

    GLA1234

    Joined:
    May 26, 2023
    Posts:
    6
    Thank you. Very informative.
     
  20. GLA1234

    GLA1234

    Joined:
    May 26, 2023
    Posts:
    6
    I found a Mickey Mouse way of sort of doing this. Although the property loop throws an exception which I don't understand exactly. It sometimes outputs "ArgumentException: method arguments are incompatible"

    I'm not sure where I'm invoking a function that is offended by a lack of proper arguments. Seems like these are just queries supported by the C# interpreter behind-the-scenes.

    Code (CSharp):
    1. var type       = someUnityObject.GetType();
    2.  
    3. var properties = type.GetProperties(BindingFlags.Static | BindingFlags.Instance |
    4.                                     BindingFlags.Public | BindingFlags.NonPublic);
    5.  
    6. foreach (var prop in properties) {
    7.      var pvalue = type.GetProperty(prop.Name).GetValue(someUnityObject, null);
    8.  
    9.      if (pvalue == null) Debug.Log( "prop name=" +prop.Name );
    10.      else Debug.Log("prop name=" +prop.Name+ " value=" +pvalue.ToString());
    11. }
    12.  
    13. MethodInfo[] funcs = type.GetMethods(BindingFlags.Static | BindingFlags.Instance |
    14.                                      BindingFlags.Public | BindingFlags.NonPublic);
    15.  
    16. foreach (MethodInfo method in funcs)
    17. {
    18.      Debug.Log("func name: " +method.Name);
    19.  
    20.      ParameterInfo[] parameters = method.GetParameters();
    21.  
    22.      foreach(ParameterInfo parm in parameters) {
    23.           Debug.Log("parm=" +parm.ParameterType);
    24.      }
    25. }
    26.  
    27.     //N.B. there are many items which can be accessed via these
    28.  
     
  21. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,893
    Have you thought about generic methods? Generic methods require a concrete type argument binding. Just like you have to use MakeGenericType with generic System.Type definitions, you have to use "MakeGenericMethod" and specify the generic type arguments before you can actually use the method.

    You now have successfully derailed the original thread which originally was about modifying a struct type in a list. You went over to properties, then to the (misleading and mutating) "Set" methods of Vector3 and now dived into reflection which is all not part of the original thread. You also don't set any value with your code, you only read it.

    Please start your own thread.