République Migrates From Unity 4 How we moved our project from Unity 4 to Unity 5 in the midst of a chaotic, 20-person project, starting with our initial investigation.

Dev diary #2 République Migrates From Unity 4 to Unity 5

Our first big step in getting started with Unity 5 was migrating our project over from Unity 4. For our 20-person team, this was a huge endeavour that required a lot of planning. Your project may not be as big as ours, but hopefully you can learn from our experience. We started by creating a new copy of our project in a separate Perforce branch. This branch served as an intermediary version of the project – the main project was broken for a few days, while we worked to fix compiler errors caused by plugins or our in-house code that didn’t work in Unity 5 (more on this later). Creating this separate branch allowed our artists and designers to continue working in the Unity 4 version of the project without getting blocked, while we got the Unity 5 version in working condition. In addition, we made a stripped-down version of the project that we moved into Unity 5 a few weeks before our team migration. This was used by the artists to develop and test our own pipeline for Unity 5’s physically based shading. Stripping it down meant tearing all our 3rd party plugins out -- anything we were concerned could impede progress. Once we had the Unity 5 branch ready and problem-free, we moved the rest of the team over to it and archived the old branch. We recommend branching your project if you’re using source control (actually, we highly recommend using source control, period!). Either way, make sure you create a backup of your project before going to upgrade it to Unity 5. Our UI designer and source control guru, Charlie Helman, was responsible for our team-wide migration, and he was gracious enough to lend us notes and screenshots on the process. Here’s our shiny new branch, created from our existing Unity 4 project. République’s Unity project is enormous compared to most other mobile games. Weighing in at 45.7GB, the project can take 24 hours to import on some machines. Utilizing Cache Server brings this time down to about 30 minutes. Before opening our project, we want to make sure Cache Server is set up in the Unity 5 preferences. Now is also a good time to make sure we set up our other preferences for things like external code and image editors (Visual Studio or Photoshop, for example). To do this, launch Unity 5 and create a new empty project. Once the new project is open, we head to the Preferences (On Mac: Unity > Preferences; On Windows: Edit > Preferences) On the Cache Server tab of the preferences, I made sure “Use Cache Server” was checked, and set the IP to our cache server’s address (which is simply “server”). After verifying it worked via “Check Connection”, I closed down the project. Now we’re ready to open the real thing up. After a few minutes, we see the import progress bar. Periodically, we see this window. The Unity 5 API features a good collection of changes, one of which being all “shortcut” references (like .rigidbody or .transform) that were previously doing a .GetComponent<T>() call under the hood now need to explicitly call GetComponent. So, myCharacter.rigidbody now needs to be myCharacter.GetComponent<Rigidbody>() . Unity’s ripped out all the shortcut references, and they’ve made an automated process to help do this for you. When we select “I Made a Backup. Go Ahead!” Unity runs that process. Unity 5’s AutoUpdater is pretty awesome - ideally, it will be able to parse through your entire codebase and make the proper updates. Speaking of which, let’s examine some of the script changes in Unity 5, and how Camouflaj made these work for us.

Script Updates in Unity 5 In Unity 5, we saw several name and API changes to scripts. Generally the obsolete attribute was added to the old names, so the compiler would give advice about what to use instead. In some cases, however, it is not merely a name change: you might need to consider between multiple alternatives. For example, the AnimatorStateInfo nameHash was replaced with both shortHash and fullPathHash. The short hash is the name of the state without the layer or state machines prepended, which is more useful in most of our cases. If you need to differentiate states that have identical names, but are in different state machines or layers, then you’ll want to use fullPathHash. If you want to maintain backwards compatibility with previous versions of Unity in your script, you can use the Unity version macros. We preferred the approach that treats unknown versions of Unity as 5 or later. That way, it’s less likely that we’ll have to revisit them in another upgrade. Here’s an example for allowing code specific to different Unity versions to coexist. #if (UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_3_6 || UNITY_3_7 || UNITY_3_8 || UNITY_3_9) #elif (UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9) #else #endif

Shader updates The shader compiler has been switched from Cg 2.2 to HLSL in Unity 5, and you’ll discover that it has some stricter requirements. With HLSL, output variables that aren’t completely initialized will cause a compilation error. There were many times where we hadn’t initialized the alpha in a color, or the w in a position. To address this, we initialized these values to zero, but as an alternative you can use the macro UNITY_INITIALIZE_OUTPUT. In some cases, we also found that we had completely unused data being passed from the vert to the frag which we removed from our v2f structs.

Manual Edits made beyond Script Auto Updater One of the things we had to manually repair in several instances were places in code from where we we were trying to retrieve components by name and not by type, which Unity no longer allows. During the Script Automatic Updater process, Unity converts these older instances of component retrieval by type name from: GetComponent(“MyMonoType”) to: UnityEngineInternal.APIUpdaterRuntimeServices.GetComponent(go, "Assets/PathToMyMonoType/Scripts/MyMonoType.cs (207,8)", "MyMonoType") You’ll probably see a few of these conversions littered through the console as errors, because these updates technically don’t compile. The main offenders of this type of component retrieval were in third-party plug-ins, since we try our best to be as type-safe as possible within Republique’s codebase. However, in some of our older AI code, we had set up our GOAP system in such a way that Actions and Goals could be specified in a dynamic list format, which referenced MonoBehaviors by name. This allowed us the flexibility of a dynamic runtime configuration, but it wasn’t going to work with this new component retrieval restriction in Unity 5. In this particular case, we didn’t need to have the underlying Action and Goal types derive from MonoBehavior so converting them to just standard classes solved our problems. For other cases where we needed to manually fix this kind of type retrieval, we were able to work around the issue using a new scheme for getting types by name like so: System.Type.GetType("MyMonoType")