- Home /
Why using = isn´t fast enough for changing Vector3 values from the transform?
= should be instant. No matter if your changing position, scale, etc. The change should be quick If you do something like this in Update ():
transform.localScale = new Vector3 ( 5.5 + 1.2 + 2.3, transform.localScale.y, transform.localScale.z);
However, when you test it, it feels slower. Is there some method to make an operation like this faster? I thought maybe using InvokeRepeating with a higher framerate, but that´s not good for performance, so I didn´t even tried it. And also I don´t know if that will work.
Changing the values of a Vector3 does not take time at all. However, under the hood, Unity does much more than simply changing the Vector3. It has to recompute the position, rotation and scale of the object, and all its children. If you have a complex hierarchy, it can take some time.
Then how can I accelerate the process? $$anonymous$$ost objects will be part of a hierarchy because I had to separate them in order to get right the colliders. Besides that, they have fixed positions and rotations, so I think they aren´t recalculated.
if it is related in anyway to physics you should try putting it into FixedUpdate ins$$anonymous$$d of just Update. Update happens once every frame, but FixedUpdate happens I think 25 times per frame? see if that helps
FixedUpdate
does not run 25 times per frame. It may be called 25 per seconds depending on the parameter you gave in the Time
settings.
What do you mean "feels slow"? Do you mean that it takes a lot of time on the CPU and harms your framerate, lowering your fps? Or do you mean that the movement is not immediate, and it takes time until you see the result on the screen? The first is a bit more of an issue, because "localscale" is a property and not a variable, so it actually calls a function that does a lot of stuff and not just assigns a value. The second is just not true, setting the scale affects the next time the object is rendered, so I guess this is not what you meant, unless you have something very weird going on and then you should explain yourself better.
Answer by Bunny83 · Nov 16, 2018 at 02:06 PM
The change is instant. Any code that runs after the line where you changed the scale will see the change and will be affected by the change. So, no, there is no way to make it faster. I'm not sure you know / understand how a realtime application works. There is not visual update until the current frame is completed. How fast you get a visual update depends on your framerate. Regardless of the fact if you use vSync or not, if you have a monitor with a refresh rate of 60Hz, you never get a faster visual update than 60 times per second. That means between two visual updates there's always a 16.6ms delay.
Your single line of code simply changes the scale to a fix size (~ 9.0 on x) within one frame and doesn't change after that. We have no context about where and when you execute this line of code and if there might be some other script that might affect the scale of the object. Also it's difficult to interpret the phrase "it feels slower". Without more information we can't give a more detailed explanation. Note that nothing you do can get you a faster visual update than what your hardware is giving you.
Note that you should not use FixedUpdate for anything that is not directly related to a continuous physics force. FixedUpdate is the physics update which typically runs slightly slower than the visual update (The default rate is 50 calls per second).
@Bunny83 am not precisely changing the scale that way. It was just an easier way of explaining the = speed problem. Am really resizing the localScale based on collisions from both sides with this:
currentX = (this.transform.localScale.x * filter.mesh.bounds.size.x + extraLeft + extraRight) / filter.mesh.bounds.size.x;
Which is getting the updated x scale (localScale multiplied by meshFilter) and adding the extra values. These values are floats that change depending on raycast length, (both) like this: hit.distance - skinWidth; (skinWidth is also a float)
Then the ray origin is defined as this = coll.contacts[i].point - Vector3.right multiplied by skinWidth; (or + ins$$anonymous$$d of - depending of the side, also am writing multiplied because for some reason I can´t use its sign). So originally I did this to increase the size when the objects started to separate, but then I realized than it also works for decreasing size since when the hit.distance < skinWidth the extra values become negative.
Anyway, for testing purposes, I moved two colliders with the mouse and squeezed the object between them. It works, but when the colliders move too fast they go through the object too much and then the object´s edge is further than the skinWidth. Therefore, there´s no space for rays to be thrown and the object stops resizing. I thought this was because the change in scale wasn´t fast enough. Also because its scale wasn´t always the distance between collisions, taking some time to increase ins$$anonymous$$d of being immeadiate. And I don´t know why nobody get the "feels slower". I meant that the change just isn´t immediate.
Answer by JVene · Nov 16, 2018 at 05:48 PM
"However, when you test it, it feels slower."
Everyone's sense of 'feel' in such matters is subjective, and isn't a scientific measurement (though that doesn't mean it should automatically be ignored). Others have posted some practical information as to why. In order to test the speed of this assignment you'd have to establish a test, independent of the myriad actions that are being taken, to really know if this code is in any way associated with performance.
That said, the "new Vector3" is the most problematic issue relative to the code posted and the immediate impact on performance. New invokes a system call to request memory, which is treated as a common resource to all threads and employs a lock in threaded models (coordinating 'new' among threads), searches for a suitable amount of memory among fragments (takes a while, relatively speaking) then returns and initializes a Vector3 for use. That takes much more time than the assignment itself. For anyone concerned about performance such a temporary Vector3 should consider creating the temporary at initialization and use it when this code is repeated.
localScale is a property, not merely POD data. It invokes methods which control the assignment (one would have to review the source to know all that it might do). For performance concerns one must study and learn what takes time, and how much, in order to make informed selections. I have no data on localScale vs the global Scale, but one may invoke more work than the other. Here, again, creating a test that could time such operations would reveal what is most efficient.
I'd like to address the propagation of the description "instant" among responses. Nothing on a computer is actually instant, which you probably realize. We do tend to talk colloquially, and in terms relative to scales that compare thousands to billions, where "instant" may be colloquially acceptable but not really accurate.
One would expect that a Vector3 assignment is a move (in assembler) of 3 floats. This is a microscopic bit of work which can be greatly impacted by cache behavior, and can only be recognized for timing at the assembler level. However, when the LValue (the recipient of the assignment) is a property in C#, all bets are off without knowing what operations the property invokes (which I know is a repeat of what I said already). Consider that if scale is altered, the next engine cycle is going to have to adjust the object's entire representation (and it's children) as to it's local entries relative to the global information fed to the rendering engine. Likewise, this would impact the physics engine's colliders, as their scales must be fed to the physics engine to represent the new scale. This may not be trivial work, and could very well differ dependent on how complex the model(s) impacted are (children, colliders, etc).
We have documentation that suggests, in late versions of Unity, scales are low or near zero cost. Even as that may be true due to the underlying representation, it does not necessarily mean that changing scale during runtime has zero cost. It may have initialization costs exacted one time, then subsequently have nearly zero impact. This may be what you 'feel', especially if scale is adjusted frequently.
You have already mentioned this in another answer but you had rewrote your answer so you removed that statement over there. Creating a Vector3 does not allocate any memory. Vector3 is a struct and therefore a value type. The "new" keyword works different for reference types (classes) and value types. Since you come from a C++ background this may seem confusing. In C++ there is actually no conceptional difference between struct and class with the only exception that the default member visibility of a struct is public and of a class it's private.
In C++ you can create an instance of a struct / class either on the heap or on the stack. In C# that's not possible. Classes are always created on the heap. Structs are just value types and occupy memory depending on where and how the variable is declared. Local variables are allocated on the stack and therefore are allocated when you enter the method. Struct type variable inside a class simply belong to the memory footprint of the class.
So new Vector3()
does never allocate memory and struct generally will only occupy memory that is already allocated. The only exception to this is when a value type get "boxed". Boxing is simply the process of allocating memory on the heap and storing a valuetype in that memory. Though such structs can't be directly accessed and require unboxing when you access them. boxing most the time happens implicitly. So when you do
Vector3 vec = new Vector3(1,2,3); // does not allocate memory on the heap
object someRef = vec;
This will box the vector3 value on the heap in order to store a reference in "someRef"
Unboxing in most cases require a cast
Vector3 vec2 = (Vector3)someRef;