Efficienty on setting transform position

Looking ahead toward optimization strategy I’m looking at the code here and wondering if we should be aware of how often we’re updating position?

Transform.position gets an update or two every frame base don the keydown then gets a whole new setting at the end of the function regardless.

In normal software engineering I wouldn’t update an object until the final value is derived for efficiency, should we be doing that here too? I tried playing with code that I thought would work by reading the transform position into a variable, doing my updates then setting it but it didn’t work.

As a transitional software engineer I’m having a bit of a problem mapping the concepts of things happening outside the code to the code itself though so I’m unsure of best practices. Any thoughts are appreciated.

Hi @Scott_Turnbull, I would be happy to have a look at the code you said you tried but didn’t work, just as another pair of eyes looking at it etc.

Regarding the other events that are happening, I saw this quite some time ago and it reminded me very much of a similar diagram (scroll down a bit) for the .Net events, which I thought was really useful. I was going to post this the other day, but with everyone at different levels I didn’t want to put anything up that was perhaps too complex or out of the scope of the course - too much - not entirely sure whether it will help massively, but might give some indication as to other things that are going on.

https://docs.unity3d.com/Manual/ExecutionOrder.html

…if nothing else, it’s a pretty diagram that looks awesome next to your computer :slight_smile:

Again, happy to have a nose at a bit of code if you want to share etc…

Thanks. The Exec order makes sense.

I recreated the examples here in a GIST https://gist.github.com/Streamweaver/29327ae77e890753f487f276cce48620

The 2nd code works, I must have had a bug when I tried it earlier.

The question is still in my mind, though I know I’m getting ahead of myself here. Best practice wise I’m wondering any of this matters.

In the first I issue several updates to the transform that are being overwritten at the end of the method and potentially creating a problem by giving the object an invalid position until the Clamp method corrects it. This seems like it could introduce bad behavior and I’m wondering about efficiency.

In the second I do all calculations in a locally scoped Vector3 in the method and only update the object transform at the end when I have a valid position. This avoids the problems above but I would think has the problem of adding a bunch of intermediate objects that have to be garbage collected. On a single player object, insignificant, but if we extend this to lots of enemy objects I’m wondering if it has impact.

This is a curiosity at this early stage mostly so no big deal. I’m just wondering if there’s a best practice?

1 Like

I see what you’re saying @Scott_Turnbull - I personally prefer the second listing, I think it’s clearer and tidier - but I do not have enough experience to suggest whether it is more or less beneficial from a performance perspective.

In the first example, it feels like the transform.position is just being updated because, well, we need somewhere to hold this info, and this already exists but we know it won’t do anything with it yet until the end, so why not. I think that to a degree is less clean and perhaps a bit harder to read - where-as the second listing isn’t.

In the second example you create the one extra Vector3 which should go out of scope, I believe, once the code has finished running in those brackets, how quickly that all gets tidied up by the GC I would argue is anyone’s guess. As you said, might not be significant until it was happening a lot of times…

I was actually writing something similar to this myself yesterday, I had broken things down a bit into separate methods and when I got to the end I thought, whilst its readable and easy to understand, maybe just returning a new Vector3 here would be the better thing to do… I think I personally would lean towards this though and your second listing.

I guess you could run up a test and spawn in 1000s of object to what performance and see what happens. Maybe moving the moveTo Vector3 out of the method and having it at a class level so that it didn’t have to keep creating a new one and getting rid of it, just re-use the existing one etc might help too? Just thinking out loud (which some how took control of my finger tips!)

I agree that it seems inefficient, though I am not familiar enough with Unity to say for sure and if so, by how much. That said, I did change the update to both prevent setting of the transform every frame regardless, and also to prevent setting it to the our of bounds value before clamping it and setting it again to an in-bounds value.

Hello everyone,
I got interested in this topic and created a unity project to test it out

Project that I used to test it

I used the StopWatch class from the System.Diagnostics library to test it out, these methods allow us to check how long it takes to make something so we can compare the performance of diferent codes.

