Search Unity

Unity 5: API changes & automatic script updating

June 23, 2014 in Technology | 6 min. read
vanessa_morgan_photo
vanessa_morgan_photo
Topics covered
Share

As the breadth of the Unity feature set keeps on growing, and as we build it with more and more engineers at the same time, the necessity for being able to split up the product in separate modules becomes bigger and bigger. Obviously because it is just sound engineering practice. (It's nice that when you change something in the physics code, that your audio doesn't break). It also gets us closer to a situation where we could ship updates to different modules at separate timelines. And it's nice that it gets easier to work on the different components with different teams. There's one additional reason that is much more user visible: the ability to ship a smaller game. Don't use particle systems? Don't pay for them. Don't use 2d rendering, Don't pay for it. If you take a look at the WebGL demos that we posted, you can see that the entire unity engine that is converted to javascript (not manually thankfully!) clocks in at about 3mb of compressed javascript. That's already pretty good, but we know there's more juice to squeeze out of that. The web is still a platform where size matters a lot, and we want to bring the minimal size of a Unity published WebGL project down further. This is also true for other platforms, but at the moment, WebGL is definitely one of our more size-conscious platforms. So, with many good reasons to modularise the way we write code, test code, and ship code, we've started work down that path. There is still a very very long way to go, but if you don't start you're not going to get there either, so we started. While doing mostly non-user-visible work on this, we hit some dependencies in our API that need to be cut in order for us to proceed further in our efforts. Let's look at that API problem: Many moons ago, when we started building our API, the C# code required to do something with a renderer component looked like this

void Start()
{
  Rigidbody r = (Rigidbody)GetComponent(typeof(Rigidbody));
  r.velocity = Vector3.up;
}

We disliked the verbosity of that so much, that (in addition to making a new .NET language unityscript-aka-javascript with much less boilerplate), we also added quick component property getters for commonly used components:

void Start()
{
  rigidBody.velocity = Vector3.up; //"rigidBody" calls: UnityEngine.Rigidbody UnityEngine.Component.get_rigidBody()
}

Over time, we've been inconsistent in wether or not we added these quick accessors to the API for new components. For some we did, for others we didn't, and by now it's not clear why some components have them and others not. These accesors are also not nice for modularisation. When you modularise, you want to make sure that all the dependencies "point in one direction". It's okay for the physics module to talk to things in the core module, but it's not okay for the core module to talk to the physics module directly, because it might not be there. Turns out that is exactly what we have here. We have the MonoBehaviour class, which is something that we want to put in a "core" module. but it has a property getter implementation for .rigidBody that returns a RigidBody object, which should obviously live in the physics module, that the core module shouldn't be able to reference. For Unity5, we decided to bite the bullet, and remove all the quick component property getters with the exception of .transform (since that one belongs in the core module, there's no need for it to go, and it's also by far the most used one) [1][2]

As I wrote in a previous post, one of the hardest things of making Unity is finding the balance between "making things better" and "not breaking projects that already work". Since we feel that modularisation is very important in the long run, we decided to remove the .rigidBody style accessors, but we were not comfortable with the amount of pain that would bring our users. So we embarked on a project to reduce the user pain introduced by this work: Automatic Script Updating

Unity5 will ship with (unless something really bad happens) the ability to detect when your scripts use old API, and is able to, after asking for your permission, rewrite that code to use the new API. So if you have a script that does this:

void Start()
{
  rigidBody.velocity = Vector3.up;
}

We will convert that for you to:

void Start()
{
  GetComponent<Rigidbody>().velocity = Vector3.up;
}

The script updaters work for C#, UnityScript-aka-Javascript, Boo, and prebuilt .dll assemblies. (We actually read the IL code inside the prebuilt assemblies using the wonderful Cecil library by Jb Evain of UnityVS fame, detect usages of deprecated API, and then rewrite the IL code to use the new API instead). Perhaps unsurprisingly the feedback from early Unity5 testers was "hey, there is a script updater now, and it fixed all my quick property accessors, but there are more API changes you guys did that it did not fix", and we find ourselves in the kind of good problem to have situation where it looks like the updaters work well enough that it makes sense to look at the more minor API changes that we've done and see which ones of those we can update reliably, and which ones we cannot. Maybe one day we could even open this up to asset store vendors, and allow them to provide "rewrite rules", so that they also gain increased flexibility in improving API, fixing old mistakes while keeping user pain at a minimum. Should you have become accustomed to the component accessors to the point that you really really want to keep them anyway, it's trivial to write a MyMonoBehaviour class with all these properties in it, that you make all your scripts derive from. We hope this approach strikes a good balance between not putting too much work on the plate of our users, while still being able to improve API, fix mistakes, and make Unity better.

Bye, Lucas (@lucasmeijer)

 

[1] An alternative approach would have been to introduce extension methods. This is basically the physics module being able to add additional methods to the MonoBehaviour class. This would mean user code would have to change from

.rigidBody

to

.RigidBody()

We felt that was hard that was not a very attractive alternative. Hopefully one day C# gets extension properties. Should those have existed, we would have been able to keep source-level compatibility and still move the rigidBody code out into its own module.

[2] in Unity5 we also cache the transform component on the c# side, so there should no longer be a performance reason to cache the transform component yourself.

June 23, 2014 in Technology | 6 min. read
Topics covered