Outdated documentation

The article below is outdated and has been replaced by the Spine Runtimes Guide.


Using the Spine Runtimes

This article explains the basics common to all official runtimes.

Animation data can be exported from Spine as JSON Example or binary. This data contains the bone, attachment, and animation information needed to render the animations just as they appear in Spine. There are many Official runtimes for various game toolkits that can load this data and render the animations. All of the official runtimes use the same class names and basic structure explained here.

Source code

The source code for the official runtimes is available on GitHub and licensing Spine grants permission to use the runtimes in your applications. All of the source code is provided, which is essential for such a fundamental component of your games.

The code examples below use pseudo code that is easily translated to runtimes in any language. See the runtime specific pages (README.md files) on GitHub for more specific documentation.

Overview

This is a high level overview of the various pieces that make up a runtime. Click for full resolution.

Export

Spine can export numerical data for skeletal animation in your games. It can also export video and image sequences for traditional sprite animation. Please note that only the full version of Spine can export.

Skeletal animation

To export animation data from Spine, click the logo in the top left of the Spine window and choose Export. Choose JSON or Binary (note: binary is not currently supported by most runtimes) . Choose a directory and Spine will create a file for each skeleton in the project containing all the bone, attachment, and animation data for that skeleton. The name of each file will be the name of the skeleton.

For JSON export, the Pretty print setting makes the JSON output more human-readable. The Format setting controls the type of JSON that is output. JSON outputs valid JSON. JavaScript outputs valid JavaScript, where field names are only quoted as necessary. Minimal outputs a JSON-like format where both field names and values are only quoted as necessary. This can only be used with JSON parsers that are very lenient, such as the libgdx JSON parser.

Sprite animation

Alternatively, Spine can export an animation as a sequence of images which can be used as traditional sprite animation. This has a number of disadvantages:

  • The images take up a lot of disk space.
  • The decompressed images take up a large amount of memory at runtime.
  • Sprite animation is not as smooth as interpolated Spine animation (sprite animation examples: Spineboy, Dragon ).
  • Individual pieces of a skeleton cannot be tinted or attached on the fly at runtime.
  • Procedural animation cannot be done.
  • Animations cannot be mixed or crossfaded.

Still, sprite animation may be useful in some situations. A Spine runtime is not needed for sprite animation, since rendering is simply drawing images.

Texture atlas

In addition to the skeleton data exported from Spine, a runtime also needs a texture atlas (also known as a sprite sheet) which contains the texture regions to draw. In most game toolkits, changing which texture is being drawn is costly. Performance is increased by binding a large texture once, then drawing many portions of the large texture.

When creating skeletons in Spine, individual images are used as this is the easiest way to organize many images. To create a texture atlas for use at runtime, the individual images need to be packed into one or more larger images. Spine has a built-in texture packing.

Atlas

Some runtimes use a game toolkit specific texture atlas. Other runtimes have their own texture atlas implementation called Atlas. This loads texture atlas data in the "libgdx" format. Multiple backing pages are supported, though be warned that this can cause additional texture binds at runtime.

An atlas is loaded this way:

TextureLoader textureLoader = ...
Atlas atlas = new Atlas("myAtlas.atlas", textureLoader);

Creating an atlas parses the atlas file, loading the data for where the regions are in the page images. The regions can be retrieved by name. The atlas supports whitespace stripping and rotation. Whitespace stripping removes the blank space at the edges of the packed regions and stores the original size. When drawn, the region must be offset by the blank pixels that were stripped. Rotation allows for better packing by allowing regions to be packed rotated by 90 degrees.

The atlas uses the TextureLoader to load the page images. The texture loader has two method which are used to create and dispose textures:

void load (AtlasPage page, String path)
void unload (Object texture)

Often a runtime will provide a texture loader that knows how to create and dispose for a specific game toolkit. An implementation might look like this:

void load (AtlasPage page, String path) {
   Texture texture = ...;
   page.rendererObject = texture;
}

void unload (Object rendererObject) {
   Texture texture = (Texture)rendererObject;
   texture.dispose();
}

The AtlasPage has a rendererObject field which is an object (or void*). It holds any game toolkit specific object. This object will be used by the game toolkit specific code that renders the skeleton. The load method also must set the width and height on the page. The unload method receives the rendererObject and can cast it as necessary to dispose of the resources.

