Monday, December 9, 2013

A few tips for Unity I learned while porting Flower Garden (1/2)

Using two versions (or more) of Unity on your computer


By default, when you install a new version of Unity, it replaces the current one. But it is actually possible to keep more than one version on your computer, which is useful if you're working on a project and don't want to upgrade immediately, while still being able to try and test the latest features. For example, when I was finishing to port Flower Garden to Android, and getting ready to publish it, I kept using 4.1.2 since it was stable and the next versions introduced some bugs I could not work around; but at the same time I was also getting those versions, to check if there were some improvements the game could benefit from in the future, and verify what the status was on those aforementioned bugs.

Keeping several versions is not difficult: before installing a new one, you just have to rename the folder containing the current one, et voila. For example, on Windows 7, I have the older version in c:\Program Files (x86)\Unity4.1.2, and the newer one in c:\Program Files (x86)\Unity. To run the current version, I use the usual Unity icon; to launch the old one, I suppose I could create another icon, but I got into the habit of opening a command window, navigating to the desired folder, and launching Editor\Unity.exe.

By default, Unity reloads the last project you worked on; but if that was a 4.1.2 project, and you're launching 4.3, you don't want this behavior since your project would be converted to 4.3 and you may not be able to load it in 4.1.2 anymore. Fortunately, Unity warns you about it. One solution is to specify what project you want to open, like this:

Editor\Unity.exe -projectPath myProject

Or, you can also use a project name that does not exist, and Unity will bring up the Project Wizard dialog. However, the easiest way to solve the issue is to go to Edit\Preferences\General in Unity's menu, and check Always Show Project Wizard once and for all.


When you go back and forth between versions of Unity and projects like this, it is possible that after loading your scene looks empty and your hierarchy tab only contains the Main Camera; do not panic, somehow the scene selection got lost, and you just have to find and double click your scene in the Project tab to bring everything back to normal.


Version control


As soon as you start working on a project, you should put it under version control, so that you can go back in time and check the changes you made even weeks ago if something goes wrong, or merge with other people's changes if you're part of a team. In its initial configuration, Unity is very unfriendly with external version control systems (by "external" I mean: anything other than the Unity Asset Server, which is a separate paid product): it creates lots of binary files in the Library folder, mostly under Library\metadata, and there is no way to know which ones need to be checked in. Even if there was, that wouldn't be very useful, since binary files cannot be merged, and looking at differences tells you nothing.

Fortunately, some version of Unity introduced a setting that will make your life easier: go to Edit\Project Settings\Editor, and in the inspector, under Version Control, select the Visible Meta Files mode. Ideally you should do that before you create anything in your project, otherwise you probably want to quit Unity so that the magic happens. Basically, once you're in this mode, every file under the Assets folder will generate a new file of the same name with an additional ".meta" extension, that contains some information about the corresponding asset. For example, the values of the parameters you can edit in the inspector for one of your textures. These meta files are text files, therefore it becomes possible to diff and merge them.


This is great for most assets, but it's not a silver bullet: scenes and prefabs are still stored in binary files, and cannot be merged if two people modify them around the same time. For more information, see the following links:


Scripting and rendering


If you load resources with Resources.Load, you need to know it doesn't like backslashes in its file path, only forward slashes should be used. This is especially annoying on Windows, where backslash is the character used by Path.Combine to concatenate strings. Therefore, don't forget to do something like: string filename = Path.Combine(ContentRoot, name).Replace("\\", "/");

Since the geometry in Flower Garden is procedurally generated, and although it could still have been stored in game objects, I didn't feel the need to use mesh renderers and mesh filters: instead, I call the lower level Graphics.DrawMeshNow function, that takes a mesh and a matrix. Since I was taking that path, I thought I could do all the processing and rendering of a single frame from the Update function of my main script, pretty much the same way it works in the iOS version. That's not the case though: rendering doesn't work in Update, because the camera hasn't been set up yet and the frame buffer cleared, etc. OnRenderObject was the right place for me, OnRenderImage and OnPostRender are also worth checking out if you're thinking of doing some "manual rendering".

