Runtime Skeletons
The Spine Runtimes provide access to bones, slots, attachments, and more. Skeletons can be customized and respond to the environment in many ways.
World transforms
Bones are arranged in a hierarchy where each bone is affected by its parent bones, all the way back to the root bone. For example, when a bone is rotated, all its child bones and all their children are also rotated. To accomplish this, each bone has a local transform consisting of:
x
andy
rotation
scaleX
andscaleY
shearX
andshearY
Starting at the root and proceeding with parent bones first, the local transform is used to compute a world transform for each bone. The world transform consists of:
a
,b
,c
, andd
This is a 2x2 matrix which encodes the combined rotation, scale and shear of the bone and all parent bones back to the root.a
andc
are the X axis,b
andd
are the Y axis.worldX
andworldY
This is the world position of the bone, where the world coordinate system is the coordinate system the root bone is positioned in.
The world transform can transform any point from the bone's local coordinates to world coordinates. For example, the vertices for a mesh attached to a bone are transformed by the bone's world transform. The resulting vertices are affected by the bone and all its parent bones. This mechanism is at the core of Spine's skeletal animation system.
The world transform can also do the reverse, transform any point from world coordinates to the bone's local coordinates.
updateWorldTransform
A bone's world transform is not normally modified directly. Instead, the local transform is modified and the world transform is computed using the local transform and the parent bone's world transform. Any time a bone's local transform is changed, the world transform for the bone and all its descendants must be recomputed by calling Bone updateWorldTransform. However, bones must be updated in the correct order so it is much more common to call Skeleton updateWorldTransform
which not only updates all the bones in the correct order, but also applies any constraints.
Applying an animation almost always modifies bone local transforms. Rendering a skeleton makes uses of the bone world transforms. Therefore it is very common to call updateWorldTransform
after applying an animation, before rendering occurs.
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
Procedural animation
Bones can be accessed and adjusted programmatically for a variety of effects. For example, the local rotation of a bone can be set so it points toward the mouse cursor for aiming. It can also be convenient to programmatically position an IK target bone, allowing IK constraints to adjust various bones with different IK mixes.
It is common to apply an animation, then adjust the bones:
...
state.update(delta);
state.apply(skeleton);
torso.rotation = ... // compute rotation for torso
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
}
If the world transform from the animation pose is may be needed to adjust the bone, updateWorldTransform
can be called before the adjustment, then again after the local transform is changed:
...
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
torso.rotation = ... // compute rotation for torso
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
Setup pose
Animations are applied relative to the setup pose. This means if a BoneData is adjusted, it will affect all animations for all skeletons that use that BoneData. Applying an animation uses the BoneData, so changes should be made before applying an animation:
...
torso.data.rotation = ... // compute rotation for torso
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
Bone positions
The world transform of a bone can be used to position game elements, such as a particles or other effects:
...
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
renderParticles(rightHand.worldX, rightHand.worldY);
In this example, an animation is applied, the world transforms are computed, the skeleton is rendered, then the world position of the "right hand" bone is used to draw particle effects. The world rotation and scale of the bone is also available, for example to shoot particles in the direction of the bone. In a similar manner, bone world transforms could be used to animate a UI by positioning, rotating, and scaling the UI elements using bones.
Generic rendering
Runtimes for a specific game toolkit are a full solution, they do everything including rendering. Generic runtimes are game toolkit agnostic and do everything except for the actual rendering. You only need to concern yourself with rendering if using a generic runtime.
To perform rendering for a generic runtime, the Skeleton class provides a drawOrder property which is a list of slots in the order they should be drawn. Rendering consists of retrieving the attachment from each slot, inspecting its type, and rendering it if necessary. The attachment types that need to be rendered are:
RegionAttachment
This has 4 vertices and is a quadrilateral (it is not always a rectangle).MeshAttachment
This has any number of vertices and triangles. Both are provided by the skeleton data, the renderer does not need to perform any triangulation.
Pseudo code for rendering looks like:
Attachment attachment = slot.attachment;
AtlasRegion region;
if (attachment is RegionAttachment) {
attachment.computeWorldVertices(slot.bone, vertices);
triangles = quadTriangles;
region = attachment.rendererObject;
} else if (attachment is MeshAttachment) {
attachment.computeWorldVertices(slot.bone, vertices);
triangles = attachment.triangles;
region = attachment.rendererObject;
}
if (texture != null) {
Texture texture = region.page.rendererObject;
draw(texture, vertices, triangles, slot.data.blendMode);
}
}
The rendererObject
property on the attachments is set by the AttachmentLoader when the skeleton data is loaded. This pseudo code assumes a Spine atlas is used, which is why the rendererObject
is an AtlasRegion.
The AtlasRegion has an AtlasPage which has its own rendererObject
property that is set by the TextureLoader when the atlas is loaded. The Texture class represents a game toolkit specific class.
Changing attachments
At any given time, a slot can have a single attachment or no attachment. The attachment for a slot can be changed by setting Slot attachment. Skeleton has a setAttachment convenience method which finds the slot and attachment objects by name. The slot's attachment will stay until changed again.
// Find the slot by name.
Slot slot = skeleton.findSlot("slotName");
// Get the attachment by name from the skeleton's skin or default skin.
Attachment attachment = skeleton.getAttachment(slot.index, "attachmentName");
// Sets the slot's attachment.
slot.attachment = attachment;
// Alternatively, the skeleton setAttachment method does the above.
skeleton.setAttachment("slotName", "attachmentName");
Attachments may be changed in other ways. Calling Skeleton setToSetupPose
or setSlotsToSetupPose
will change slot attachments. An animation may have key frames that change attachments. Calling Skeleton setSkin
may change attachments (see Skin changes).