Loading

JSON or binary data is loaded using SkeletonJson or SkeletonBinary. Both have the same interface, which is a readSkeletonData method that parses the data and returns a SkeletonData instance:

AttachmentLoader attachmentLoader = ...
SkeletonJson json = new SkeletonJson(attachmentLoader);
SkeletonData skeletonData = json.readSkeletonData("mySkeleton.json");

AttachmentLoader

The AttachmentLoader has a single method which is used to create attachment instances:

Attachment newAttachment (Skin skin, AttachmentType type, String name)

This returns a new attachment for the specified skin, attachment type, and name. Most attachments are a "region" attachment for rendering a texture region, but there can be other attachment types, such as a bounding box. The attachment loader allows attachments to be customized by application specific code. The built-in attachment types can be extended to customize rendering or other behavior.

The most common attachment loader has a texture atlas. In newAttachment it creates a RegionAttachment and populates it by looking up a texture region in the atlas using the attachment name. Code to do that might look like this:

TextureAtlas textureAtlas = ...

Attachment newAttachment (Skin skin, AttachmentType type, String name) {
   if (type != AttachmentType.region)
      throw new Error("Unknown attachment type: " + type);
   RegionAttachment attachment = new RegionAttachment(name);
   TextureRegion region = textureAtlas.findRegion(name);
   if (region == null) throw new Error("Region not found in atlas: " + name);
   attachment.rendererObject = region;
   return attachment;
}

Note the rendererObject field is runtime specific. Each runtime may have a different way of storing the texture region on the RegionAttachment. The runtime specific rendering needs to know how to access the texture region.

The attachment loader only creates a new attachment. It may do some configuration, such as setting a texture region, but additional configuration is done by the JSON or binary loader. For example, the loader may have code like this:

Attachment attachment = attachmentLoader.newAttachment(skin, type, name);
if (type == AttachmentType.region) {
   RegionAttachment regionAttachment = (RegionAttachment)attachment;
   regionAttachment.x = ...;
   regionAttachment.y = ...;
   regionAttachment.scaleX = ...;
   regionAttachment.scaleY = ...;
   regionAttachment.rotation = ...;
   regionAttachment.width = ...;
   regionAttachment.height = ...;
   regionAttachment.updateOffset(regionAttachment);
}

AtlasAttachmentLoader

If using a runtime that has a Spine atlas, an AtlasAttachmentLoader is provided:

TextureLoader textureLoader = ...
Atlas atlas = new Atlas("myAtlas.atlas", textureLoader);
AtlasAttachmentLoader attachmentLoader = new AtlasAttachmentLoader(atlas);
SkeletonJson json = new SkeletonJson(attachmentLoader);
SkeletonData skeletonData = json.readSkeletonData("mySkeleton.json");

Since this is very commonly used, the JSON and binary loaders for most runtimes will accept an atlas and create an atlas attachment loader internally:

TextureLoader textureLoader = ...
Atlas atlas = new Atlas("myAtlas.atlas", textureLoader);
SkeletonJson json = new SkeletonJson(atlas);
SkeletonData skeletonData = json.readSkeletonData("mySkeleton.json");

Scaling

A scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations:

AttachmentLoader attachmentLoader = ...
SkeletonJson json = new SkeletonJson(attachmentLoader);
json.scale = 2;
SkeletonData skeletonData = json.readSkeletonData("mySkeleton.json");

This causes a skeleton to be drawn at a different size, twice as large in this example. This can be useful when using different sized images than were used when designing the skeleton in Spine. For example, if using images that are half the size than were used in Spine, a scale of 0.5 can be used. This is commonly used for games that can run with either low or high resolution texture atlases.

Using a loader scale changes the units used. For example, if a bone has a local position of 50,100 then with a scale of 2 it will be changed at load time to 100,200. This can be useful when not using a pixel unit scale at runtime, such as with Box2D.

A skeleton can also be scaled without affecting the units:

AttachmentLoader attachmentLoader = ...
SkeletonJson json = new SkeletonJson(attachmentLoader);
SkeletonData skeletonData = json.readSkeletonData("mySkeleton.json");
BoneData root = skeletonData.findBone("root");
root.scaleX = 2;
root.scaleY = 2;

