Tuesday, February 26, 2013

The Unity Way, or....THE UNITY WAY!

When you use a complete "out-of-the-box" game engine like Unity, you're going to be forced into using it in ways that you may not like. Seems obvious, right? You're going to have to do things that may seem counter-intuitive and just deal with the design decisions made by the Unity team. You"ll need to do things "The Unity Way". If you're coming from a different game development environment, regardless of what it is (C++/DirectX/SDL, XNA, etc), there's going to be a great learning curve when starting with Unity. 

A good example of this is how you instantiate prefab objects in a scene. Instead of using the new operator to instantiate a new object (or making use of a factory method), you instead must make use of Unity's Instantiate method. Now there is some sound reasoning for this. When you instantiate a prefab, you're actually instantiating all of the multiple components that make up a prefab as well. The Unity documentation explains it well. You can think of the Instantiate method as a somewhat non-traditional factory method that creates and returns your complete prefab instances, so that you don't have to "new" up the code and piece together the prefab by hand.

There is a downside to this. What if you want to change a script parameter of the prefab at instantiation? Hmmmm..., the Instantiate method doesn't support parameter passing, unlike a traditional constructor would. Instead, after you instantiate the prefab, you have to obtain a reference to the attached script, and modify any fields through setter methods. 



The above Gist is from my Enemy script. My enemy prefabs travel at a random velocity. Because of this, I need to be able to adjust the speed at which the bullets they fire travel at as well; I was running into scenarios where when the enemy fired a bullet, they both might end up traveling at the same, or near the same velocity. Instead of simply passing speed + BULLET_SPEED_MULTIPLIER as a constructor parameter upon instantiating, I need to do things "The Unity Way". 

So I do things "The Unity Way" and I change the fields of my script through setters instead. Not the end of the world. OR IS IT?!?!?! When I went to test my BULLET_SPEED_MULTIPLIER, I noticed that the bullet speeds were unaffected. Why? Because I was setting the enemy bullet speed in the Start method of my EnemyBullet script.



If you read the Start documentation carefully, you'll find out that "Start is called just before any of the Update methods is called the first time". It seems that the execution path is essentially (ignoring several other methods being called in between, I'm sure):

fireBullet -> Instantiate -> setSpeed -> Start -> Update 

The lesson of the story is to be careful of what fields you set in your Start method, because that's the value that will be used regardless if you used a setter method immediately after instantiation. It could be argued that this bug would have happened even if I was using a constructor, because Start would have gotten called by the Unity engine later on anyways. I would then counter argue that I probably wouldn't have even used Start to initialize key variables, if I was using a constructor ;)



No comments:

Post a Comment