This guide assumes you have already read/completed the previous tutorials.
File list
File Link | Description |
---|---|
End Result | The end product of this tutorial for you to examine and compare. |
Introduction
On this page, we will take a look at how to create a basic animation, the process of getting it in-engine, and gain an understanding of the .model_animation_graph
tag. The overall goal of the following tutorial is to add an animation to our platform scenery to make it move in-engine.
Animations in Blam!
Animation in the Blam! engine is quite a broad topic and not everything will be covered here, but the following basics should give you a good enough understanding such that you can create animations for any scenery or device machine object you wish. Unlike models, which all use the .JMS
format for importing, animations use a multitude of formats that determine how tool
handles them on import. You can see a list of all types here, but the main two we will focus on are .JMM
and .JMO
. Think of .JMM
as the "base" type of animation - one that causes an object's bones to simply move. The most common use-case is for object idling animations. .JMO
are overlay animations - these are additive meaning that the bone movement data is applied on top of any current base animation on the object. When talking about things that apply to all extension types, I will use .JMx
to indicate any extension.
Some animations require specific numbers of frames, where certain actions happen on certain frames - this is not the case for basic scenery or device machine animations, but is something you should keep in mind in the future. For example, wheel-based vehicle (think Warthog) steering animations only consist of four frames: rest, full-right-turn, rest, full-left-turn.
Vertex weighting
Vertex weighting is a complex topic - it is the process of determining how the vertices
in a 3D mesh are manipulated by the movement of bones in an armature. If you'd like to learn more, a good place to start is the official Blender documentation. Luckily, the weighting that we have to do for our model is extremely simple. As we only have one bone, and we want the entire model to follow the animation of that bone, we can simply weight all of the vertices in our model to that bone, with a value of 1
.
- Open your Blender file from the last tutorial, or download the end result from the previous page.
- Select the
platform
object, then navigate to theObject Data
(green) tab in theProperties
window. - At the top, locate the
Vertex Groups
section, and click the+
symbol. This adds a new vertex group to the model. - Vertex groups rely on their name to determine which bone they are tied to. Put simply, rename the vertex group to
Bone
to match the name of the bone in the armature. - In the
3D Viewport
, pressTab
to enter intoEdit Mode
. Set your selection mode toVertex
with the buttons to the right of the mode dropdown. You will notice that four new options have popped up underneath theVertex Groups
section -Assign
,Remove
,Select
andDeselect
.Assign
takes any currently selected vertices, and assigns them to the currently selected vertex group.Remove
does the opposite.Select
will highlight any vertices assigned to the currently selected vertex group.Deselect
does the opposite. - Press A in the
3D Viewport
to select all vertices in the model (they will turn orange). Then, in theProperties
window, hit theAssign
button with theBone
vertex group selected. - In the
Properties
window, switch to theModifiers
(wrench icon) tab. We need to add anArmature
modifier to our model, so that the newly added vertex group knows which armature to use (we only have one armature in the scene, but it needs the modifier regardless). - Click
Add Modifier
, then chooseArmature
at the top of theDeform
column. - In the newly added
Armature Modifier
, click inside theObject
field, and chooseArmature
. Ensure thatBind To Vertex Groups
is checked.
That's it! Our platform
object will now correctly animate with the armature, once we have created an animation. If you wish to perform a check to make sure you have done these steps correctly, do the following:
- In the
3D Viewport
window, click the mode select dropdown near the top left and choosePose Mode
. Pose mode is a special object mode used to createkeyframes
for animation, but we will use this more in the next section. - Expand the Armature in the
Outliner
window, and select theBone
. - Hover over the
3D Viewport
, and press G to move the bone. As you move your mouse to move the bone around, theplatform
object should follow it. Don't worry that the physics object doesn't do the same - we will talk more about this later. - Right-click to stop moving the bone, or use Ctrl + Z to undo any changes if you accidentally made them. Then use the mode selection to return to
Object Mode
.
Creating an animation
Our second goal is to make a simple animation to play on our .scenery
object - a spinning animation will do nicely, and is easy to create with only a few keyframes.
If you are already comfortable with animating in Blender, you may wish to skip this sub-section and make an animation on your own - the types of animation we will be making in this tutorial do not need to follow any halo-specific rules.
- Ensure that you can see the
Timeline
window (at the bottom of the screen in the default workspace). It is recommended to expand it vertically slightly for better visibility, as per the image below. - In the
Timeline
window, set theEnd
value to80
. This number is the end frame - we start at 1, and the animation now ends at 80. Animations are played at 30 frames per second in-engine, so this animation will last roughly 2.67 seconds. - Select the
Armature
object in your scene. - Use the
Mode
drop-down menu in the top right of the 3D View to switch toPose Mode
. - Select the bone in the armature (named
Bone
). You can do this by zooming in and left-clicking the bone in the 3D View, or by expanding the dropdown on theArmature
object in theOutliner
view on the right. - Make sure the currently active frame is
1
. You can do this by dragging the blue slider in theTimeline
window to the start, or by typing1
into the box in the top right, next to theStart
andEnd
frame boxes. - Hover your cursor over the
3D Viewport
window, and press I. This will open the keyframe window - chooseRotation
.
You will now notice an orange dot has appeared on the timeline. This is a keyframe - a keyframe, at its most basic, stores specific information about the keyframed object; in this case, the rotational data of our Bone
in the Armature. It tells Blender that the specified frame, the rotation of the bone should be set to the keyframed value, regardless of what happens on previous or future frames. Let's add the rest of the necessary keyframes, and how they work should become much clearer:
- Use the timeline slider or the frame number box to set the current frame to
20
. - In the
3D Viewport
, press R, then Z, then type 90. Left-click or press Enter to confirm. This rotates the bone by 90 degrees, or one-quarter turn, and theplatform
object follows suit. - Hit I to open the keyframing menu, and select
Rotation
to store the current rotational data into frame 20. - Move the timeline to frame
40
. Once again press R, then Z, then type 90, followed by a left-click or the Enter key to apply. As before, use I to open the keyframe menu and keyframe theRotation
data. - Move the timeline to frame
60
, and repeat the rest ofStep 11
. - Move the timeline to frame
80
, then repeatStep 11
one final time.
On frame 80
, the bone and platform
should now be back in their original rotation. You've done it - this is all that is required to make an anti-clockwise spinning animation! Press the play button in the top middle of the Timeline
window to see your animation play on loop. You can use the other controls to skip to the start or end frame, or step through the animation one frame at a time. It should look like this:
However, you may or may not have spotted an issue - the animation looks "jerky", as it slows down and speeds up slightly as it approaches each keyframe. We need to change blender's extrapolation mode to linear to make it nice and smooth:
- Click the
Editor Type
drop-down in the top left of theTimeline
window (the clock icon). SelectGraph Editor
. - Click the
Channel
heading, theExtrapolation Mode
, thenLinear Extrapolation
. - Open the
Editor Type
drop-down again and set it back toTimeline
. Play the animation again, and it will loop smoothly. - You can now leave
Pose Mode
and return toObject Mode
in the3D Viewport
.
Exporting animations from Blender
As you should already be familiar with exporting .JMS
files from Blender with the Blender addon, the export process for animations should feel at least somewhat familiar.
As we have not directly edited the render model, physics model or armature since exporting them in previous tutorials, there is no need to re-export them now.
- Click
File
->Export
and chooseHalo Jointed Model Animation (JMA)
. - Navigate to your the
custom_platform
's data directory - that would be"H3EK\data\objects\scenery\custom_platform"
if you have been following along exactly. - Create a new folder here (the
Create New Directory
button to the left of the file path box), and name itanimations
. As with therender
andphysics
folders,tool.exe
looks specifically for this folder name, so make sure it is named correctly. - Navigate inside the new
animations
folder. - On the right side of the export menu, make sure the
Game Version
is set toHalo 3
, and theExtension
is set toJMM
. Leave everything else default. - At the bottom of the export menu, name the file
any idle
exactly.
Naming animations correctly is important, as the name describes to tool.exe
how the animation should be handled and where it gets put inside the .model_animation_graph
tag. any idle
specifies that this is an idle animation that should play under any circumstances.
- Hit the
Export Animation
button, and wait for theExport completed successfully
message to appear at the bottom of Blender.
Importing animations and the model animation graph
Animations are stored in .model_animation_graph
tags once imported. This tag type is automatically generated on first import, similarly to how a .render_model
tag is built automatically when you first import a .JMS
. These tags can contain as many animations as needed, and are re-built whenever the animations
folder is imported. This re-building process deletes any existing animations in the tag, and then adds those found in the animations
folder back in. This means that if you no longer have the source .JMx
files for some animations and attempt to import others, they will be removed.
Let's go through the process of importing our new any idle.JMM
file:
- Open up
CMD
in yourH3EK
folder. The command we will be using this time ismodel-animations
. - Type
tool model-animations "path\to\your\scenery"
, wherepath\to\your\scenery
is the tags-relative path to your scenery's data folder. For example if you have been following along exactly, this will be"objects\scenery\custom_platform"
.
You should now see the following output from tool
, informing us that the .model_animation_graph
tag has been successfully created (it will be alongside the other tags, e.g. in "H3EK\tags\objects\scenery\custom_platform"
). The warning and error here can be ignored - they are simply informing us that a new tag is being made from scratch: Now, go ahead and open up Guerilla, and then open the newly generated .model_animation_graph
tag in the object's tags folder. This isn't a necessary step, but it's a good idea to familiarise yourself with the tag regardless. There are three important sections of note - the Skeleton Nodes
block, the Animations
block, and the Mode-n-State Graph
block.
The Skeleton Nodes
block lists all of the bones used in this model and animation. In Blam!, bones are called nodes
. We only have one node, and so there is only one entry in this block. The node list of a .model_animation_graph
and the .render_model
it is associated with must match exactly. You will get an error when importing animations if they do not. If you change something after the fact, such as editing the render model to add new bones but not updating the animation to reflect this, the node lists will no longer match, and the object will refuse to spawn at all.
The Animations
block is quite obvious - it lists all of the animations held in the tag, and their associated data. Here you can see our named animation. Note that spaces are replaced with colons in Guerilla, such that any idle.JMM
becomes any:idle
in the tag. One use of this block is to add sounds that play on certain frames of the animation - this is done in the Sound Events
sub-block.
The Mode-n-State Graph
block shows in what states the animations in the graph will play. As we named the animation any idle
, tool
recognised that it is to be played under any circumstance, and so it is already set up correctly in this block. This is why naming conventions for .JMx
files are important, as it tells tool
how to automatically handle them. Our idle animation will therefore play for any mode, in any weapon class, and in any weapon type. You can see the reference to the any:idle
animation here in the Actions
section. Actions
are base animations, such as .JMA
and .JMM
, whereas the Overlays
section is for .JMO
animation types.
Playing the animation in-engine
It's about time we saw that platform spinning in Sapien! We've already done 99% of the hard work, so let's do the final steps:
- In Guerilla, open the
.model
tag for the platform. - Right at the top, locate the
animation
entry - it's in between the entries for the.render_model
and.physics_model
that we have already added. - Click the
...
next to the animation entry, and open the new.model_animation_graph
tag. - Save the tag.
- Open Sapien on a map of your choice, and add the platform scenery tag to the scenery palette if you haven't added it already. If you can't remember how to do this, refer to this section of the render importing guide
- Place the scenery down anywhere, and witness it spin!
Once you've gotten to this point, you are ready to look at adding a second custom animation and a .device_machine
tag, so that we can have the platform move us around! This next section is available here
Acknowledgements
Thanks to the following individuals for their research or contributions to this topic:
- PepperMan (Writing this guide)