In this case, if a bone has a local position of 50,100 then it will remain at that position. When the bone's world SRT (scale, rotation, and translation) is computed (explained below), its position will be scaled.

SkeletonData

SkeletonData stores all the skins and animations as well as the setup pose's bone SRT, slot colors, and which slot attachments are visible. The setup pose is how the skeleton looks in Spine's Setup Mode.

SkeletonData and all other classes whose name ends with "Data" store information that is (typically) constant across multiple instances of the same skeleton. The data may be quite large, so this architecture avoids the need to load the same data multiple times.

Field Description
name The name of the skeleton as it appears in Spine.
bones A `BoneData` list, ordered so a parent always comes before all children.
slots A `SlotData` list, ordered using the slot draw order.
animations A list of all `Animation` for the skeleton.
skins A list of all `Skins` for the skeleton.
defaultSkin The skin containing all attachments that aren’t in a skin in Spine.
Method Parameters Description
findBone boneName Finds a bone data by name. This does a string comparison for every bone.
findBoneIndex boneName Finds a bone data index by name. This does a string comparison for every bone.
findSlot slotName Finds a slot data by name. This does a string comparison for every slot.
findSlotIndex slotName Finds a slot data index by name. This does a string comparison for every slot.
findSkin skinName Finds a skin by name. This does a string comparison for every skin.
findAnimation animationName Finds an animation by name. This does a string comparison for every animation.

BoneData

BoneData stores the hierarchy of bones in the setup pose and their SRT.

Field Description
name The name of the bone as it appears in Spine.
parent The parent `BoneData`.
length The length of the bone. This is normally used solely to draw the bone.
x,y The local position of the bone relative to the parent bone.
rotation The local rotation of the bone.
scaleX,scaleY The local scale of the bone.
inheritRotation True if the local rotation is relative to the parent bone.
inheritScale True if the local scale is relative to the parent bone.

SlotData

SlotData stores the slot color and which attachment is visible in the setup pose.

Field Description
name The name of the slot as it appears in Spine.
boneData The bone the slot belongs to.
r,g,b,a The color in the setup pose to tint the image for the slot.
attachmentName The name of the attachment visible for the slot in the setup pose, or null.
additiveBlending True if a region attachment in the slot should use additive blending when rendered.

Skin

Skin is used to find an attachment for a slot by a name. It is simply a map where the key is a slot and a name and the value is an attachment. The name used in the key does not have to be the name of the attachment. This allows code and animations to set attachments by name without knowing what attachment is actually used.

For example, a skin might have a key [slot:head,name:head] and a value for that key [attachment:head-fish]. Another skin might have a key [slot:head,name:head] and a value [attachment:head-donkey]. The skin allows the name in the skin ("head") to be used without knowing which attachment is actually used ("head-fish" or "head-donkey").

All attachments that are not in a skin in Spine will appear at runtime in a skin named "default". When a skeleton needs to find an attachment by name, it first looks in its skin. If the attachment is not found, then it looks in the default skin.

See the skins video for configuring skins in Spine. See Using skins for how to apply skins at runtime.

Field Description
name The name of the skin as it appears in Spine.
Method Parameters Description
getAttachment slotIndex, name Returns the attachment for the slot and name. `slotIndex` is the index in the skeleton data’s slot list.

Animation

Animation has a list of timelines. Each timeline has a list of times and values which represent keyframes and knows how to apply the values to a skeleton for a given time.

Field Description
name The name of the animation as it appears in Spine.
duration The duration of the animation in seconds. This is computed by the JSON or binary loader to be the time of the last key, but can be adjusted manually.
timelines A list of timelines.
Method Parameters Description
apply skeleton, time, loop Applies all keyframe values, interpolated for the specified time. Any current values are overwritten.
mix skeleton, time, loop, alpha Applies all keyframe values, interpolated for the specified time and mixed with the current values. `alpha` is 0-1 and controls what percentage of the keyframe values are used. See below.

Skeleton

