a.k.a. Blender to Unity Asset Builder (Part 2)
I didn’t plan on writing a second part to the Blender to Unity Asset Builder post, but I found myself continuing work on my asset build scripts because for whatever reason, Unity does not recognize Blender’s pose markers during FBX import, so no animation events get loaded for all the animation clips. But for a full explanation, let me rewind a bit.
Some Background
Definitions
3D animation for games consists of short clips of animation made up of keyframes that define the motions of a skeleton or armature. Short clips can be defined as “walk” cycles or “idle” loops. Furthermore, for real-time animation used in video games, “event triggers” can be attached to specific frames of the animation to let other systems know about a key point in the animation to react to. For example, events can be added to footsteps in a walk cycle, so that the audio system can trigger a particular footfall sound.
For my purposes, I needed event triggers for “attack” animations, in which a character winds up for a punch or sword stroke, and subsequently lunges forward for the strike. That strike needs a trigger, so that the character on the receiving end of the attack can play the reaction animation.
Blender’s event triggers are called Pose Markers (in the Dope Sheet/Action Editor):
Whereas Unity’s event triggers are called AnimationEvents (as seen on the FBX asset’s Animation tab):
The Problem
When exporting a Blender file to the FBX format, the Pose Markers do not get saved out with the file. This means Unity will never know about these animation events. While Unity has a way of storing these events, it’s preferable that the data comes from Blender because this is where the animation is being authored. Adding the events on the Unity side means that every time a model is modified in Blender, the animation events must also be re-added back to Unity. That’s a lot of throw-away work:
Solution Hints
After a lot of researching online, I came across two good sources that were promising:
- Importing Animation Events from Blender into Unity3D by Jens Ch. Restemeier
- ADD ANIMATION EVENTS USING ASSETPOSTPROCESSOR IN UNITY
Both of these solutions are pretty much the same, but they’re just implemented slightly differently. I’m personally using Mr. Restemeier’s solution as the basis for my solution, since it uses XML as the intermediate data format, so I’ll use that as the basis of my explanation below.
Solution Overview
Here are the main steps that need to happen:
- Blender constructs an XML structure of the animation events.
- Blender exports the resulting XML file into the Unity Asset directory.
- Blender exports the model as an FBX file into the Unity Asset directory. (See the previous blog post.)
- Unity processes the XML file after processing the FBX model.
Starting with the XML-based solution, I mainly had to modify the Python and C# scripts to be usable with the latest versions of Blender and Unity. For the most part, I didn’t have to update any API calls, which surprised me. The majority of my changes had to deal more with getting those scripts to work with my own build system.
Integrating Into the Existing Asset Builder
Blender Scripts
There’s not that much that’s different from the Blender Operator script that I discussed in the previous blog post. The MyExportFBXOperator
class would simply be adapted into something like a MyAnimEventOperator
class. The obvious difference would be in the execute(self, context)
function, of course, in that instead of exporting an FBX file, the operator would write out an XML file, and Mr. Restemeier’s solution in export_events.py
provides a perfectly sound solution to this. What is of interest though, is how the Pose Markers are obtained. Here’s a Python snippet that will loop through all pose markers in each action clip in a Blender file.
for action in bpy.data.actions: print('action: ' + action.name) for marker in action.pose_markers: print('marker: ' + marker.name) print('frame: ' + (int)marker.frame)
Unity Scripts
The Unity scripts also did not require much editing. The file of interest is EventImporter.cs.
The EventImporter
class is derived from the AssetPostProcessor
class, which is a class used by Unity’s build pipeline to allow any custom post processing. The function of interest is OnPostprocessModel()
which reads the XML file generated by Blender and populates each animation clip’s AnimationEvents from that XML data.
The only part that gave me problems was the introduction of the EventReceiver
class in that function. The EventReceiver
is intended to receive the events (duh) from the AnimationEvents that fired. For instance, an AnimationEvent
with the name “Stab”, will call the function
public void Stab() {}
in the EventReceiver
class. Naturally, this class is very game-specific, and is only intended to serve as an example. I decided to comment out anything regarding the EventReceiver
in the OnPostprocessModel()
function, since the EventReceiver
component is dynamically added in this function, it has a tendency to leak memory.
I also commented out this code block:
if (methodInfo != null){ ... } else { ... }
This code populates the user-defined data for the AnimationEvent
. It can be composed of a float, int, string, or object reference. It may be necessary for some projects, but I don’t have a particular need for it now. Additionally, it’s not absolutely necessary to use C# reflection (the only usage of the EventReceiver
component in this function) to determine a match between the receiving function and the supplied data. Although it’s probably good programming practice, it unnecessarily ties a game-related class to a generic build pipeline solution.
After everything is working, the “Stab” event shows up in Unity’s Animation Window.
Unfortunately, I couldn’t get the event to show up when selecting the FBX asset in the Project window, but that’s okay. At least the Animation Window shows proof that it actually works.
Blender Export Dialog
To tie together all of the export functionality in Blender, I decided to write up a Dialog to invoke from a keyboard shortcut.
bl_info = { "name": "Export Master Menu", "author": "Under the Weather, LLC", "category": "Import-Export", "description": "Export Master Menu", } import bpy from bpy.types import Menu, Panel, UIList import MyExportFBXOperator import MyExportAnimEventsOperator # export flags gModel = True gTextures = True gAnimEvents = True class DialogOperator(bpy.types.Operator): bl_idname = "wm.exportmastermenu" bl_label = "Export Master" sTargetGameAssetPath = bpy.props.StringProperty(name="Target") bExportModel = bpy.props.BoolProperty(name="Model") bExportTextures = bpy.props.BoolProperty(name="Textures") bExportAnimEvents = bpy.props.BoolProperty(name="Anim Events") def invoke(self, context, event): global gModel, gTextures, gAnimEvents self.bExportModel = gModel self.bExportTextures = gTextures self.bExportAnimEvents = gAnimEvents return context.window_manager.invoke_props_dialog(self) def execute(self, context): print('Processing') if bpy.data.filepath == "": print('Blend file needs to be saved first.') return {'FINISHED'} if self.bExportModel: bpy.ops.wm.exportfbx('EXEC_DEFAULT', targetGameAssetPath=self.sTargetGameAssetPath, copyTextures=self.bExportTextures) if self.bExportAnimEvents: bpy.ops.wm.exportanimevents('EXEC_DEFAULT', targetGameAssetPath=self.sTargetGameAssetPath) print('Process complete!') return {'FINISHED'} def register(): bpy.utils.register_class(DialogOperator) def unregister(): bpy.utils.unregister_class(DialogOperator)
Which looks something like this:
Conclusion
I seriously thought I was done writing my custom asset builder the last time I wrote about it. As I was working more on my game, I was trying to avoid using Unity’s AnimationEvent
system by simply capturing state changes in the Animator
component’s AnimatorStateInfo
. Soon enough, I found that I needed to capture events in the middle of animation clips rather than just at the end or beginning, so I dug around for solutions, and the ones I linked to above seemed to be the most promising ones. There were a few solutions in the Unity Asset store as well, but I wasn’t able to get the free one working out of the box, and the other solutions weren’t free. I think that’s about all I want to talk about in regards to the asset build pipeline for a while. It’s time to make a game!
- Blender: 2.74
- Unity 3D: 5.1.1f1