The animation editor can be accessed from the main editor menu (when within a mod, press Tab to open the editor), then click on the text at the top. The menu entry is near the bottom.
Warning: Save your map before you enter the animation editor! Entering the animation editor unloads the current map and you will lose changes if you don't save!
Compatibility note: This article describes the OSE version. Older versions have a severely crippled animation editor that is a pain to use. Don't!
Getting started with animating
See animation tutorial.
User interface
The box in the middle displays the currently loaded animation. Only one animation can be loaded at a time.
The one line of text above and below the box display information about the currently selected bone.
The vertical lines at the bottom form a timeline. The default spacing is 0.1 seconds per tick.
Most of the buttons have a hotkey assigned but some don't.
Buttons without hotkeys
-
CLONEKEY - Duplicates the current keyframe and places the copy right to the current keyframe.
-
REVERSEANIM - Pretty much what it says on the tin. Doesn't move keyframes, just swaps the content. There is rerely a reason to use this.
-
DELETEKEY - Delete current keyframe. Can not delete the first keyframe. If you want to delete the first keyframe, move it to the right so that it's no longer the first keyframe, then delete it.
-
Unit DOWN/UP - Change timeline to show one tick every seconds.
-
Grid DOWN/UP - Change spacing between ticks to fit longer animations on the timeline.
Controls
General notes
- Click keyframes with the mouse to select them
- Drag keyframes with the mouse to move them on the timeline
Global keys
- F1 - Load animation by name
- Ctrl+S - Save current animation
- F5 - Load skin by name
- Warning: This applies the skin on top of the currently loaded skeleton. This does change textures recorded in the animation file if you save afterwards.
- M - Selection mode
- The first mode selects the bone that is closest to the mouse (red highlight).
- The second mode selects bones with the keyboard. Press arrow up or down to cycle through bones (blue highlight). There is probably no reason to use this mode, ever.
- B - Toggle bounding boxes and center points
- E - Toggle between normal edit mode and strip edit mode.
- Normal mode = grey background
- Strip mode = blue background
- Enter - Start animation (from the beginning)
- Shift+Enter - Stop animation and reset
- PageUp/PageDown - Cycle through animations
- Ctrl+C - Copy current keyframe
- Ctrl+V - Paste current keyframe
- Note that you can't copy/paste between multiple instances of Aquaria. This doesn't actually use the clipboard.
- Left/Right - Select prev/next keyframe
- L - Change interpolation style of the current keyframe.
- A - Set all bones to visible.
- Caution: Some bones may be intended to stay hidden. If you save while a previously hidden bone is visible the bone will not be saved as hidden! This means that on subsequent loads, the bone will show up normally. The only way to fix this is to go into a text editor, add the rq="0" property to the offending bone(s), and reload the file in the animation editor.
- Middle mouse button - Move viewport. This does not move the animation itself.
Bone-specific keys
These apply to whichever bone is currently selected. Each function can be combined with modifier keys that change which keyframes are affected.
- D - Negate rotation
- R - Reset rotation to 0
- P - Reset position to 0 (this snaps the bone right onto its parent).
- N - Hide bone. A hidden bone is excluded from mouse selection. This state is temporary and will not be saved to the file, unless the bone was set to hidden when the file was loaded (rq="0" property).
- Drag left mouse button - Move bone
- Drag right mouse button - Rotate bone
- Space - Copy bone configuration (position, rotation, strip data) to the next keyframe.
Modifier keys
- None pressed - Affect only the current keyframe, in the current animation.
- Shift - Affects all keyframes in the current animation (= all keyframes on the timeline).
- Shift+Ctrl - Affects all keyframes in all animations.
Workflow
TODO
Normal mode
TODO
Strip edit mode
TODO
Grid edit mode (2023 update)
TODO
Anatomy of an Animation (XML) file
Example:
<AnimationLayers>
<AnimationLayer />
</AnimationLayers>
<Bones>
<Bone idx="1" gfx="singbulb/bulb" pidx="-1" name="bulb" fh="0" fv="0" gc="1" cr="0" cp="0 0" />
<Bone idx="0" gfx="singbulb/base-0001" pidx="-1" name="base" fh="0" fv="0" gc="1" cr="0" cp="0 0" />
<Bone idx="2" gfx="Particles/glow" pidx="1" name="glow" fh="0" fv="0" gc="0" cr="0" cp="0 0" sz="6 6" rb="0"/>
</Bones>
<Animations>
<Animation name="idle">
<Key e="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 -1 -48 0 0 " />
</Animation>
</Animations>
Animation layers
An animation is made of multiple simultaneously playing layers that can include or ignore certain bones, eg. you can play one animation that affects the upper body while playing another animation that affects the legs.
At least one animation layer must exist. In most cases you will not need special behavior so you can leave it at the default:
<AnimationLayers>
<AnimationLayer />
</AnimationLayers>
When nothing is specified, an animation layer affects all bones.
Compare this to the animation layers in naija.xml:
<AnimationLayers>
<AnimationLayer />
<AnimationLayer ignore="6 7 8 9 12 13 " />
<AnimationLayer ignore="0 2 3 4 5 6 7 8 9 10 11 12 13 " />
<AnimationLayer name="flourish" />
<AnimationLayer />
<AnimationLayer ignore="0 1 2 4 5 6 7 8 9 10 12 13 " />
<AnimationLayer ignore="6 7 8 9 12 13 " />
<AnimationLayer ignore="0 2 3 4 5 6 7 8 9 10 12 13 15 16 " name="HeadOverride" />
</AnimationLayers>
Each anim layer is indexed starting from 0. Higher (ie. further down) animation layers override lower anim layers, ie. the topmost anim layer with an active playing animation wins.
An animation layer that does not actively play an animation is ignored.
This is evaluated for each bone separately.
Note that the animation editor always plays its animation on layer 0, therefore you should always leave it at the default <AnimationLayer />
and not exclude any bones.
NB: The ANIMLAYER_* constants as defined by ScriptInterface.cpp refer directly to the animation layers as defined by naija.xml. It's recommended not to change the anim layers in naija.xml because the game expects them to exist in this order.
To modify the anim layers: Use a text editor! The animation editor does not touch them.
Bones
The <Bones>
section has all the bones that take part in an animation.
This section can also not be modified with the animation editor and you'll need a text editor to modify bones.
Bone attributes (and their defaults)
- idx - Numeric index of the bone. Used by Lua function entity_getBoneByIdx(). Must be unique. Does not need to be ordered, and you can leave holes in your numbering, but don't use negative indices.
- gfx ("") - The texture name to use (without file extension, as usual).
- name ("") - Sometimes it's useful to reference bones by name. Leave empty if not needed. It's certainly easier to name a bone "Head" than to remember the ID for each different skeleton that has a head. Used by Lua function entity_getBoneByName().
- pidx - idx of parent Bone. Set this to attach this bone to a parent, so that when the parent moves, this bone moves with it.
Think of it like your arm (attached to the body), and when you move it around the lower arm follows, and the hand follows the lower arm, etc.
(The skeleton is actually a Scene graph).
Usepidx="-1"
to denote a root bone (that is not attached to a parent). You need at least one root bone in each animation.
The parent bone obviously needs to exist or bad things may happen. - fh (0) - Set to 1 to flip bone horizontally. It is recommended not to use this and make a flipped version of the texture instead.
- fv (0) - Set to 1 to flip bone vertically. Definitely avoid using this because it doesn't do what you think it should do.
- gc (0) - Set to 1 to generate a collision mask for this bone. Use for bones that should partake in skeletal collision.
(See also:entity_handleShotCollisionsSkeletal
and theentity_collideSkeletal*()
family of Lua functions). - cr (0) - Collide radius. Can be used instead of
gc=
if a simple circle collision is enough. - sz ("1 1") - Scale factor; TWO values. Affects children. Use the same value for both X and Y direction for a uniform scale.
- rq (1) - Render quad. Set to 0 to not render the bone by default. Can be changed later with
bone_setVisible()
. - rbp (0) - Render before parent. Normally a bone is drawn after its parent (ie. on top of it). This flag inverts this so the bone is drawn underneath the parent.
- sel (1) - Selectable. Set to 0 to make this bone non-selectable to make sure you don't accidentally grab and move it.
- pass (0) - Layer pass. Higher numbers get drawn more in front regardless of the parent/child hierarchy. This is an advanced setting, make sure you understand the draw order first!
- prt ("") - Attaches particle effects to the bone. The particle effects are initially disabled but can be controlled with a bone command.
The string is a list of (slot, name) pairs, ie.
"0 explode 1 fire"
will allocate two slots, IDs 0 and 1 with a particle effect each. - strip - Enable bone
strip
mode. Two numbers; the first is 1 if vertical, 0 if horizontal; the second is the number of segments. Incompatible with grid. - grid - Enable bone
grid
mode. Two numbers, the size of the grid points in X and Y directions. Each must be >= 2. Incompatible with strip. - io ("0 0") - Two numbers. Sets internal offset of bone (ie. shift away from the center without changing the rotation point). Similar to offx / offy, but does not affect children.
Less commonly used attributes:
- c (1) - Enable collision. Can be set to 0 to explicitly disable collision even if
cr
orgc
are set. - offx, offy (0) - Offset the bone from its center. This offset affects child bones.
- rt (0) - Repeat texture. Set to 1 to enable repeating texture mode. Can use
quad_setRepeatTexture()
instead.
Animation section
Each animation has an entry here, of which each is a list of keyframes.
The <Key e=...
attribute stores the rotation, position and whatever values for all bones in the animation.
You can use <Key cmd="..."
to issue additional bone commands when that keyframe is reached.
Bone commands
All bone commands are permanent and once used, change the skeleton for all subsequently played animations for a single entity. Use them carefully and only if really necessary.
Each bone command starts with the bone index it refers to (as set via <Bone idx=
), followed by a string starting with "AC" that specifies the action.
Additional parameters may follow; depends on the command.
- bone AC_PRT_LOAD file - Change a particle effect slot to a different particle effect. Slot must have been allocated first.
- bone AC_SND_PLAY file - Plays a sound effect. It's not attached to a bone or anything, so the bone ID is useless here.
- bone AC_FRM_SHOW frameID - Change bone texture. Uses the texture of the frame with that ID. Need to have bone frames specified, otherwise this has no effect.
- bone AC_PRT_START slot - Start particle effect in that slot ID.
- bone AC_PRT_STOP slot - Stop particle effect in that slot ID.
- bone AC_SET_PASS pass - Change layer pass of bone.
- bone AC_RESET_PASS - Reset layer pass to the original
<Bone pass=
value
Unknown commands are ignored.
When you use bone commands, make VERY sure they follow the format (bone cmd [params...]), because if one thing is read incorrectly it will cause the parser to trip up, populate the wrong fields, not recognize anything, and subsequently ignore everything.
Note that instead of a single long string:
<Key cmd="1 AC_FRM_SHOW 3 27 AC_FRM_SHOW 1 7 AC_SET_PASS 1 12 AC_SET_PASS 1 8 AC_SET_PASS 2 9 AC_SET_PASS 2" e="..." />
You can break it into multiple lines:
<Key cmd="
1 AC_FRM_SHOW 3
27 AC_FRM_SHOW 1
7 AC_SET_PASS 1
12 AC_SET_PASS 1
8 AC_SET_PASS 2
9 AC_SET_PASS 2
" e="..." />
The parser doesn't care and it's much easier to see the individual commands that way.
Skeleton draw order
Note that drawing a bone first means it may get overdrawn by other bones drawn after it. So to have bones appear on top of everything, you want them drawn as late as possible.
The following rules apply, in this order, recursively:
- For each layer pass:
- Scan the bone list top to bottom
- For each root bone (ie. those with
pidx="-1"
):- drawBone(root)
- For each root bone (ie. those with
- Scan the bone list top to bottom
And drawBone(b) does this:
- Scan the bone list top to bottom:
- If a bone has b as parent and
rbp="1"
:- drawBone(that bone)
- If a bone has b as parent and
- If b has
pass=
equal to current layer pass:- Draw b to the screen
- Scan the bone list top to bottom:
- If a bone has b as parent and
rbp="0"
:- drawBone(that bone)
- If a bone has b as parent and
If you want your skeleton to be drawn precisely in a specific way you need to take care of the correct bone ordering.
The layer pass depends on the layer that the entity is on. For most layers there is only a single pass with id 0, but by default LR_ENTITIES goes from passes -2 to 5, inclusive.
You can use the Lua function setLayerRenderPass()
to change a layer's begin and end pass but that comes at a cost of overall render performance.
Here are some rules of thumb that make the above easier to remember:
- Bones with a greater layer pass are drawn on top of bones with a lower layer pass
- Bones towards the end of the list are drawn later.
- Bones with
rbp="1"
are drawn earlier
Common problems, Q&A
-
Help, I've set
<Bone sz=
and now it's gone- Make sure you pass in TWO values, one for X and one for Y directions:
<Bone sz="1.5 1.5" .../>
- If it's only one value, the other is interpreted as zero, so it's squished infinitely thin and you can't see it.
- Make sure you pass in TWO values, one for X and one for Y directions:
-
Help, I'm moving one bone around and another unrelated bone follows whenever I release the mouse button?!
This can happen if two (or more) bones accidentally share the same ID. Check that bone IDs are unique. To fix this, pick the bone that is less annoying to animate, change its ID to something unique in a text editor, then reload the animation. You'll need to fix all animations but the Shift and Shift+Ctrl modifiers should help to get this done quickly. -
Help, i modified the anim XML by hand and now it doesn't load anymore!
Your edits probably added an error, making the XML invalid. Check that:- Each
<Tag>
is properly closed with</Tag>
- Each single-line tag is closed at the end (
<Bone attributes=... />
- note the trailing /) - Attributes appear only once in a tag (
<Bone idx="0" rq="1" gc="0" rq="0">
is invalid, sincerq
appears twice) - Attributes must be quoted,
idx=0
is invalid, should beidx="0"
.
- Each
-
Help, i modified the anim XML by hand and now the game crashes when trying to load it?!
- Check that all bones referenced with
pidx="..."
existpidx="-1"
for no parent is fine too
- Check that you didn't set any bone's
idx=
andpidx=
to the same value - Check that you didn't create a cycle - ie. if you have two bones like this:
<Bone idx="0" pidx="1" ... />
and<Bone idx="1" pidx="0" ... />
they try to set one another as parent and that will crash. Always make sure that following allpidx
links will eventually lead to a bone withpidx="-1"
.
- Check that all bones referenced with
-
I've set a bone's
pass=
to something other than 0, in the editor it's fine but in-game that bone doesn't show up?- Make sure the entity is spawned on LR_ENTITIES. This is the default if you don't move it in the entity script.
- If you really want the entity on another layer, make sure that that layer has the correct pass limits set -- use
setLayerRenderPass()
. - NB: The animation editor spawns its animation puppets on LR_ENTITIES. If you use non-standard layer pass limits then you'll want to make sure that the pass limits of LR_ENTITIES are set to cover the full range of render passes any of your animations use, otherwise you won't see those bones that exceed the pass limits.
-
I've set up bones to render across different layer passes and it's working in the editor, but in-game the passes are completely ignored?
- Make sure that the entity script doesn't call
entity_setRenderPass()
-- if the entity has a render pass set, all bones will use the render pass of the entity they belong to and ignore their own. This is special-cased in the engine for backwards compatibility reasons, and there's hardly any reason to set a render pass for an entire entity at once. Use bone passes and never set entity passes.
- Make sure that the entity script doesn't call