Skeleton has a reference to a SkeletonData and stores the state for skeleton instance, which consists of the current pose's bone SRT, slot colors, and which slot attachments are visible. Multiple skeletons can use the same SkeletonData (which includes all animations, skins, and attachments).

Field Description
data The `SkeletonData` for the skeleton.
bones A `Bone` list, ordered the same as the skeleton data.
slots A `Slot` list, ordered the same as the skeleton data.
drawOrder A `Slot` list, initially ordered the same as the skeleton data. The order can be manipulated to change the order slots are drawn.
skin The currently active skin, or null.
r,g,b,a The color to tint the entire skeleton.
time Increases via `update(delta)` and allows slots to know how long an attachment has been visible.
flipX,flipY Flips the rendering of the skeleton horizontally and/or vertically.
x,y The drawing position of the skeleton in world coordinates.
Method Parameters Description
updateWorldTransform   Computes the world SRT from the local SRT for each bone. See below.
setBonesToSetupPose   Sets the bones to the setup pose, using the values from the `BoneData` list in the `SkeletonData`.
setSlotsToSetupPose   Sets the slots to the setup pose, using the values from the `SlotData` list in the `SkeletonData`.
setToSetupPose   Sets the bones and slots to the setup pose.
findBone boneName Finds a bone by name. This does a string comparison for every bone.
findBoneIndex boneName Finds a bone index by name. This does a string comparison for every bone.
findSlot slotName Finds a slot by name. This does a string comparison for every slot.
findSlotIndex slotName Finds a slot index by name. This does a string comparison for every slot.
setSkin skinName Finds a skin by name and makes it the active skin. This does a string comparison for every skin. Note that setting the skin does not change which attachments are visisble. See Skin changes.
getAttachment slotName, attachmentName Returns the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin,
setAttachment slotName, attachmentName Sets the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin,
update delta Increments the skeleton’s `time` field.

Bone

Bone has a reference to a BoneData and stores the hierarchy of bones and their SRT for the current pose.

Field Description
data The `BoneData` for the bone.
parent The parent `Bone`.
x,y The local position of the bone relative to the parent bone.
rotation The local rotation of the bone.
scaleX,scaleY The local scale of the bone.
worldX,worldY The world position of the bone. Readonly.
worldRotation The world rotation of the bone. Readonly.
m00,m01,m10,m11 The 2×2 world rotation matrix of the bone. Readonly.
Method Parameters Description
updateWorldTransform flipX,flipY Computes the world SRT from the local SRT for this bone. Usually called by `Skeleton updateWorldTransform`.
setToSetupPose   Sets the bone to the setup pose, using the values from the `BoneData`. Usually called by `Skeleton setBonesToSetupPose`.

Slot

Slot has a reference to a SlotData and stores the slot color and which attachment is visible for the current pose.

Field Description
data The `SlotData` for the slot.
bone The bone the slot belongs to.
r,g,b,a The color to tint the image for the slot.
attachment The attachment visible for the slot, or null.
attachmentTime The time in seconds the attachment has been visible.
Method Description
setToSetupPose Sets the slot to the setup pose, using the values from the `SlotData`. Usually called by `Skeleton setSlotsToSetupPose`.

Attachments

An attachment has a name and a type, but otherwise can be anything: a texture region, bounding box, etc.

Field Description
name The name of the attachment as it appears in Spine.
type The type of the attachment. This enables the renderer to know if or how it should draw the attachment.

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 calling setAttachment on the Skeleton or a Slot. The attachment will stay until changed again.

Skeleton skeleton = ...
// Finds the slot by name, finds the attachment by name in the skeleton's skin or default
// skin and sets it on the slot.
skeleton.setAttachment("slotName", "attachmentName");
// Attachments can be gotten from a skin or created manually (though this is advanced).
Attachment attachment = ...
// An attachment can be set directly on a slot.
skeleton.findSlot("slotName").setAttachment(attachment);

Attachments may be changed in other ways. Calling Skeleton setSlotsToSetupPose will change attachments. An animation may have keyframes that change attachments. Calling Skeleton setSkin may change attachments (see Skin changes).

RegionAttachment

The most common attachment type is the RegionAttachment, which has a texture region, size, and offset SRT relative to the bone it is attached to. Usually the attachment name is used to look up the texture region in a texture atlas, but this behavior is left up to the AttachmentLoader.