The script I’ve done is the following, the way I done it we can change the method inside the void CheckingTime() in order to test the diferent methods:

using UnityEngine;
using System.Collections;
using System.Diagnostics;

public class KeyDown : MonoBehaviour {
	float MovementSpeed = 0.001f;
	int xMin = -8;
	int xMax = 8;
	int TimeDiagnostic;

	void Update () 
	{
		// Pressing Space to start the test
		if (Input.GetKeyDown(KeyCode.Space))
		{
			CheckingTime(); // You should test holding left arrow and right arrow too
		}

	}


	void CheckingTime() // Method used to check the time to perform the method
	{.
		Stopwatch timer = new Stopwatch(); //declare the timer
		timer.Start (); // starts the timer

		Translating();     // method that I want to check <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CHANGE IT TO TEST OTHER METHOD

		timer.Stop ();  // stops the timer
		TimeDiagnostic = timer.Elapsed.Milliseconds; // Converting the timer to integer
		print ("The time to load this method a Hundred thousand times was " + TimeDiagnostic + " milliseconds" ); // Output
	}



	void JohnnyMoving1() //Using if statement instead of mathf.clamp (keycode as first parameter)
	{
		// Repeat the method 100,000
		for (int i = 0; i < 100000;i++)
		{
			if (Input.GetKey (KeyCode.LeftArrow) && transform.position.x >= xMin)
			{
				transform.position += Vector3.left * MovementSpeed * Time.deltaTime;
			} 
			else if (Input.GetKey (KeyCode.RightArrow) && transform.position.x <= xMax) 
			{
				transform.position += Vector3.right * MovementSpeed * Time.deltaTime;
			}
		}
	}


	void JohnnyMoving2() //Using if statement instead of mathf.clamp (min/max as first parameter)
	{
		// Repeat the method 100,000
		for (int i = 0; i < 100000;i++)
		{
			if (transform.position.x >= xMin && Input.GetKey (KeyCode.LeftArrow))
			{
				transform.position += Vector3.left * MovementSpeed * Time.deltaTime;
			} 
			else if (transform.position.x <= xMax && Input.GetKey (KeyCode.RightArrow)) 
			{
				transform.position += Vector3.right * MovementSpeed * Time.deltaTime;
			}
		}
	}


	void JohnnyMoving3() //Using double ifs instead of mathf.clamp (keycode as first parameter)
	{
		// Repeat the method 100,000
		for (int i = 0; i < 100000;i++)
		{
			if (Input.GetKey (KeyCode.LeftArrow) )
			{
				if (transform.position.x >= xMin)
				{
					transform.position += Vector3.left * MovementSpeed * Time.deltaTime;
				}
			} 
			else if (Input.GetKey (KeyCode.RightArrow)) 
			{
				if (transform.position.x <= xMax) 
				{
					transform.position += Vector3.right * MovementSpeed * Time.deltaTime;
				}
			}
		}
	}


	void ScottMoving1() //Using Mathf.Clamp and setting the newX variable
	{
		// Repeat the method 100,000
		for (int i = 0; i < 100000;i++)
		{
			if (Input.GetKey (KeyCode.RightArrow)) {
				transform.position +=  Vector3.right * Time.deltaTime * MovementSpeed;
			} else if (Input.GetKey (KeyCode.LeftArrow)) {
				transform.position +=  Vector3.left * Time.deltaTime * MovementSpeed;
			}
			float newX = Mathf.Clamp (transform.position.x, xMin, xMax);
			transform.position = new Vector3(newX, transform.position.y, transform.position.z);
		}
	}


	void ScottMoving2() //Using Mathf.Clamp and setting the newX variable and moveTo variable
	{
		// Repeat the method 100,000
		for (int i = 0; i < 100000;i++)
		{
			Vector3 moveTo = new Vector3 (transform.position.x, transform.position.y, transform.position.z);
			if (Input.GetKey (KeyCode.RightArrow)) {
				moveTo +=  Vector3.right * Time.deltaTime * MovementSpeed;
			} else if (Input.GetKey (KeyCode.LeftArrow)) {
				moveTo +=  Vector3.left * Time.deltaTime * MovementSpeed;
			}
			float newX = Mathf.Clamp (moveTo.x, xMin, xMax);
			transform.position = new Vector3(newX, moveTo.y, moveTo.z);
		}
	}


