11
\$\begingroup\$

Im making more games and asking more stupid questions.

Hopefully this one is very brief. I'm making a very basic class which just moves a Player object by applying force to a rigidbody, but it got me wondering, should I be making a class reference to the rb or just a local variable inside Update every frame? (bearing in mind it already exists in the Monobehaviour.GameObject unity parent class).

I'm wondering if doing many local variables would slow down the loop as a whole(by local I mean inside the function itself and not at the top of the class - hope im using the correct term) .

Here is what I mean, the two ways I was thinking of doing it:

public class Player : MonoBehaviour {

private void FixedUpdate()
{
    Rigidbody rb = GetComponent<Rigidbody>();

    float v = Input.GetAxis("Vertical");

    rb.AddForce(v * rb.transform.forward * Const.walkForce);
}
}

or...

public class Player : MonoBehaviour {
Rigidbody rb;

private void Awake()
{
    rb = GetComponent<Rigidbody>();
}

private void FixedUpdate()
{
    float v = Input.GetAxis("Vertical");

    rb.AddForce(v * rb.transform.forward * Const.walkForce);
}
}
\$\endgroup\$
  • 4
    \$\begingroup\$ This seems more like a question for code review SE. \$\endgroup\$ – Orphevs Jun 11 '18 at 8:17
  • 1
    \$\begingroup\$ You could also make float v a class property. \$\endgroup\$ – Chad Jun 11 '18 at 15:39
  • \$\begingroup\$ I probably will in the end actually Chad, because I will use it in two methods within the class. It was really just simple code to get the question about local vs class variables across without bloating it out with lots of code. I'm really liking keeping the variables local (and 'out of the way' as it where!) but of course I must make sure I dont start duplicating things instead of having one class variable for each. \$\endgroup\$ – Big T Larrity Jun 11 '18 at 16:22
  • \$\begingroup\$ A snippet of code is not 'a code'. Asking about 'which code is better' makes you look like an amateur. \$\endgroup\$ – Miles Rout Jun 14 '18 at 0:46
  • \$\begingroup\$ i am an amateur so thats fine with me thanks Miles \$\endgroup\$ – Big T Larrity Jun 15 '18 at 1:52
25
\$\begingroup\$

In general, I've found that you should scope data as narrowly as possible to start with and broaden the scope as you discover you need to. That means if you can make it a local variable instead of a member, you should probably start there.

This is because a narrower scope reduces the number of places in the code where you must reason about that data, and thus reduces the number of places you can reason about it incorrectly (which leads to bugs).

Actually declaring local variables alone is never going to practically be a performance concern: they're cheap, and modern compilers may even optimize "extras" out.

Initializing them may be a concern; in your case your rigid body is looked up every time via GetComponent, which has some non-zero cost. If you know that the rigid body component will not change for this particular game object, you can look it up once and store the result in a member variable and avoid a small bit of per-call overhead.

It's unlikely that per-call overhead is going to be significant in this one instance, but over time doing this for many components may add up. You'd want to apply a profiler to be sure.

So, to summarize: in general, default to making things as local as possible, but in this case it's probably reasonable to make rb a member so you can perform the lookup once in Awake and avoid having to do it every FixedUpdate call.

\$\endgroup\$
  • 1
    \$\begingroup\$ thank you Josh, I did feel it would be better having the rb initialized just once , and thanks very much for clearly explaining why. It's nice to know for sure, you see i wasnt 100% sure that the GetComponent counted as initializing it each time, or simply 'looking it up' from the Monobehaviour. But now I get it so many thanks for the super-quick helpful answer \$\endgroup\$ – Big T Larrity Jun 10 '18 at 16:29
  • 1
    \$\begingroup\$ @SuperMegaBroBro GetComponent is pretty fast - you'd be hard pressed making any kind of lookup that has comparable speed. Unless you have performance data that suggests it's a problem, stick with the "keep things local" approach. You can always optimize it later. See answers.unity.com/questions/185164/… Don't forget that caching isn't free either - if you cache every single GetComponent in your game, it'll probably be a net loss. Measure. Identify where optimizations are worth it. Focus on making a great game :) \$\endgroup\$ – Luaan Jun 11 '18 at 6:56
  • 1
    \$\begingroup\$ The main gist you should take away from this answer (which is pretty good) is: If you want to go for code that is most easy to reason about, to test and to ensure that it is bug-free, take the local variable. But, as always, we sometimes need to make sacrifices to perfect code for performance reasons, so a class member might be appropriate (but profile first, don#t simply do premature optimization). \$\endgroup\$ – Polygnome Jun 11 '18 at 8:25
  • \$\begingroup\$ thanks very much guys, especially Luaan as I was wondering about that 'caching' issue whilst i wrote the original question (but i didn't really know how to word that). I will certainly take the 'local' approach as much as possible. thanks for all the great help as usual fellas. \$\endgroup\$ – Big T Larrity Jun 11 '18 at 13:53
2
\$\begingroup\$

Josh Petrie's answer is very good - here's a complementary perspective.

When you work with code which you understand the domain of fairly well, you can scope variables to where they make sense.

For instance if I have a Person class with a waveGoodbye method, it's possible that my class didn't have a hand object before and so I could declare it locally in waveGoodbye. Now here it's obvious a person has a hand so you might naturally declare it as a member of Person without thinking about it, but the same issue applies.

If you declare hand locally then later you might add a karateChop method which also requires a hand. This is when declaring variables locally can become problematic - because junior developers often fall back to copy/pasting the declaration from waveGoodbye and now you have the same concept located in two spots. Any changes to hand then need to be modified in both places and thus begins a ant bug hill.

So all in all,

  1. If you're confident in the placement of your declaration, scope it to where it makes sense to.

  2. If you're not confident, then start local and make sure to refactor when you get a whiff of code reuse.

Edit: Oh and don't make the same mistake I have of spending too much time figuring out where to scope your declarations. It's difficult to understand the structure of your data up front, and as you write your code the structure has a way of revealing itself.

\$\endgroup\$
  • 2
    \$\begingroup\$ Also having things naturally ordered, like every Person should have the member hand if its relevant in the application, often helps other developers (most times you 6 months later) to better understand the code. In this case, it seems logic to have a Rigidbody as member of your Player and, I say more, even if that implies worse performance. In video-games that is a relevant point to consider, but I think in code, to be clear is often more important \$\endgroup\$ – alseether Jun 11 '18 at 15:07
  • 1
    \$\begingroup\$ excellent answers thanks guys. I'm not trying to sound like an know-all, but tbh all these answers have just re-affirmed what I was thinking already, so for once I'm very happy and making some nice progress in my games. Right now I am making a 3d football game with proper animations and everything, making real strides :D soon I will be here needing help about how to make it 2-player and dare i say online mulitplayer :D have fun guys and thanks again! \$\endgroup\$ – Big T Larrity Jun 11 '18 at 16:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.