Field Description
x,y The offset position of the attachment relative to the bone.
rotation The offset rotation of the attachment relative to the bone.
scaleX,scaleY The offset scale of the attachment relative to the bone.
width,height The size the texture region will be drawn.
offset An array of 8 values that are the world positions of the 4 vertices as computed by `updateOffset`.
uvs An array of 8 values that are the texture coordinates of the 4 vertices as computed by `setUVs`.
Method Parameters Description
updateOffset   Uses the size and offset SRT to compute the `offset`.
setUVs u,v,u2,v2,rotate Sets the `uvs`. If `rotate` is true, the UVs are rotated 90 degrees.
computeVertices x,y,bone,vertices Uses the `offset` and bone world SRT to populate the vertices. The vertices are incremented by `x,y`, which is typically the skeleton position.

Creating attachments

Attachments can be created programmatically. This can be useful when there are many attachments that would be tedious to create manually in Spine.

In Spine the offset SRT is defined by where an image is placed on a bone. When creating a RegionAttachment programmatically, some convention is needed to know where to place the attachment. For example, all attachments could be the same size and have the same offset SRT, then the art must positioned within this size so it appears in the correct place on the bone.

Rendering

Rendering is done by iterating the Skeleton drawOrder field, which is a Slot list. The renderer checks the type of each attachment and draws the attachments it knows about. Typically it knows how to draw a texture region from RegionAttachment.

Applying animations

Animation apply overwrites the current pose of a Skeleton with the pose from the animation at a specific time:

Skeleton skeleton = new Skeleton(skeletonData);
Animation walkAnimation = skeletonData.findAnimation("walk");
float animationTime = 0;
...
function render (float delta) {
   animationTime += delta;
   walkAnimation.apply(skeleton, animationTime, true); * true is for loop
   skeleton.updateWorldTransform();
   renderSkeleton(skeleton);
}

In this example, the animationTime is incremented by the delta time since the last render. Next, apply is called which changes the bones and slots that have keyframes in the animation. If bones are changed, only the local SRT is changed. Next, updateWorldTransform is called to compute the world SRT for each bone. Lastly, the skeleton can now be rendered, which uses the world SRT for each bone.

The time passed to the apply method controls the speed of the animation. It can be decremented to play the animation backward. An animation is complete when the time is greater than the animation duration.

Because the animation time is not stored inside the animation, the animation is stateless and can be used for any number of skeleton instances.

Mixing animations

Animations can be mixed, which is often used for crossfading when animations change. Animation mix is similar to apply, accept instead of overwriting the current pose with the animation pose, it positions bones by interpolating between the current pose and the animation pose:

Skeleton skeleton = new Skeleton(skeletonData);
Animation walkAnimation = skeletonData.findAnimation("walk");
Animation jumpAnimation = skeletonData.findAnimation("jump");
float animationTime = 0;
...
function render (float delta) {
   animationTime += delta;
   walkAnimation.apply(skeleton, animationTime, true);
   jumpAnimation.mix(skeleton, animationTime, true, 0.75); * 0.75 is the alpha parameter
   skeleton.updateWorldTransform();
   renderSkeleton(skeleton);
}

In this example, first the walk animation is applied. This overwrites the skeleton's current pose. Next, mix is called which will compute the pose for the jump animation, then adjust the bones so they are 75% of the way between the current pose and the jump pose.

To crossfade animations using mix, the old one is applied, the new one is mixed, and the alpha parameter is adjusted from 0 to 1 over time. This causes the old animation to contribute less and less to the pose. Once the alpha parameter reaches 1, the old animation is completely overwritten by the new animation and the crossfade is complete.

AnimationState

Since applying animations with crossfading is very common, AnimationState makes it more convenient. First AnimationStateData is configured with the durations to crossfade each pair of animations. AnimationStateData is stateless and can be used with multiple AnimationState instances. Next, AnimationState takes an AnimationStateData and is told what animation to use. When the AnimationState animation changes, it does the mixing automatically.