	void Translating() //Using Translate to move the object
	{
		// Repeat the method 100,000
		for (int i = 0; i < 100000;i++)
		{
			if (transform.position.x >= xMin && Input.GetKey (KeyCode.LeftArrow))
			{
				transform.Translate(Vector3.left * MovementSpeed * Time.deltaTime,Space.World);
			} 
			else if (transform.position.x <= xMax && Input.GetKey (KeyCode.RightArrow)) 
			{
				transform.Translate(Vector3.right * MovementSpeed * Time.deltaTime,Space.World);
			}
		}
	}


}

I tried both @Scott_Turnbull codes and others 4 methods that I made to try it out, unfortunately I was unable to test @Mochnant code, since the link to his repository isn`t opening here. I’ve worked with the codes inside a loop of 100,000 repetitions triggered by the spacebar instead of using them inside the upload (I felt it would be easier to test), I tested the performance in five different situations: With the paddle stopped, with it moving to right and left and with it struck at the right or left side while trying to move to those respective directions, and the result was the following:

AVERAGE MILLISECONDS TO LOOP THROUGH METHOD 100,000 TIMES:

**Scott Moving1:** Using Mathf.Clamp and setting the newX variable
Stopped:  82 
Moving Left: 152
Moving Right: 155
Struck at left side: 182
Struck at Right side: 174



**Scott Moving2:** Using Mathf.Clamp and setting the newX variable and moveTo variable
Stopped: 83.5
Moving Left: 136
Moving Right: 126
Struck at left side: 109
Struck at Right side: 105



**Johnny Moving1:** Using if statement instead of mathf.clamp (keycode as the first if parameter)
Stopped: 15.5
Moving Left: 98
Moving Right: 107
Struck at left side: 31.5
Struck at Right side: 31.5



**Johnny Moving2:** Using if statement instead of mathf.clamp (min/max as the first if parameter)
Stopped: 47.5
Moving Left: 97
Moving Right: 122.5
Struck at left side: 39.5
Struck at Right side: 39.5


**Johnny Moving3** Using double ifs instead of mathf.clamp (keycode as the first if parameter)
Stopped: 15.5
Moving Left: 98
Moving Right: 107
Struck at left side: 24
Struck at Right side: 31.5


**Translating:** Using transform.Translate instead of transform.position +=, and if instead of mathf.clamp
Stopped: 47.5
Moving Left: 98
Moving Right: 125
Struck at left side: 39
Struck at Right side: 39

My conclusions:
*Seems that @Scott_Turnbull second method with the Vector3 moveTo runs faster than the first one;
*Incremente position with transform position += is way faster than using transform.Translate in this situation;
*The order that you put the if statements inside the format if ( comparisonX && comparisonY) has a great impact in the performance, you should always use the lighter one first;
*Using more thqn one if in ladders instead of using them inside the same line seems to improve the run speed of the first possible possibility, which seems to be a little strange, further testing should be done;

*The biggest impact was: using if statements instead of mathf.clamp seems to improve the performance of the paddle, since it does only the necessary comparison instead of always comparing the min and max value (you already tell which one you should compare when you press LeftArrow or RightArrow).

Well, those are the results of my experimentation, please feel free to download the project and experiment with it too, share the conclusions too. If you do, you may need to increase the loop to 1,000,000 , the computer I used to test it is very weak.

I also prefer the 2nd code. When I was watching the video I was exactly thinking: “Why we update the position twice? It’s not better to do all the calculations before moving the playerShip?”.

Just one thing with your code. In the MoveShip() method:

Vector3 moveTo = new Vector3 (transform.position.x, transform.position.y, transform.position.z);

can be simpler written as

Vector3 moveTo = transform.position;

1 Like

Privacy & Terms