Building Character Customization (Part 1)

For the past week or so, I’ve been adding more features to my asset build system on the Blender side and asset loaders on the Unity side to support character customization.

What is character customization you ask? Read below! (And of course, if you’re not asking, skip to the Requirements section below.)

Overview

Character customization is a game system that allows a player to choose parts and settings of a character’s appearance to define a more unique experience for the player. Examples of this include different hairstyles, body types, clothes, face shapes, and skin color.

Games that include character customization provide the player with a sense of identity for the hero character. The more customizations a game supports, the more specifically the character can resemble the player’s identity, whether that identity is a reflection of himself/herself, or someone else, like a role model, icon, or celebrity.

Character customization has grown in popularity over the past decade or more in video games, and now, a lot of players take this system for granted. It seems that as games are released with more customization options, future games are expected to have more options than their predecessors.

From a game development perspective, the reality of implementing character customization is still considered a massive undertaking. Character models no longer have a one-to-one relationship between rig (skeleton) and mesh (geometry). Instead, rigs are shared between multiple models that represent any number of variations of hair, heads, eyes, body types, clothes, accessories, sexes, etc. As you might imagine, authoring the content for all of these variations can be boundless, and the number of combinations is exponential. And that’s why game developers have to place a certain limit on those choices, unless, of course, one of the following happens:

  • The game never gets released.
  • Someone pays for all the content to be developed.
  • Allow players to create their own content.

Okay, so that last bullet point is an article in itself, so I’m not even going to go there.

One last thing I want to mention before I move on. The reason why the rig is typically shared between character models is so that animations can be shared between each model. Animation is expensive all across the board. Animator authoring time, computer disk space, in-game memory and processing power all are significantly impacted by 3D skeletal animation, so sharing these animations between characters helps a ton.

Next up, here are the requirements for my game.

Requirements

Gameplay

For my game, I’m aiming to keep it as simple as possible while providing a fair amount of customization. The player can change:

  • Character
  • Hair Style
  • Hair Color
  • Head/Face Type
  • Skin Color
  • Costume

For launch, I plan on supporting one male and one female hero character, with around 2-4 different types for each category. That’s a reasonable amount of work for about a month of development time.

Technical

So, the gameplay requirements are just the surface of what we need. To achieve this, we need to nail down the technical requirements to get to the end result.

As a brief refresher, I’ve been working on an asset builder to convert .blend files created in Blender3D to FBX files for the Unity3D game engine to use. While this has been working great for my purposes, supporting character customization means that the asset builder, among other parts of my game, have to change a bit.

How It Works Now

blender_blend_to_fbx

Currently, nothing changes here between the input and output files, other than an FBX file being slightly smaller than a .blend file. But all geometry are bound to the skeleton in one blend file, and also in one output FBX file. I also added a rough graphic indicating that each file contains the armature, meshes, and animations, and that animations take up a significant amount of space, on disk as well as in memory.

Before I go into details about my solution, it’s worth mentioning that there is more than one way to do this. That said, I’m switching to Blender mode now.

Blender and Linking Fail

Blender offers a feature called Link. Most 3D packages have a similar feature though. My original intention was to create one .blend file that contains the rig (armature) and animations, and then a .blend file for each custom hair/head/costume. The theory behind this is, you can Link (or reference) an external .blend file (namely the rig/animation blend file) into the mesh (geometry) .blend file, so that changes that occur to the rig or animation only happen in one file. This enforces that the mesh is using the shared rig, so that the bone names, hierarchy, and vertex weights conform to that rig; a requirement for animation to work correctly in the run-time game environment.

blender_blend_file_link_armature

Alas, I was unable to get the Link feature working correctly for the animated Rigify armature in Blender, so I needed to use a workaround. If anyone out there reading this has actually figured that out, please drop me a line. Linking a reference to the rig is actually the most ideal way to author these models, and it’s unfortunate that I couldn’t get this working.

Using Blender Layers to Manage Meshes

The workaround for this problem is to work out of one .blend file for all custom meshes that are intended to be attached to a specific rig/animation combo. It’s a little cumbersome to work with, but it’s also 100% guaranteed to be using the appropriate bone names, hierarchy, and vertex weights. The reason it’s cumbersome is because multiple meshes need to be parented to the same armature in the same file. This can make it difficult to manage.

blender_multiple_meshes

To alleviate this problem, Blender (and almost all other 3D packages) has a layer system to hide objects in the scene that are not on a currently active layer. Moving hair, head, and body meshes to an appropriate layer makes managing them a little bit more tolerable.

blender_layers

The above image is from Blender’s Layer Management plugin that is not enabled by default. This needs to be enabled in User Preferences > Add-ons > 3D View: Layer Management to see this in action.

So, that solves one problem. Authoring custom character parts can be done in one file that is specific to a rig/animation combo. This means for any additional characters that may have different rigs and/or animations, a new .blend file must be created.