Skeleton skeleton = new Skeleton(skeletonData);
AnimationStateData stateData = new AnimationStateData(skeletonData);
stateData.setMix("walk", "jump", 0.2f);
stateData.setMix("jump", "walk", 0.4f);
AnimationState state = new AnimationState(stateData);
state.setAnimation(0, "walk", true); * trackIndex, name, loop
...
function render (float delta) {
   state.update(delta);
   state.apply(skeleton);
   skeleton.updateWorldTransform();
   renderSkeleton(skeleton);
   if (spacebar()) state.setAnimation(0, "jump", true);
}

In this example, the AnimationStateData is configured, an AnimationState is created, and the initial animation is set to "walk". In the game loop, update is called so the AnimationState can keep track of the time. Next, apply is called which will apply the animations to the skeleton. updateWorldTransform computes the world SRT of the bones, then the skeleton is rendered just as it was before. If spacebar is pressed, the animation is changed to "jump". The AnimationState will automatically crossfade the animation change.

AnimationState has the notion of "tracks" which are indexed starting at zero. The animation for each track is applied in sequence each frame, allowing animations to be applied on top of each other. Also, each track can have animations queued for later playback:

AnimationState state = new AnimationState(stateData);
state.setAnimation(0, "walk", false);
state.addAnimation(0, "jump", false, 0); * trackIndex, name, loop, delay
state.addAnimation(0, "fly", true, 0);

In this example, first the "walk" animation is played. When the end of that animation is reached (loop is ignored), the "jump" animation is played. When the end of that animation is reached, the "fly" animation is played. The mix durations are taken into account when computing the end of an animation.

The delay parameter specifies how much time the previous animation plays before the animation is changed. A delay <= 0 is special and uses the duration of the previous animation plus the delay.

AnimationState state = new AnimationState(stateData);
state.setAnimation(0, "walk", true);
state.addAnimation(0, "jump", false, 2.5);
state.addAnimation(0, "fly", true, -0.5);

In this example, first the "walk" animation is played. After 2.5 seconds, the "jump" animation is played. 0.5 seconds before the end of that animation is reached, the "fly" animation is played.

Field Description
animation The current animation for the animation state.
time The current time for the animation state. When a new animation is set, the time is reset to zero.
Method Parameters Description
setAnimation trackIndex,animation,loop Sets the current animation. Any queued animations are cleared.
addAnimation trackIndex,animation,loop,delay Queues an animation to be played after a delay. If delay is <= 0, the duration of previous animation is used plus the negative delay.
clearTrack trackIndex Sets the current animation of a track to null and clears all queued animations.
update delta Increases the animation state's `time` field.
apply skeleton Poses the skeleton using the current animation and time.
isComplete   Returns true of the time is greater than the current animation's duration.

Animation changes

An animation only affects the bones and slots for which it has keyframes. This allows the state of the skeleton to be fully controlled by the application.

When animations are applied in sequence, a previous animation may have made changes to bones or slots that a subsequent animation does not have keyframes for. In some cases, the changes from the first animation may not be desired when the second animation is applied.

This can be solved be keying everything at the start of the second animation that the first animation affects. With many animations this quickly leads to everything being keyed at the start of every animation. This is suboptimal because each property that is keyed adds a small amount of overhead when the animation is applied each frame.

Another solution is to call setToSetupPose, setBonesToSetupPose, or setSlotsToSetupPose on the skeleton when the current animation is changed (or otherwise write code that changes the slots and bones as needed). This ensures a previous animation has not left the skeleton in an undesirable state without requiring a large number of keyframes. However, this will cause bones that aren't keyed in the second animation to be instantly set to the setup pose state. AnimationState won't be able to do crossfading for those bones.

Combining animations

Multiple animations can be applied in sequence to animate parts of the skeleton differently:

Skeleton skeleton = new Skeleton(skeletonData);
AnimationState state = new AnimationState(stateData);
state.setAnimation(0, "walk", true);
state.setAnimation(1, "shoot", false);
...
function render (float delta) {
   state.update(delta);
   state.apply(skeleton);
   skeleton.updateWorldTransform();
   renderSkeleton(skeleton);
}

In this example, first the "walk" animation is applied, then "shoot" is applied. If "shoot" only has keyframes for bones in the torso, the legs will not be affected and will remained posed by the "walk" animation.

Creating animations

