teaching machines

CS 491 Lecture 18 – Animations from Blender

April 7, 2016 by . Filed under gamedev3, lectures, spring 2016.

TODO

Lab

Today we’ll be animating a couple of objects in Blender and importing those animations into Unity. Alas, we can’t use Unity’s animations for everything. To animate characters, for instance, we’ll need more power over our geometry than the coarse component-parameter-based Unity animation system provides.

We’ll continue on in Roundup. If you finished last lab, feel free to use your project. If you didn’t finish, please use my reference implementation. Download the ZIP file and unpack it on your machine. Open it in Unity.

Jewel Model

Let’s make a jewel for our character to pick up. It will look something like this:

Screen Shot 2016-04-07 at 5.54.34 AM

In a new Blender scene, delete the default cube. Right-click to select it and hit X.

Hit Shift-A and add a Mesh / Circle object. Before deselecting or doing anything else, change the Fill Type in the operator panel to Triangle Fan. Change the number of vertices to something small, like 10. This circle will be the top of our jewel.

Tab into Edit mode. Enter vertex select mode using the toolbar or Control Tab. Hit E to extrude downward. Drag down just a bit to create the faces of the top bevel. Hit S to scale the bottom edge loop outward.

Hit E again and extrude down to the bottom tip of the jewel. Hit S and then type 0 to collapse the bottom vertices to one location. These 10 or so vertices really can be represented with just one vertex. In vertex select mode, hit A to select all. In the Tools panel on the left, click Remove Doubles to coalesce them. You should see a message in the status at the top-right of the window that some vertices were removed.

Scale or translate the vertices to make the jewel look somewhat jewelish.

Material

Let’s give this jewel some color. In the Properties panel on the bottom right, find the Material tab. Click on the circle to the right of the wrench (Modifiers) and triangle (Data), and to the left of the checkerboard (Texture).

Click the plus sign to add a new material. Name it Jewel Material. (Good names make importing into Unity a much clearer process.)

Select a diffuse color of your liking. Increase the specular intensity. Tweak until you are happy.

Import to Blender

Now, save your Blender file directly into the Assets directory of your Unity project. Switch to Unity. It will save the raw *.blend file into your Unity project, but it will also import your model into a form that Unity understands using Blender’s FBX importer. (FBX is a popular 3D file format that supports animations. Blender can export this format and Unity can import it.)

Drag the model from the assets into the hierarchy to create a game object.

Fix Normals

Click on the imported jewel model in the assets. In the preview widget shown at the bottom of the Inspector, does the lighting look strange when you rotate it with the right mouse button? If so, the faces of your model might be flipped.

In Blender, select the jewel and hit Tab to go into Edit mode. Click View / Properties to open the Properties panel. Find Mesh Display in the Properties Panel. Click the face icon under Normals, and increase the size until you can see lines. Do these normals point into the jewel? If so, the faces are flipped. If the normals point out, then the model is in good shape. Heh heh.

To flip the faces, hit A to select all the vertices or faces. In the Tools panels, click on the Shading / UVs tab. Under Normals, hit Flip Direction.

Save and jump back to Blender. Does the model look better now?

Jewel Collider

We want to be able to run into jewels and “round them up.” We’ll need a collider for this.

Unity can make mesh colliders that fit tightly around your mesh, but they are more expensive to detect collisions with than a simple box or sphere collider. Add a sphere collider to fit somewhat snugly around your jewel.

Playtest. Does running into the jewel stop you from moving?

Bone

Let’s create our jewel’s idle animation. It will just rotate around its y-axis and look dazzling. This is an animation we could easily do in Unity, but let’s create it in Blender because soon we will be creating animations there that can’t be done in Unity.

Blender provides several mechanisms for animating objects, but the one that works best is based on an armature. We will span a series of bones through our model. When these really simple bones move, they will pull the much more complicated surrounding geometry along with them.

Select the bottom vertex of the jewel and hit Shift-S to move the cursor to the selection. Hit Tab to go back to Object Mode. Hit Shift-A and click Armature / Single Bone. It should point up into the jewel by default. Rotate it if not. This single bone will serve as the entire skeleton or armature of our jewel. In a humanoid model, we would use many bones and make the geometry bend at the joints.

Right-click and select only the jewel. Now shift-right-click to also select the bone. The order of selection matters here. Hit Control-P to make the jewel a child of the bone. (What would reversing the order do?) Select Armature Deform With Automatic Weights.

Now in the 3D editor with only the bone selected, switch from Object Mode into Pose Mode. This mode is where we can configure bones to put the model into various poses. None of the underlying geometry is changed; only the bones.

Spin Animation

In the timeline at the bottom of the window, click on the window type dropdown menu at the bottom left. Switch from the timeline to the dope sheet. In the Mode dropdown to the right of the menu items, switch from Dope Sheet to Action Editor. Rename the current action from its default name to Spin. Hit the F button next to the action name. This creates a “fake user” of the animation. Animations and materials that aren’t referenced by anything else in the Blender file get garbage-collected when the file is saved. We won’t be referencing this animation elsewhere in our file, so we must pretend there’s a fake user referencing it to keep it from being deleted.