But we still don’t have a solution for actually exporting these for Unity to use. Of course, we can export the entire .blend file that includes ALL meshes. Unity would then load all mesh resources into the game when you load the FBX file. Then, your game code in Unity can look for the appropriate mesh name to load (i.e. “body”, “body.001”, “body.002”, etc.). I think this solution is suitable for games that don’t have a lot of custom meshes. However, I think it would also clutter the animation viewport in Unity, because all the custom meshes are skinned to the skeleton.

The problem with this is that the game is now unnecessarily loading all custom meshes, when you really only want one loaded at any one time.

blender_export_fbx_rig_and_all_models

This also means that the more custom meshes you have in the .blend file, the more valuable memory you’re wasting to load unused meshes,. Below, I describe how I implemented my solution.

Saving Memory and Sanity

Here’s a graphic outline of my solution.

blender_export_fbx_rig_and_models

As you can see, I’ve set up my build process so that the .blend file exports multiple FBX files. The key FBX file contains just the rig, animation, and a low-poly mesh*, and then all other FBX files contain the custom meshes bound to the same rig, and no animations. This way, when we load the FBX files in Unity, only the meshes that we need are ever loaded into memory. You’ll also notice that the rig is duplicated for each model FBX file. This is a small cost compared to loading any number of unused meshes.

* A note about the low-poly mesh. I use the Unity asset called RigifyToUnity which removes unnecessary bones from the Rigify armature on import of the FBX. RigifyToUnity actually doesn’t recognize the armature without a skinned mesh. Of course, you could simply modify RigifyToUnity to support skinless armatures, but I’d rather keep that script intact. Instead, I added a simple quad mesh and skinned that to the armature. This skinned quad will also prove important when importing the model into Blender, as it provides a SkinnedMeshRenderer component that can be used to swap in alternate models. I’ll write about that in the next part of this series.

Modifying the Asset Builder

I’ve used the scripts that I’ve written about in my post called Blender to Unity Asset Builder, and simply added support for Blender layers. I’m not going into full detail of how I implemented this, because it would simply take too long, but feel free to ping me if you have any questions. You always have the option to export your armatures/animations and meshes manually with Blender’s Export UI if you choose not to use Python scripts. But I’d still like to point out some utility code that will be useful if you choose to implement this yourself.

To access the names of the layers in a Blender scene, you first have to make sure that the Layer Management plugin is enabled, as I mentioned above. Then, you’d be able to access the names like this in Python:

 for layer in bpy.data.scenes[0].namedlayers.layers:
     print(layer.name)

The whole function looks like this:

# gets tuples consisting of [layer index, layer name]
def get_named_layers(sceneIndex=0):
    ret = []
    index = 0
    # NOTE: If namedlayers causes an error, make sure the Layer Manager plugin is loaded in Blender
    for layer in bpy.data.scenes[0].namedlayers.layers:
        if layer.name[:5] != 'Layer':
            print(layer.name)
            tuple = (index, layer.name)
            ret.append(tuple)
        index += 1
        print('index: ' + str(index))
 return ret

I have my rig layer contain the name “rig” in it, so I have a function that does this:

def get_rig_layer(sceneIndex=0):
    layers = get_named_layers(sceneIndex)
    for layer in layers:
        if layer[1][:3].lower() == 'rig':
            return layer
    return None

You can also select objects by layer using this Blender command:

 bpy.ops.object.select_by_layer(layers=layerIndex+1)

The layer indices are one-based, hence the +1.

And lastly, the options into the fbx export call:

bpy.ops.export_scene.fbx(filepath=outputfilepath, axis_forward='-Z', axis_up='Y', use_selection=export_selected, bake_space_transform=True, object_types=object_type_set, use_armature_deform_only=True, bake_anim=exportAnimations, use_anim=exportAnimations)

Where:

  • export_selected is a boolean indicating the export of the objects selected by layer above.
  • object_type_set specifies that we are exporting both the ‘ARMATURE’ and ‘MESH’, but nothing else.
  • exportAnimation specifies that animations are exported with the FBX, and this should only be exported with the rig file, and not with the mesh files.

The reason why I want to capture the layer names is so that the filenames of the export FBX files contain the layer name. I use the layer name when loading the FBX into Unity.

Conclusion

That’s about it for now. Part 2 of this article explains how I load the rig and model FBX files into Unity. This does seem like a lot of work to support character customization. It is a lot of work. But these sorts of systems and optimizations are what prevent a game from running like a snail, and some of these techniques are used across the industry, in one form or another. Also, when the people playing your game are super excited about changing the look of their character, isn’t it all worth it in the end?

 

  • Unity3D 5.1.1f1
  • Blender3D 2.74
  • Python 2.7.10
This entry was posted in Dev, Programming. Bookmark the permalink.

2 Responses to Building Character Customization (Part 1)

  1. Fei says:

    Your article has been extremely helpful.

    Looking forward to part 2.

Leave a Reply

Your email address will not be published. Required fields are marked *