Tips For Working With Unity #2: Asset Workflow
filed in Open Development, Unity on Sep.14, 2010
Last time I talked about what parts of a Unity project need to go into source control, and alluded to some issues that can crop up due to the way that Unity works. Today I’ll delve into some of the details of what’s going on, why it causes problems, and how to avoid those problems working in a small team.
The first problem that you’ll run into is the sheer quantity of binary files that Unity creates. Nearly every single file that the free version of Unity generates is a binary file. (Not having used the pro version, I cannot vouch for the quantity or quality of binary files therefrom.) The first thing that you’ll need is some way to make sure that you only have one person working on a file at a time. Perforce, for example, has a very strong file locking concept which works great to enforce this. We used Subversion, which has a weaker file locking mechanism, and wouldn’t work to enforce the issue. So, without some software enforcing the separation of work, we had to be very careful and make sure that we all knew what the other was working on. With three of us, it worked fairly well, since we were all working on different parts of the system, but we did have the occasional collision. When you do have these collisions, the only thing you can do is note what changes you made, revert your local changes, and then redo them after you update. Such is the nature of the beast, unfortunately.
When Unity encounters a new file in the Assets folder, it generates a new GUID for it. GUIDs are 128-bit (16-byte) identifiers that are generated through an algorithm that attempts to guarantee their uniqueness. It then generates a metadata and a cache file for that object named the same as its GUID, and places them into the appropriate subdirectories of the Library directory. Finally, it modifies the (binary) Library/guidmapper file to tell Unity that it knows about this file, and what its GUID is.
Do you see the problem? Let’s play the “What if two people tried to do this” game and see what happens. Joe Artist creates a new texture, and places it into the Assets directory somewhere. On his computer, Unity merrily chugs away, and generates GUID aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa. Meanwhile, Jane Designer creates a new prefab, places it into the Assets directory, and her computer generates GUID bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. Back on Joe’s machine, Unity has modified the guidmapper file to indicate that SnazzyTexture.png is mapped to aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, while on Jane’s machine, guidmapper indicates that BestPrefabEver.prefab is maped to bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. Now, Joe finishes his work first, so he checks his files in: Assets/Textures/SnazzyTexture.png, Library/metadata/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, and Library/guidmapper. Jane finishes shortly thereafter, and…oh no! The source control server tells her that her guidmapper is out of date! And in a conflicted state, I might add, since she’s got local changes.
The solution to fixing this problem is to single out one person as the “Library Manager”. It is this person’s responsibility to maintain the Library directory. So, when Joe checks in his texture, all he checks in is the SnazzyTexture.png file. Joe then tells the Library Manager (let’s call him Bob) about his new texture. Bob updates his local repository with the texture, and lets Unity generate a GUID for it. Bob, then, checks in the Library/metadata/GUID file, as well as the Library/guidmapper file. Joe synchs, and then everybody is on the same page.
There’s still a problem with this setup, though. Joe can make textures all day, but a texture is a texture, and it doesn’t reference any other data. That texture isn’t really useful until Joe makes a material for it. The material file is its own asset with its own GUID, but it links to a texture, and the chances are that Joe will be checking in a material file along with his texture. Unity generates this link by storing the GUID of the linked texture in the material. Now, this works fine on Joe’s machine, because his copy of Unity has been generating the GUIDs. But what happens when Bob updates on his machine? The material file will have a GUID in it that doesn’t exist, and the link will be broken.
There are several ways to solve this problem. The first is that Joe can tell Bob what changes to make before he checks in. The second is that Joe can just let Bob check in the Library files, then fix up the links after Bob checks in his piece. The last one is that Joe can export a .unitypackage containing both the texture and the material, which Bob will import, and the link will be proper. All three of these solutions are valid, and will work just fine. In fact, we employed all three of them during the development of the Remnant gameplay demo. (Ace Attack has had a somewhat different development process, so we haven’t run into this problem.)
The problem with these solutions is that they don’t scale beyond three people or so. If I were to use Unity on a larger-scale project, I would have a single machine designated as the Library Manager machine. This machine would watch a directory on a shared drive for new UnityPackages. It would copy them locally, import them into the Unity project, and check in the new files. We won’t be using Unity for any future projects, though, so that won’t be an issue for us.
There are a couple of items left on this subject to tie the whole process in a nice bow. The first item is deleting of files. Just as people should only check their content into the Assets directory, they should only delete from the Assets directory. Unity will detect that the file is no longer there, and will remove the matching files from the Library directory. The Library Manager will be responsible for actually removing the files from source control. Here’s the catch, though: Unity will only remove the files from the Library when it is first loading the project. If you delete a file while Unity is running, it will not delete the matching Library files until the next time you run. So, it is important that the Library Manager close and open the Unity project after the initial update process.
The second is that, when people other than the Library Manager synch, they are likely to get conflicted files in their Library directory. The correct action in these situations is *always* to resolve the conflict by using the file from the repository.
Unity’s layout is optimized to work with its own Asset Server. But, armed with the above knowledge, you should be able to work in small teams without stepping on each others toes too much. Next time I’ll talk about one last workflow issue: large sub-projects.
September 15th, 2010 on 9:43 am
[...] filed in Open Development, Unity on Sep.15, 2010 Editor’s note: This is part 3 in a 5 part series outlining our experiences with Unity & Unity iPhone. Be sure to check out Part 1 and Part 2. [...]
September 17th, 2010 on 10:02 am
[...] 5 part series outlining our experiences with Unity & Unity iPhone. Be sure to check out Part 1, Part 2, Part 3, and Part [...]
September 18th, 2010 on 7:28 am
Hi,
Very interesting article about the assets in Unity. The binary format they use seem to bother a lot of developers.
You say “We won’t be using Unity for any future projects, though”: why is that ?
September 20th, 2010 on 10:28 am
Hi boeington,
There are two major issues with binary files in general:
1.) You can’t diff them. If I want to review what changes I made, so that I can write a checkin report, or make sure that I didn’t accidentally leave a debug flag on, it’s basically impossible to do that with a binary file.
2.) You can’t merge them. If I make some changes to a binary file, and you make some changes to the same file, then how are we to merge those changes cleanly? With a text file, the answer is obvious, and, at the very least, you can do a hand-merge with a high degree of confidence that you’ve got the right output. Not so with binary files, where whoever was the last one in wins.
The answer to your question, though, can best be summed up by saying that we’ve outgrown Unity. Unity has a lot of great features for getting up and running quickly, but it also has a lot of limitations, and we’ve found ourselves hitting those limits quite frequently. You’ll find hints of those limits in the third and fourth entries in this series (thread-safety, Instantiate() is slow, etc.).
We found that we were hitting Unity’s limits so frequently, in fact, that we ended up having to recreate some parts of its library. Once we started doing that, we figured that it was time to rethink what we were doing, what we wanted to do, and how we wanted to go about doing it.
I’ll be writing a blog post in the near future discussing this issue, and talking about how we’re going to be making our middleware decisions.
Thanks for a great question!
September 20th, 2010 on 1:24 pm
[...] Asset Workflow [...]
September 21st, 2010 on 8:24 am
I’d love to see the discussion on middle-ware sooner rather than later. I’m in the process of evaluating engines and would love to hear what you are thinking of using as an alternative to Unity.
October 4th, 2010 on 3:29 am
Good alternative to unity is Neoaxis. It have a lot of “alpha/beta stages” but for experienced team its not an issue as it gives code for most elements for cheap price. Also its build on top of OGRE in which case all low level stuf might be modified with strong comunity as a support. Unity gives you fast and easy start with polished tools for small teams. NA is for bigger teams which may handle some issues by fixing them, in exchange getting much more freedom and be able to lift limits to level where they need easly(beased on experience ofc)
Khash Firestorm
coder @ MuHa Games
October 4th, 2010 on 12:05 pm
Hi Kash,
Good call! NeoAxis (http://www.neoaxisgroup.com/) is a good alternative to Unity, and it definitely competes on price! Another good option (though a little bit more expensive than NeoAxis) is the Esenthel Engine (http://www.esenthel.com/). There’s also Shiva3D (http://www.stonetrip.com/) and Leadwerks (http://leadwerks.com/werkspace/index.php?/page/products/_/tools/leadwerks-engine-r3).
All of these are great engines for smaller developers, and are all good alternatives to Unity at their various price-points. And now, of course, even the big boys are starting to open up. Epic has released the Unreal Development Kit (http://www.udk.com/), and Crytek announced plans to release some sort of free version soon as well (http://www.develop-online.net/news/34466/Free-to-use-CryEngine-plans-emerge).
Look out for my discussion on middleware choices later this week.