The Perils of Prefabs

Prefabs are Unity’s intended way for you to construct composite game objects. When you first start using Unity, they seem fantastic. You can assign a handful of components to a model, and have an object you can easily spawn over and over during gameplay. When you hit the point in your game development cycle you want complex, composite objects that are reusable, prefabs can help you meet this goal.

One example of popular prefab usage is the base enemy character prefab, with collision components, a character controlling component, a state machine component, and the character model for your enemy. This lets you get your enemy all setup and squared away, so you can tweak settings on them globally, if you have that enemy prefab instanced multiple times within a scene. Another example is UI. As you build your game, you’ll probably find you can share a lot of your UI logic and functionality. Instead of building a new button from scratch every time you need a button in your menu somewhere, it can be much easier to create a prefab that is a already setup general case button, and then make local changes to the instanced prefabs of the button within your scene, or even make new prefabs based on the base button prefab, so you can reuse your pause menu button in multiple places.

Unfortunately, prefabs are a Unity feature that can crumble under strain if you’re not careful. If you plan ahead, and are aware of their limitations, they become much more manageable.

The first hurdle everyone runs into with prefabs is the inability to easily update nested prefabs. A nested prefab happens when you try and parent one prefab to another. When you update the child prefab, the data within the parent will not update automatically, you will need to manually update the parent prefab. For smaller projects and teams, without a lot of prefabs, this is not a huge bottleneck, but on larger teams, or projects with many assets, this can be a maintenance nightmare. If you nest a third layer deeper than that, and the maintenance can become a hassle for even the smallest time.

The next hurdle with prefabs is asset loading and management. If you want to build a resource management system with features like reference counting, prefabs add a large amount of work to this process. With textures, a resource load brings in one asset, that you can directly manage loading and unloading of. With prefabs, you might bring in any type of asset, the dependency list can be quite large.

Dependencies also mean prefabs can play extremely poorly with asset loading systems. Most systems do not perform reference counting, so if you have two different prefabs that depend on the same asset, you can easily end up with that asset loaded twice. If you have prefabs in different asset bundles that both depend on the same asset, depending on your asset bundle build solution, you can easily end up at a point where your asset is duplicated between two bundles, and loaded twice.

So prefabs are not great to work with once your game hits a larger scale, what are solutions for handling this?

For some content, you can keep prefabs one layer deep and avoid nesting them through careful use of scenes and Application.LoadLevelAdditive. HUDs are the perfect example of this. You probably want to construct your HUD from multiple prefabs. If you have a dedicated scene for your HUD, managing these prefabs becomes much simpler. The other benefit to this is you can make use of the asynchronous operation Application.LoadLevelAdditiveAsync to load this data.

Another solution for achieving the same effect as nesting prefabs is the data driven factory approach. This is my personal favorite way for working around nesting prefabs, it is straightforward, and matches how most games and game engines are built, and I often end up wanting a factory for creating things in-game for other reasons, such as more directly handling the process. Instead of constructing an enemy character through nesting prefabs and setting the data on components that way, use a database to contain data you can use to construct your characters at runtime. Not sure where to begin with this? Well, chances are you have a few components you need to add to every character. Usually a state machine, a controller, and a base character class. The controller tells the state machine what it wants to do, the state machine figures out the behavior of the character based upon internal and external input, and the base character class usually holds the data for the character such as health. From here, you want to build a class that can construct characters based on input data. Take in a model, character name, and other values such as maximum health or movement speed. Finally, you build a factory class, a class that takes data from a database, and passes it through to the previous class to construct the character. There are downsides to this solution, though. Unity does not have any built in database functionality, so you will either need to write your own, or grab one off of the Unity asset store. After that, most factory solutions will not provide the same level of in-editor visualization tools as prefabs. You either have to live with that, which might not make artists happy, or build features into your database for editing your characters in a way that replicates the prefab editing behavior.

The final solution for handling nesting prefabs, that I do not recommend, is writing code that automates most of the steps in nesting prefabs, and updating children. I recommend against this solution just because you have to handle a large number of edge cases, and I prefer the factory solution. You can begin exploring a script driving solution for nesting prefabs by looking at the AssetPostprocessor class. By inheriting from this class, you can have scripts run right after Unity finishes importing an asset, such as your prefab, or content that your prefab depends on. From here, you need a way to identify dependencies of this prefab, and prefabs nested off of this one. Once you solve this, you can use this data to update all prefab dependencies, and continue onwards until all prefabs have been updated. You will need to track changes that users have made over the original data on the prefab. An example of this would be: Your child prefab has a scale value of 2,2,2, within your parent prefab you’ve set the scale of that child to 1.5, 1.5,2, and then in a specific instance of the parent prefab within a scene, you have set the scale to 1.0, 1.5, 2. If you update the scale on the child prefab, you will need to define a behavior pattern for this, and potentially provide a user a way to make sure the behavior matches what they intended.

Advertisements
About

Joseph Stankowicz is a software engineer who has worked in the video games industry for over eight years. The last two years have had a heavy focus on Unity development, where he helped ship over eleven titles to iOS and Android platforms. He also is really excited about 3D printing, and keeps his Solidoodle 3 printing out stuff as often as possible. You can view his LinkedIn profile here http://www.linkedin.com/pub/joseph-stankowicz/60/294/420

Tagged with:
Posted in Unity3D Performance

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: