11 KiB
name, description
| name | description |
|---|---|
| spine-godot | spine-godot runtime for Godot 4 — Spine skeleton display, animation playback, bone/slot manipulation, skin mixing, 2D lighting, and runtime asset loading. Use when working with Spine skeletons, animations, SpineSprite nodes, SpineBoneNode, SpineSlotNode, or SpineAnimationTrack in this project. |
spine-godot Skill
The spine-godot runtime provides Godot nodes and APIs to load, display, animate, and manipulate skeletons exported from Spine. It wraps the spine-cpp runtime and exposes it to GDScript and C#.
Official docs: https://en.esotericsoftware.com/spine-godot Spine Runtimes Guide: https://en.esotericsoftware.com/spine-runtimes-guide Source: https://github.com/EsotericSoftware/spine-runtimes (under
spine-godot/)
Architecture Overview
spine-godot Runtime
├── SpineSprite — displays a skeleton from SkeletonDataResource
│ ├── SpineSkeleton — skeleton instance (bones, slots, attachments, skins, constraints)
│ ├── SpineAnimationState — animation playback, queuing, mixing via tracks
│ └── Signals — animation_started, animation_completed, animation_event, etc.
├── SpineBoneNode — follows or drives a bone's transform
├── SpineSlotNode — inserts nodes into drawing order or overrides slot materials
└── SpineAnimationTrack — animates a SpineSprite via Godot AnimationPlayer (C++ engine module only)
Resource Types
| Resource | Purpose |
|---|---|
SpineSkeletonFileResource |
Imported .skel or .spine-json skeleton data |
SpineAtlasResource |
Imported .atlas texture atlas data (uses normal map prefix n_ by default) |
SpineSkeletonDataResource |
Combines a SpineSkeletonFileResource + SpineAtlasResource + animation mix times. Shared across SpineSprite instances. |
Asset Management
Exporting from Spine Editor
Export these files from the Spine Editor:
- Skeleton data:
skeleton-name.spine-jsonorskeleton-name.skel(binary, smaller/faster — preferred). - Texture atlas:
skeleton-name.atlas - Atlas pages: One or more
.pngimage files.
Prefer
.skelbinary exports. If using JSON, use.spine-jsonextension (not.json). Pre-multiplied alpha atlases are not supported — Godot's default bleed handles artifacts.
Importing into Godot
- Drag
.skel/.spine-json,.atlas, and.pngfiles into a Godot project folder. - The importer creates a
SpineSkeletonFileResource, aSpineAtlasResource, and Godot texture resources. - Create a
SpineSkeletonDataResource: Right-click folder →New Resource...→SpineSkeletonDataResource. - Assign the atlas resource and skeleton file resource in the inspector. Set animation mix times here.
A
SpineSkeletonDataResourceshould be shared by allSpineSpriteinstances displaying the same skeleton. Do not use inline resources.
Updating Assets During Development
Overwrite the source files re-exported from Spine. Godot auto-detects changes and re-imports. If it doesn't, force re-import in the import settings panel.
Runtime Loading from Disk (modding)
Load skeleton and atlas files from arbitrary paths at runtime:
# Load skeleton file
var skeleton_file_res = SpineSkeletonFileResource.new()
skeleton_file_res.load_from_file("/path/to/skeleton.skel")
# Load atlas file
var atlas_res = SpineAtlasResource.new()
atlas_res.load_from_atlas_file("/path/to/skeleton.atlas")
# Create shared skeleton data resource
var skeleton_data_res = SpineSkeletonDataResource.new()
skeleton_data_res.skeleton_file_res = skeleton_file_res
skeleton_data_res.atlas_res = atlas_res
# Create sprite from data
var sprite = SpineSprite.new()
sprite.skeleton_data_res = skeleton_data_res
sprite.position = Vector2(200, 200)
sprite.get_animation_state().set_animation("animation", true, 0)
add_child(sprite)
Nodes
SpineSprite Node
Displays skeleton data from a SkeletonDataResource. Provides access to the animation state and skeleton instance.
Creating a SpineSprite
- Add a
SpineSpritenode via the+button in the scene panel. - Assign a
SkeletonDataResourcein the inspector. - Transform the node freely in the editor viewport.
Editor Features
- Debug view: Check bones, slots, attachments in the
Debugproperty section. Hover over parts in the viewport to see names. - Animation preview: Use the
Previewsection to set an animation and scrub through time with the slider. - Custom materials: Set per-blend-mode materials in the
Materialssection (applied to all slots). UseSpineSlotNodeto override per-slot. - Update Mode:
Process(default, every frame),Physics(fixed interval, 60fps),Manual(callupdate_skeleton()yourself in_process/_physics_process).
Animating a SpineSprite
extends SpineSprite
func _ready():
var animation_state = get_animation_state()
# Set an animation on track 0, looping
animation_state.set_animation("walk", true, 0)
# Queue an animation after a delay (mix duration implied by mix times)
animation_state.add_animation("idle", 0.5, true, 0)
# Set a reversed animation
var track_entry = animation_state.set_animation("walk", true, 0)
track_entry.set_reverse(true)
# Mix to an empty animation (reset to setup pose)
animation_state.set_empty_animation(0, 0.5)
animation_state.add_empty_animation(0, 0.5, 0.5)
# Clear tracks
animation_state.clear_track(0) # clear one track
animation_state.clear_tracks() # clear all tracks
# Reset skeleton poses
get_skeleton().set_to_setup_pose() # bones + slots
get_skeleton().set_slots_to_setup_pose() # slots only
Animation State API
| Method | Purpose |
|---|---|
set_animation(name, loop, track) |
Play animation. Returns SpineTrackEntry. |
add_animation(name, delay, loop, track) |
Queue animation after delay. Returns SpineTrackEntry. |
set_empty_animation(track, mix_duration) |
Mix out to empty pose immediately. |
add_empty_animation(track, mix_duration, delay) |
Queue empty animation mix-out. |
clear_track(track) |
Immediately clear one track. |
clear_tracks() |
Immediately clear all tracks. |
Important: Do not hold
SpineTrackEntryreferences across frames. They are reused internally and become invalid once the animation completes.
SpineTrackEntry Properties
| Method | Purpose |
|---|---|
set_reverse(true) |
Play animation in reverse. |
set_mix_duration(seconds) |
Override mix duration. |
set_alpha(value) |
Mix alpha for blending (0-1). |
set_time_scale(scale) |
Animation speed multiplier. |
set_track_time(time) |
Jump to specific time in seconds. |
SpineSprite Signals
Animation state signals:
animation_started(track_entry)— animation started playing.animation_interrupted(track_entry)— track cleared or new animation set.animation_completed(track_entry)— animation finished one full loop.animation_ended(track_entry)— animation will never be applied again.animation_disposed(track_entry)— track entry disposed.animation_event(track_entry, event)— user-defined Spine event triggered.
Lifecycle signals:
before_animation_state_update— before animation state is updated with delta.before_animation_state_apply— before animation state is applied to skeleton pose.before_world_transforms_change— before bone world transforms update. Use to set bone transforms manually.world_transforms_changed— after bone world transforms update.
Mix-and-Match Skins
Combine multiple skins to create custom avatars:
var custom_skin = new_skin("custom-skin")
var data = get_skeleton().get_data()
custom_skin.add_skin(data.find_skin("skin-base"))
custom_skin.add_skin(data.find_skin("nose/short"))
custom_skin.add_skin(data.find_skin("eyes/violet"))
custom_skin.add_skin(data.find_skin("hair/brown"))
custom_skin.add_skin(data.find_skin("clothes/hoodie-orange"))
get_skeleton().set_skin(custom_skin)
get_skeleton().set_slots_to_setup_pose()
Getting and Setting Bone Transforms
# Get a bone's global transform in Godot canvas space
var transform = get_global_bone_transform("bone_name")
# Set a bone's transform (must be called before world transforms update)
set_global_bone_transform("bone_name", Transform2D(0, Vector2(100, 200)))
When manually setting bone transforms, connect to before_world_transforms_change signal. Alternatively, use SpineBoneNode for simpler bone manipulation.
SpineBoneNode
A child node of SpineSprite that either follows or drives a bone.
Must be a direct child of a
SpineSprite.
Inspector properties:
Bone Name— dropdown of all available bones.Bone Mode—Follow(node moves with bone) orDrive(node controls bone position).Enabled— toggle following/driving on/off.
Use cases:
- Drive mode: Control a bone via mouse position or other input (e.g., aiming).
- Follow mode: Attach other nodes (CollisionShape, Marker2D) to follow a bone.
See examples: example/05-mouse-following (drive), example/06-bone-following (follow).
SpineSlotNode
A child node of SpineSprite that inserts children into the skeleton's drawing order or overrides a slot's materials.
Must be a direct child of a
SpineSprite.
Inspector properties:
Slot Name— dropdown of all available slots.Materials— custom materials that override the slot's default materials.
Use cases:
- Attach particle systems, custom sprites, or other
SpineSpritenodes on top of a slot. - Override materials per-slot (overrides
SpineSprite's global materials).
See examples: example/07-slot-node (drawing order), example/09-custom-material (material override).
SpineAnimationTrack (C++ Engine Module Only)
Animates a SpineSprite via Godot's AnimationPlayer and animation editor. Ideal for cutscenes.
Experimental. Not available in the GDExtension.
Must be a direct child of a
SpineSprite.
- Creates a child
AnimationPlayerautomatically. - Key animations on the child
AnimationPlayer; key animation properties (loop, reverse, etc.) on theSpineAnimationTrack. - Multiple
SpineAnimationTracknodes on oneSpineSpriteenable multi-track layering. - Each track gets a unique track index — higher tracks override lower tracks' skeleton properties.
Mix times from
SpineSkeletonDataResourcecannot be previewed in the editor.
See example: example/08-animation-player.
2D Lighting
spine-godot integrates with Godot's 2D lighting system.
Setup
- Provide normal map
.pngimages alongside atlas page images with prefixn_(default): e.g.,raptor.png→n_raptor.png. - The normal map prefix is configurable in the atlas import settings.
- After importing, apply Godot 2D lights to
SpineSpritenodes.
See example: example/10-2d-lighting.
Spine Runtimes API Access
Almost all spine-cpp API is mapped 1:1 to GDScript. Objects from SpineSprite (via get_skeleton(), get_animation_state()) mirror the C++ API.
GDScript Limitations
- Returned arrays/maps are copies — mutations don't affect internals.
- Cannot set per-track-entry listeners — use
SpineSpritesignals instead. - Cannot create/add/remove bones, slots, or other Spine objects directly.
- C++ class hierarchies of attachments and timelines are not exposed.