Animations can be edited or created programmatically. For example, this might be useful to adjust an existing animation based on a character's distance from an object they are interacting with. Timelines are configured with keyframe values, added to the animation, then the animation is applied as normal.

Manipulating bones

Bones can be accessed programmatically. They can be manipulated to perform procedural animation:

Skeleton skeleton = new Skeleton(skeletonData);
Bone torso = skeleton.findBone("torso");
AnimationState state = new AnimationState(stateData);
state.setAnimation(0, "walk", true);
...
function render (float delta) {
   state.update(delta);
   state.apply(skeleton);
   torso.rotation = ... // compute rotation for target
   skeleton.updateWorldTransform();
   renderSkeleton(skeleton);
}

In this example, the "torso" bone's local rotation is adjusted. This is done after AnimationState apply poses the skeleton, but before Skeleton updateWorldTransform computes the world SRT for the bones. This can be used for dynamic behavior, such as having the skeleton look toward the mouse cursor.

All animations are relative to the setup pose. This means the BoneData can be adjusted to affect all animations:

Skeleton skeleton = new Skeleton(skeletonData);
Bone torso = skeleton.findBone("torso");
AnimationState state = new AnimationState(stateData);
state.setAnimation(0, "walk", true);
...
function render (float delta) {
   torso.data.rotation = * compute rotation for target
   state.update(delta);
   state.apply(skeleton);
   skeleton.updateWorldTransform();
   renderSkeleton(skeleton);
}

This example is similar to the last one, except this time the BoneData rotation is modified. This is done before the animation is applied, because the animation uses the BoneData when computing the pose to apply.

The world SRT of a bone can be used to position game elements:

Skeleton skeleton = new Skeleton(skeletonData);
Bone rightHand = skeleton.findBone("right hand");
AnimationState state = new AnimationState(stateData);
state.setAnimation(0, "walk", true);
...
function render (float delta) {
   state.update(delta);
   state.apply(skeleton);
   skeleton.updateWorldTransform();
   renderSkeleton(skeleton);
   renderParticles(rightHand.worldX, rightHand.worldY);
}

In this example, an animation is applied, the world SRT for the bones is computed, the skeleton is rendered, and then the world position of the "right hand" bone is used to draw particle effects. This can also be used for animation a UI by positioning UI elements. The world rotation and scale is also available.

Using skins

Skins allow all animations for a skeleton to be reused with different attachments.

For example, a skeleton has green attachments and purple attachments. An animation wants to change the "head" slot's attachment so the skeleton's eyes blink. If skins are not used, two animations are needed: one that attaches "blink-green" and one that attaches "blink-purple". This causes an explosion of the number of animations needed. If skins are used, only one animation is needed. It sets the "head" slot's attachment to "blink". If the "green" skin is active, "blink-green" is used. If the "purple" skin is active, "blink-purple" is used.

When not using animations that change attachments, skins are not needed. Skins can still be used to group attachments to make it easy to change how the skeleton looks, but this is not their primary goal. Application code can easily call setAttachment on the Skeleton or a Slot.

Skin changes

When setAttachment or getAttachment is called on a Skeleton, the skeleton finds the attachment by name by first looking in its skin (if any). If not found, it will look in the default skin of its SkeletonData. The skin for a Skeleton can be changed, which allows different attachments to be found by the same names.

When a new skin is set and the skeleton does not already have a skin, any attachments in the skin that are visible in the setup pose are attached.

When a new skin is set and the skeleton already has a skin, then attachments from the new skin are attached if the slot had the attachment from the old skin attached. Otherwise, no attachments are changed.

Creating skins

Skins can be created programmatically. This can be useful in the case where animations change attachments that are configurable by application code.

For example, a skeleton has a head that can be a dog head or a snake head. It also has wings that can be normal wings or burning wings. An animation changes both the head and wing attachments. To allow the skeleton to be configured with any combination of heads and wings, a skin can be created programmatically with the appropriate head and wing attachments.

This task may be simplified by creating skins in Spine for each head (dog, snake) and wing (normal, burning) solely to group the attachments. To configure the skeleton at runtime, get the attachments from the desired head and wing skins and put them in a new skin. Note: Spine can currently only show a single skin at once, so it won't be possible to preview the combined skin in the editor.