In Properties / Transform, under Rotation, make sure XYZ Euler is selected instead of WXYZ Quaternion. The former should be a bit more familiar to you because euler angles are used in Unity transforms.

Scrub in the dope sheet to frame 1. With the mouse over the 3D editor, hit the I (that’s the letter I, which comes after H) key to insert a keyframe. Select VisualLocRotScale to record all three properties. Scrub to frame 72. Change the y-rotation in the Properties to 360. Hit I and record another keyframe.

Switch the dope sheet back to the timeline for a moment. Set the end frame to 72 and play the animation. How does it look?

We saw this same problem in Unity. The model slows down near the end of a cycle. Do you remember how we fixed it?

We need to change the interpolation scheme. Switch to the graph editor instead of the timeline. (You can start or stop the animation at any time by hitting Alt-A or Option-A while the mouse is over the 3D window.) We see curves for all the location, rotation, and scale properties, but only one of them is changing over time. Click on the eye next to the Bone to hide all the curves. Click on the eye next to Y Euler Rotation to make just that curve reappear.

Select both keyframes by right-clicking or box selecting with B. Click Key / Interpolation Mode / Linear. Start the animation again. It should be steady now. Save the file and switch to Unity.

Select the jewel in the assets. Click on Animations at the top of the Inspector. You should now see the Spin animation listed from frame 0 through frame 71.

Playtest. Does the jewel spin around in the scene? Probably not yet. We need an Animator Controller to drive the animation. Create one in your assets and call it Jewel Animator Controller. Drag it onto the jewel in the hierarchy. It should automatically drop into the Animator component.

Double-click on the animator controller to find our animation state machine. When we did our animations in Unity, we next created animation clips which appeared in the state machine. This time our clips have already been made during the import process. Expand the model in the assets and find your Spin clip. Drag it into the state machine and make sure it’s set as the default state.

Playtest. Does the jewel spin? Hopefully, but it probably only does so once.

Select the jewel in the assets, and click Animations. Select Spin, check Loop Time to make it repeat, and click Apply.

Playtest. Does it spend endless now?

Up and Away

Let’s make a second animation now. When we run into a jewel, let’s have it spin really fast, fly up, and scale down to nothingness. It will look like we collected it. At the end of the animation, the object will get destroyed and our character will be one jewel richer.

Back in Blender, open up the dope sheet. Click the plus sign next to the Spin action name to add a new animation clip. Name it UpAndAway. Hit A to select all keyframes and X to delete them. Scrub to frame 0 and make sure the jewel’s properties are all at their defaults. Hit I to record a VisualLocRotScale keyframe.

Scrub to frame 72. Translate the jewel up. Rotate it 2 or 3 times around the y-axis. Scale it down very small. Hit I to record a keyframe. Play the animation in Blender. How does it look?

Save the file and jump back to Unity. The new animation should automatically appear in the jewel asset’s Animations widget.

Drag the UpAndAway clip from the assets panel into the animation state machine. Add a trigger parameter Claim. Make a transition from Spin to UpAndAway via condition Claim. Make sure Has Exit Time is unchecked.

Playtest. Select the jewel in the hierarchy. In the Animator window, click on the Claim trigger in the Parameters pane. Does the jewel fly up into the air and get really small?

Jewel Controller

We just tested the trigger manually, but we really want to fire this trigger in code when the player passes through the jewel.

First, add a Player tag to the player object.

Second, make the jewel’s collider a trigger collider so that we can pass freely through it instead of collide with it.

Third, add a JewelController script to the jewel game object. In OnTriggerEnter, check if the collider you are colliding with belongs to the player. grab the jewel’s collider component (you can use the supertype Collider and it will work for BoxCollider, SphereCollider, or any other collider subtype) and disable it through the enabled property. Grab the animator component and call SetTrigger on the Claim parameter.

Playtest. Can the player claim the jewel?

Destroying a Claimed Jewel

Once a jewel has been claimed and its animation finishes, we want to destroy it. Last time when we wanted to add code that was called when the lifting animation state was entered and exited, we added a script to the lift state itself that attached or detached the crate to the player.

In this case, we want to fire an event when the UpAndAway animation finishes. However, we don’t ever exit this state to go to another (you should see no transitions away from it in the state machine), so we won’t get a callback for this event. We need a different mechanism for this. We’ll use Unity’s animation events system, which let us add function calls at certain frames of an animation.

Let’s add the code first. Add a public method to JewelController named Die, Jettison, JewelBeGone, or whatever you want to call it. Destroy the game object within it.

Select the jewel in the hierarchy and open the Animation window. Select the UpAndAway animation clip. Scrub to the final frame. Click the tall trapezoid with a plus sign next to it in the toolbar to add an animation event. A small icon will appear in the timeline. Double-click on it and choose the destroy function that you just wrote.