At some point, I wanted to apply a specific change to all the textures in my project, and I thought: Unity sure knows who they are, and has all the code needed to load them, so why not write a C# script, attach it to the main camera, and launch the game in the editor once to run the code I'll put in the Start function? That sounded clever, until I realized most of my textures were compressed and read only (most of yours probably are too), and I needed them uncompressed and writeable in order to process them. No problem, a quick search in the forums told me how to retrieve the corresponding texture importers, and change their properties before loading the textures the way I wanted them. Problem is: when you do that, you don't work on copies of the importers, you really modify "the real thing", and after happily running the game, you soon notice you lost all your texture compression settings. So: be careful, and use version control so that you can revert those unwanted changes.

Shaders in Unity are a strange beast, at least at first. I've written simple shaders before, in assembly and HLSL, but when I started looking for examples to do it in Unity, it seemed like there were 3 different ways to do it, which I found confusing. My understanding is: you can use commands to rely on color combiners and the fixed function pipeline, or write Cg code, or use some kind of meta programming that will allow your shader to work correctly with Unity's lights in both rendering modes (forward and deferred). Anyway, the thing I want to point out is a bunch of rendering states (for example: writing to the z-buffer or not, changing its test mode, setting the blending mode, back-face culling, etc) are hard coded in the shader, and I did not find a way to turn them into parameters I could set from script. I can see why it was done like this, but it means each time I want to reuse a shader with one or more rendering states having a different value than before, I need to duplicate the shader. Not a huge deal in my case, but that's how Flower Garden ends up having 23 shaders (which I'm sure you would not guess by looking at it).

When assigning data to a Mesh, it seemed using null was not a good idea (I haven't verified it recently, this may have changed). For example, if your data doesn't contain texture coordinates, don't do mesh.uv = null; (that would trigger an error), just leave that array alone. Same thing for normals, etc. I can only assume mesh.uv (in my example) references a zero-sized array by default, and replacing it with null prevents the engine from getting its length, or something like that.


Making builds


Creating builds may fail on some platforms if some of your files are read only (for example, if you're using Perforce for version control, and haven't checked those files out). The one I know of from the top of my head is AndroidManifest.xml, that gets copied to Temp\StagingArea during the build and needs to be writable to get modified.

Code stripping is not as smart as you may think. This Pro-only feature, found in the Player Settings under Optimization | Stripping Level (for some platforms only), is meant to reduce the size of your executable (and therefore the size of the file people will download to play your game) by removing some libraries your script code is not using. Ideally, this would mean that after the compiler has done its job, the build process would figure out what classes or dll's are required, and get rid of the rest (which is pretty much was this page says:
http://docs.unity3d.com/Documentation/Manual/iphone-playerSizeOptimization.html).

In reality, at least on Android, this is not what happens: it looks like a predefined list of classes, that are supposed to not be used very often by most developers, is simply excluded from the build, without any further test. I believe encryption classes are part of this list, and HttpWebRequest most definitely is: when I tried to enable code stripping in Flower Garden, the game would crash as soon as a script tried to use this class, that could not be found anymore (and no, I am not accessing it through reflection). One problem is: I don't use that class a lot, therefore I did not notice the crashes immediately, and when I got one it took me a while to realize code stripping was the culprit. No compiler error, no error when building, it's nasty. When I finally understood the issue, I had 2 options: disable code stripping, or rewrite the scripts that used the missing class in a different way; since I was getting close to publishing the game, and had 100 other little things left to do, you can guess which solution won.

Since then, I learned there is a third option: you can create a link.xml file under the Assets folder, and specify in it some namespaces that should be included in (or: not excluded from) the build. I haven't tried it yet, which is why I'm borrowing the encryption example I found (warning: use at your own risk), instead of following up with HttpWebRequest. It's good to have this workaround, but it still looks to me like I'm doing the job of the computer, that should be able to figure these things out much better than me.

<linker>
 <assembly fullname="mscorlib">
 <namespace fullname="System.Security.Cryptography" preserve="all"/>
 <namespace fullname="System.Security.Cryptography.X509Certificates" preserve="all"/>
 </assembly>
</linker>



To Be Continued...

2 comments:

  1. Nice tips! Couple clarifications:

    On version control. By default files like prefabs & materials are binary, but there's a setting to make them be text format right there in editor settings ("Asset Serialization").

    Shaders and controlling states like blend mode etc. from variables: that was implemented in Unity 4.3. You can do "Cull [MyCullModeVariable]" for example and do material.SetInt ("MyCullModeVariable", x) from a script.

    ReplyDelete

Note: Only a member of this blog may post a comment.