Playtest. Does the jewel go away once the UpAndAway animation finishes?

Jewel Prefab

Drag the jewel game object back into the assets to make it a prefab. You have a couple of jewels in the assets now. One is the imported model, the other is a prefabricated game object. Their icons are different, but similar. Make sure you can tell the difference.

Drag out a couple more jewel instances into the scene.

Wall with a Door

Let’s create one more animated object. In Blender, open a new file. Create a wall with a hole in it. Start with a cube rather than a plane so we have a object with thickness. Immediately go into Edit Mode and do all transformations there. (Recall that if you transform at the object level, you don’t modify the underlying vertices. The transforms get applied to the object when its rendered, and they make working with the object in Unity confusing and difficult.) Name the object Wall.

In Edit Mode, stretch the cube wide and tall. Subdivide or do some loop cuts to break up the geometry into finer detail. Delete some faces to create a hole for a door. Move the geometry up so that it’s all above the origin.

Go back into Object Mode. Add a second cube for the door. It’s important the door be a separate object, because we want to animate it but not the wall. Modify it in Edit Mode to fill the void in the wall. Name the object Door.

Door Armature

Now let’s add a single-bone armature to the door so we can animate it.

Select the bottom face of the door and hit Shift-S to move the 3D cursor to its center. Tab into Object Mode.

Hit Shift-A and add an Armature / Single Bone. Right-click on the door object, shift-right-click on the bone, and hit Control-P to parent the door to the bone. Use automatic weighting.

Open up the dope sheet, select the bone, and enter Pose Mode. Switch from the Dope Sheet toolbar to the Action Editor toolbar. Rename the current action or create a new action named Idle. Click F to create a fake reference to the animation.

In this animation, the door will rest. Scrub to frame 0. Hit I to record a Visual Location keyframe. Scrub to a later frame. Hit I to record a second Visual Location keyframe. We are done with that animation.

Click the plus sign next to the action to create a second animation clip. Name it SlideUp and click F to create a fake reference. Hit A to select all existing keyframes and X to delete them. Scrub to frame 0. Hit I to record a Visual Location keyframe. Scrub to a later frame. Translate the bone up so the door lifts out of the way. Hit I to record a final Visual Location keyframe.

Save the file in your game’s Assets directory.

Colliders

We want the player to not be able to pass through the wall except when the door is open.

Click on the the new model that you just imported in the assets. Under the Model tab, check Generate Colliders. Drag the model into the scene.

Playtest. Does the wall block you for passing through it? How about the door? For reasons I can’t explain, Unity generated a mesh collider for the wall for me, but not the door. If you got a door collider, congratulations. Now delete it off the door child. We will make our own colliders for the door.

Select the door child and add a box collider that fits snugly around the geometry. This collider will block us from passing through.

Also add a sphere collider that is a trigger. We’ll use this collider to trigger our sliding up animation when the player draws near.

Lifting Door

Create a Wall Animation Controller and drag it on to the parent object of your model. It’s the parent that has the Animator component, even though it’s the door child that actually animates.

Drag the Idle clip from the assets into the animator state machine. Drag the SlideUp clip as well. Add a Bool parameter named IsUp. Transition from Idle to SlideUp when the parameter goes true. Make a complementary exit condition.

Attach to the door child a DoorController script with these callbacks:

// Disables the physics collider only after the door is fully lifted.
public void OnLifted() {
  GetComponent<BoxCollider>().enabled = false;
}

void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.name == "Player") {
    transform.parent.GetComponent<Animator>().SetBool("IsUp", true);
  }
}

void OnTriggerExit(Collider collider) {
  if (collider.gameObject.name == "Player") {
    transform.parent.GetComponent<Animator>().SetBool("IsUp", false);
    GetComponent<BoxCollider>().enabled = true;
  }                                                                                                                                                           
}

This code sets the animator parameter when the player enters or exits the sphere collider. The Animator is a component of the parent, so we have to reach up to the parent to the set the IsUp parameter.

Playtest. Does the door open when you enter its trigger collider? Does it close when you exit? Can you pass through the door?

I wasn’t able to pass through the door. I needed to disable the non-trigger box collider once the door lifted. We don’t want to disable the door prematurely; otherwise we’ll be able to pass through it. Let’s disable it through an animation event.

Animation events can only call public functions of the object with the Animator. So, add a WallController script to the wall parent object that has the Animator. Define this public function:

public void OnLifted() {
  GetComponentInChildren<DoorController>().OnLifted();                                                                                                      
}

This method just calls the door child’s OnLifted function we defined above, which disables the box collider.

Add an animation event to the end of the SlideUp animation to call this method.

Playtest. Can you pass through the door once it is up? Does it continue to behave correctly on subsequent passes?

That’s it! We’ve just explored how to bring in animations from Blender.