From a09e765c334a0f1cc2225b85ec0d0d760b7efd58 Mon Sep 17 00:00:00 2001 From: badlogic Date: Sun, 24 Apr 2022 20:05:22 +0200 Subject: [PATCH] [godot] Added SpineAnimationTrack to sequence Spine animations with AnimationPlayer, see examples/08-annimation-player --- .../example/assets/raptor/raprot-data.tres | 3 +- .../08-animation-player/animation-player.gd | 6 + .../08-animation-player/animation-player.tscn | 1464 +++++++++++++++++ spine-godot/example/project.godot | 2 +- .../spine_godot/SpineAnimationTrack.cpp | 229 +++ spine-godot/spine_godot/SpineAnimationTrack.h | 71 + spine-godot/spine_godot/SpineEditorPlugin.cpp | 18 + spine-godot/spine_godot/SpineEditorPlugin.h | 14 +- spine-godot/spine_godot/SpineSkeleton.h | 1 + spine-godot/spine_godot/SpineSlotNode.cpp | 2 +- spine-godot/spine_godot/SpineSprite.cpp | 5 +- spine-godot/spine_godot/register_types.cpp | 3 + 12 files changed, 1811 insertions(+), 7 deletions(-) create mode 100644 spine-godot/example/examples/08-animation-player/animation-player.gd create mode 100644 spine-godot/example/examples/08-animation-player/animation-player.tscn create mode 100644 spine-godot/spine_godot/SpineAnimationTrack.cpp create mode 100644 spine-godot/spine_godot/SpineAnimationTrack.h diff --git a/spine-godot/example/assets/raptor/raprot-data.tres b/spine-godot/example/assets/raptor/raprot-data.tres index c36f08e3c..d4e99ca58 100644 --- a/spine-godot/example/assets/raptor/raprot-data.tres +++ b/spine-godot/example/assets/raptor/raprot-data.tres @@ -6,5 +6,4 @@ [resource] atlas_res = ExtResource( 1 ) skeleton_file_res = ExtResource( 2 ) -animations = null -skins = null +default_mix = 0.2 diff --git a/spine-godot/example/examples/08-animation-player/animation-player.gd b/spine-godot/example/examples/08-animation-player/animation-player.gd new file mode 100644 index 000000000..eb6a971ba --- /dev/null +++ b/spine-godot/example/examples/08-animation-player/animation-player.gd @@ -0,0 +1,6 @@ +extends Node2D + +onready var player = $AnimationPlayer + +func _ready(): + player.play("walk-run-die") diff --git a/spine-godot/example/examples/08-animation-player/animation-player.tscn b/spine-godot/example/examples/08-animation-player/animation-player.tscn new file mode 100644 index 000000000..ee8cf067d --- /dev/null +++ b/spine-godot/example/examples/08-animation-player/animation-player.tscn @@ -0,0 +1,1464 @@ +[gd_scene load_steps=40 format=2] + +[ext_resource path="res://assets/spineboy/spinebody-data-res.tres" type="SpineSkeletonDataResource" id=1] +[ext_resource path="res://examples/08-animation-player/animation-player.gd" type="Script" id=2] +[ext_resource path="res://assets/raptor/raprot-data.tres" type="SpineSkeletonDataResource" id=3] + +[sub_resource type="Animation" id=26] +resource_name = "RESET" +length = 0.5 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "" ] +} + +[sub_resource type="Animation" id=27] +resource_name = "aim" +length = 0.5 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "aim" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.5 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.5 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=28] +resource_name = "aim_looped" +length = 0.5 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "aim" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.5 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.5 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=29] +resource_name = "death" +length = 4.9333 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "death" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 4.9333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 4.9333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=30] +resource_name = "death_looped" +length = 4.9333 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "death" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 4.9333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 4.9333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=31] +resource_name = "hoverboard" +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "hoverboard" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.0 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=32] +resource_name = "hoverboard_looped" +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "hoverboard" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.0 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=33] +resource_name = "idle" +length = 1.6667 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "idle" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1.6667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.6667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=34] +resource_name = "idle-turn" +length = 0.2667 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "idle-turn" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.2667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.2667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=35] +resource_name = "idle-turn_looped" +length = 0.2667 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "idle-turn" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.2667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.2667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=36] +resource_name = "idle_looped" +length = 1.6667 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "idle" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1.6667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.6667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=37] +resource_name = "jump" +length = 1.3333 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "jump" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1.3333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.3333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=38] +resource_name = "jump_looped" +length = 1.3333 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "jump" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1.3333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.3333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=39] +resource_name = "portal" +length = 3.1667 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "portal" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 3.1667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 3.1667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=40] +resource_name = "portal_looped" +length = 3.1667 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "portal" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 3.1667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 3.1667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=41] +resource_name = "run" +length = 0.6667 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "run" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.6667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.6667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=42] +resource_name = "run-to-idle" +length = 0.2667 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "run-to-idle" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.2667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.2667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=43] +resource_name = "run-to-idle_looped" +length = 0.2667 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "run-to-idle" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.2667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.2667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=44] +resource_name = "run_looped" +length = 0.6667 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "run" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.6667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.6667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=45] +resource_name = "shoot" +length = 0.6333 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "shoot" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.6333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.6333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=46] +resource_name = "shoot_looped" +length = 0.6333 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "shoot" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.6333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.6333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=47] +resource_name = "walk" +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "walk" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.0 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=48] +resource_name = "walk_looped" +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "walk" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.0 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=49] +resource_name = "RESET" +length = 0.5 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "" ] +} + +[sub_resource type="Animation" id=50] +resource_name = "gun-grab" +length = 0.666667 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "gun-grab" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.666667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.666667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=51] +resource_name = "gun-grab_looped" +length = 0.666667 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "gun-grab" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.666667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.666667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=52] +resource_name = "gun-holster" +length = 0.666667 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "gun-holster" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.666667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.666667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=53] +resource_name = "gun-holster_looped" +length = 0.666667 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "gun-holster" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 0.666667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 0.666667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=54] +resource_name = "jump" +length = 1.53333 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "jump" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1.53333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.53333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=55] +resource_name = "jump_looped" +length = 1.53333 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "jump" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1.53333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.53333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=56] +resource_name = "roar" +length = 2.13333 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "roar" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 2.13333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 2.13333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=57] +resource_name = "roar_looped" +length = 2.13333 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "roar" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 2.13333 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 2.13333 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=58] +resource_name = "walk" +length = 1.26667 +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "walk" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1.26667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.26667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ false ] +} + +[sub_resource type="Animation" id=59] +resource_name = "walk_looped" +length = 1.26667 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath(".:animation_name") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ "walk" ] +} +tracks/1/type = "value" +tracks/1/path = NodePath(".:animation_time") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0, 1.26667 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ 0, 1.26667 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath(".:loop") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ true ] +} + +[sub_resource type="Animation" id=25] +length = 10.0 +loop = true +tracks/0/type = "value" +tracks/0/path = NodePath("Raptor:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ Vector2( 1284, 540 ) ] +} + +[sub_resource type="Animation" id=1] +resource_name = "walk-run-die" +length = 10.5 +step = 0.02 +tracks/0/type = "value" +tracks/0/path = NodePath("Spineboy:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0, 4, 9.94 ), +"transitions": PoolRealArray( 1, 1, 1 ), +"update": 0, +"values": [ Vector2( 72.0001, 520 ), Vector2( 500, 520 ), Vector2( 500, 520 ) ] +} +tracks/1/type = "animation" +tracks/1/path = NodePath("Spineboy/SpineAnimationTrack/Track 0") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"clips": PoolStringArray( "walk_looped", "idle", "death" ), +"times": PoolRealArray( 0, 4, 4.34 ) +} +tracks/2/type = "value" +tracks/2/path = NodePath("Raptor:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0, 2.02, 3.7, 10 ), +"transitions": PoolRealArray( 1, 1, 1, 1 ), +"update": 0, +"values": [ Vector2( 1284, 540 ), Vector2( 1284, 540 ), Vector2( 800, 540 ), Vector2( 800, 540 ) ] +} +tracks/3/type = "animation" +tracks/3/path = NodePath("Raptor/SpineAnimationTrack/Raptor Track 0") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/keys = { +"clips": PoolStringArray( "walk_looped", "roar_looped" ), +"times": PoolRealArray( 0.46, 3.7 ) +} + +[node name="Node2D" type="Node2D"] +script = ExtResource( 2 ) + +[node name="Spineboy" type="SpineSprite" parent="."] +position = Vector2( 161.88, 520 ) +scale = Vector2( 0.323942, 0.323942 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineAnimationTrack" type="SpineAnimationTrack" parent="Spineboy"] +track_index = 0 +loop = true +animation_time = 0.84 + +[node name="Track 0" type="AnimationPlayer" parent="Spineboy/SpineAnimationTrack"] +"anims/-- Empty --" = SubResource( 26 ) +anims/RESET = SubResource( 26 ) +anims/aim = SubResource( 27 ) +anims/aim_looped = SubResource( 28 ) +anims/death = SubResource( 29 ) +anims/death_looped = SubResource( 30 ) +anims/hoverboard = SubResource( 31 ) +anims/hoverboard_looped = SubResource( 32 ) +anims/idle = SubResource( 33 ) +anims/idle-turn = SubResource( 34 ) +anims/idle-turn_looped = SubResource( 35 ) +anims/idle_looped = SubResource( 36 ) +anims/jump = SubResource( 37 ) +anims/jump_looped = SubResource( 38 ) +anims/portal = SubResource( 39 ) +anims/portal_looped = SubResource( 40 ) +anims/run = SubResource( 41 ) +anims/run-to-idle = SubResource( 42 ) +anims/run-to-idle_looped = SubResource( 43 ) +anims/run_looped = SubResource( 44 ) +anims/shoot = SubResource( 45 ) +anims/shoot_looped = SubResource( 46 ) +anims/walk = SubResource( 47 ) +anims/walk_looped = SubResource( 48 ) + +[node name="Raptor" type="SpineSprite" parent="."] +position = Vector2( 1284, 540 ) +scale = Vector2( -0.328761, 0.328761 ) +skeleton_data_res = ExtResource( 3 ) + +[node name="SpineAnimationTrack" type="SpineAnimationTrack" parent="Raptor"] +track_index = 0 +loop = true +animation_time = 0.38 + +[node name="Raptor Track 0" type="AnimationPlayer" parent="Raptor/SpineAnimationTrack"] +"anims/-- Empty --" = SubResource( 49 ) +anims/RESET = SubResource( 49 ) +anims/gun-grab = SubResource( 50 ) +anims/gun-grab_looped = SubResource( 51 ) +anims/gun-holster = SubResource( 52 ) +anims/gun-holster_looped = SubResource( 53 ) +anims/jump = SubResource( 54 ) +anims/jump_looped = SubResource( 55 ) +anims/roar = SubResource( 56 ) +anims/roar_looped = SubResource( 57 ) +anims/walk = SubResource( 58 ) +anims/walk_looped = SubResource( 59 ) + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +reset_on_save = false +anims/RESET = SubResource( 25 ) +anims/walk-run-die = SubResource( 1 ) diff --git a/spine-godot/example/project.godot b/spine-godot/example/project.godot index d72ceb1de..53b5ec3b8 100644 --- a/spine-godot/example/project.godot +++ b/spine-godot/example/project.godot @@ -11,7 +11,7 @@ config_version=4 [application] config/name="spine-godot-examples" -run/main_scene="res://examples/07-slot-node/slot-node.tscn" +run/main_scene="res://examples/08-animation-player/animation-player.tscn" run/low_processor_mode=true config/icon="res://icon.png" diff --git a/spine-godot/spine_godot/SpineAnimationTrack.cpp b/spine-godot/spine_godot/SpineAnimationTrack.cpp new file mode 100644 index 000000000..fcab9c760 --- /dev/null +++ b/spine-godot/spine_godot/SpineAnimationTrack.cpp @@ -0,0 +1,229 @@ +#include "SpineAnimationTrack.h" +#include "core/engine.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/animation.h" + +#ifdef TOOLS_ENABLED +#include "editor/plugins/animation_player_editor_plugin.h" +#endif + +void SpineAnimationTrack::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_track_index", "track_index"), &SpineAnimationTrack::set_track_index); + ClassDB::bind_method(D_METHOD("get_track_index"), &SpineAnimationTrack::get_track_index); + ClassDB::bind_method(D_METHOD("set_animation_name", "animation_name"), &SpineAnimationTrack::set_animation_name); + ClassDB::bind_method(D_METHOD("get_animation_name"), &SpineAnimationTrack::get_animation_name); + ClassDB::bind_method(D_METHOD("set_loop", "loop"), &SpineAnimationTrack::set_loop); + ClassDB::bind_method(D_METHOD("get_loop"), &SpineAnimationTrack::get_loop); + ClassDB::bind_method(D_METHOD("set_animation_time", "time"), &SpineAnimationTrack::set_animation_time); + ClassDB::bind_method(D_METHOD("get_animation_time"), &SpineAnimationTrack::get_animation_time); + ClassDB::bind_method(D_METHOD("update_animation_state", "spine_sprite"), &SpineAnimationTrack::update_animation_state); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "track_index", PROPERTY_HINT_RANGE, "0,256,0"), "set_track_index", "get_track_index"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation_name"), "set_animation_name", "get_animation_name"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "get_loop"); +#if VERSION_MAJOR > 3 + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "animation_time"), "set_animation_time", "get_animation_time"); +#else + ADD_PROPERTY(PropertyInfo(Variant::REAL, "animation_time"), "set_animation_time", "get_animation_time"); +#endif +} + +SpineAnimationTrack::SpineAnimationTrack(): track_index(-1), loop(false), animation_time(0), sprite(nullptr) { +} + +void SpineAnimationTrack::set_track_index(int _track_index) { + track_index = _track_index; +} + +int SpineAnimationTrack::get_track_index() { + return track_index; +} + +void SpineAnimationTrack::_notification(int what) { + switch(what) { + case NOTIFICATION_PARENTED: { + sprite = Object::cast_to(get_parent()); + if (sprite) { + sprite->connect("before_animation_state_update", this, "update_animation_state"); + } else { + WARN_PRINT("SpineAnimationTrack parent is not a SpineSprite."); + } + NOTIFY_PROPERTY_LIST_CHANGED(); + break; + } + case NOTIFICATION_READY: { + setup_animation_player(); + break; + } + case NOTIFICATION_UNPARENTED: { + if (sprite) { + sprite->disconnect("before_animation_state_update", this, "update_animation_state"); + } + break; + } + default: + break; + } +} + +void SpineAnimationTrack::setup_animation_player() { + if (!sprite) return; + if (!sprite->get_skeleton_data_res().is_valid() || !sprite->get_skeleton_data_res()->is_skeleton_data_loaded()) return; + + // If we don't have a track index yet, find the highest track number used + // by existing tracks. + if (track_index < 0) { + int highest_track_number = -1; + for (int i = 0; i < sprite->get_child_count(); i++) { + auto other_track = Object::cast_to(sprite->get_child(i)); + if (other_track) { + if (other_track->track_index > highest_track_number) + highest_track_number = other_track->track_index; + } + } + track_index = highest_track_number + 1; + } + + // Find the animation player under the track and reset its animation. Create a new one + // if there isn't one already. + AnimationPlayer *animation_player = nullptr; + for (int i = 0; i < get_child_count(); i++) { + animation_player = Object::cast_to(get_child(i)); + if (animation_player) { + break; + } + } + + if (!animation_player) { + animation_player = memnew(AnimationPlayer); + animation_player->set_name(String("Track ") + String::num_int64(track_index)); + add_child(animation_player); + animation_player->set_owner(sprite->get_owner()); + } else { + List animation_names; + animation_player->get_animation_list(&animation_names); + for (int i = 0; i < animation_name.size(); i++) { + animation_player->remove_animation(animation_names[i]); + } + } + + auto skeleton_data = sprite->get_skeleton_data_res()->get_skeleton_data(); + auto &animations = skeleton_data->getAnimations(); + for (int i = 0; i < animations.size(); i++) { + auto &animation = animations[i]; + Ref animation_ref = create_animation(animation, false); + animation_player->add_animation(animation_ref->get_name(), animation_ref); + Ref animation_looped_ref = create_animation(animation, true); + animation_player->add_animation(animation_looped_ref->get_name(), animation_looped_ref); + } + Ref reset_animation_ref; + INSTANTIATE(reset_animation_ref); + reset_animation_ref->set_name("RESET"); + reset_animation_ref->set_loop(true); + reset_animation_ref->set_length(0.5f); + reset_animation_ref->add_track(Animation::TYPE_VALUE); + reset_animation_ref->track_set_path(0, NodePath(".:animation_name")); + reset_animation_ref->track_insert_key(0, 0, ""); + animation_player->add_animation(reset_animation_ref->get_name(), reset_animation_ref); + animation_player->add_animation("-- Empty --", reset_animation_ref); + + this->animation_player = animation_player; +} + +Ref SpineAnimationTrack::create_animation(spine::Animation *animation, bool loop) { + float duration = animation->getDuration(); + if (duration == 0) duration = 0.5; + + Ref animation_ref; + INSTANTIATE(animation_ref); + animation_ref->set_name(String(animation->getName().buffer()) + (loop ? "" : "_looped")); + animation_ref->set_loop(!loop); + animation_ref->set_length(duration); + + animation_ref->add_track(Animation::TYPE_VALUE); + animation_ref->track_set_path(0, NodePath(".:animation_name")); + animation_ref->track_insert_key(0, 0, animation->getName().buffer()); + + animation_ref->add_track(Animation::TYPE_VALUE); + animation_ref->track_set_path(1, NodePath(".:animation_time")); + animation_ref->track_insert_key(1, 0, 0); + animation_ref->track_insert_key(1, duration, duration); + + animation_ref->add_track(Animation::TYPE_VALUE); + animation_ref->track_set_path(2, NodePath(".:loop")); + animation_ref->track_insert_key(2, 0, !loop); + + return animation_ref; +} + +void SpineAnimationTrack::update_animation_state(const Variant &variant_sprite) { + sprite = Object::cast_to(variant_sprite); + if (!sprite) return; + if (!sprite->get_skeleton_data_res().is_valid() || !sprite->get_skeleton_data_res()->is_skeleton_data_loaded()) return; + if (track_index < 0) return; + + spine::AnimationState *animation_state = sprite->get_animation_state()->get_spine_object(); + if (!animation_state) return; + spine::Skeleton *skeleton = sprite->get_skeleton()->get_spine_object(); + if (!skeleton) return; + + if (Engine::get_singleton()->is_editor_hint()) { +#ifdef TOOLS_ENABLED + // When the animation dock is no longer visible, reset the skeleton. + if (!AnimationPlayerEditor::singleton->is_visible_in_tree()) { + skeleton->setToSetupPose(); + animation_state->clearTracks(); + animation_state->setTimeScale(1); + return; + } +#endif + + if (track_index == 0) skeleton->setToSetupPose(); + animation_state->setTimeScale(0); + animation_state->clearTrack(track_index); + if (!EMPTY(animation_name)) { + auto entry = animation_state->setAnimation(track_index, SPINE_STRING(animation_name), loop); + entry->setMixDuration(0); + entry->setTrackTime(animation_time); + } + } else { + auto current_entry = animation_state->getCurrent(track_index); + if (current_entry) { + if (animation_name != current_entry->getAnimation()->getName().buffer() || current_entry->getLoop() != loop) { + if (!EMPTY(animation_name)) + animation_state->setAnimation(track_index, SPINE_STRING(animation_name), loop); + else + animation_state->setEmptyAnimation(track_index, 0); + } + } else { + if (!EMPTY(animation_name)) + animation_state->setAnimation(track_index, SPINE_STRING(animation_name), loop); + else + animation_state->setEmptyAnimation(track_index, 0); + } + } +} + +void SpineAnimationTrack::set_animation_name(const String& _animation_name) { + animation_name = _animation_name; +} + +String SpineAnimationTrack::get_animation_name() { + return animation_name; +} + +void SpineAnimationTrack::set_loop(bool _loop) { + loop = _loop; +} + +bool SpineAnimationTrack::get_loop() { + return loop; +} + +void SpineAnimationTrack::set_animation_time(float _animation_time) { + animation_time = _animation_time; +} + +float SpineAnimationTrack::get_animation_time() { + return animation_time; +} diff --git a/spine-godot/spine_godot/SpineAnimationTrack.h b/spine-godot/spine_godot/SpineAnimationTrack.h new file mode 100644 index 000000000..45a233ee1 --- /dev/null +++ b/spine-godot/spine_godot/SpineAnimationTrack.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated January 1, 2020. Replaces all prior versions. + * + * Copyright (c) 2013-2020, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef GODOT_SPINEANIMATIONTRACK_H +#define GODOT_SPINEANIMATIONTRACK_H + +#include "SpineCommon.h" +#include "SpineSprite.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/animation.h" + +class SpineAnimationTrack : public Node { + GDCLASS(SpineAnimationTrack, Node) +protected: + int track_index; + String animation_name; + String last_animation_name; + bool loop; + float animation_time; + SpineSprite *sprite; + AnimationPlayer *animation_player; + + static void _bind_methods(); + void _notification(int what); + void setup_animation_player(); + Ref create_animation(spine::Animation *animation, bool loop); + void _on_before_world_transforms_change(const Variant& _sprite); + void update_animation_state(const Variant &variant_sprite); +public: + SpineAnimationTrack(); + + void set_track_index(int _track_index); + int get_track_index(); + + void set_animation_name(const String& _animation_name); + String get_animation_name(); + + void set_loop(bool _loop); + bool get_loop(); + + void set_animation_time (float _animation_time); + float get_animation_time(); +}; + +#endif diff --git a/spine-godot/spine_godot/SpineEditorPlugin.cpp b/spine-godot/spine_godot/SpineEditorPlugin.cpp index e031454e0..a111439a6 100644 --- a/spine-godot/spine_godot/SpineEditorPlugin.cpp +++ b/spine-godot/spine_godot/SpineEditorPlugin.cpp @@ -80,6 +80,7 @@ SpineEditorPlugin::SpineEditorPlugin(EditorNode *node) { add_import_plugin(memnew(SpineJsonResourceImportPlugin)); add_import_plugin(memnew(SpineBinaryResourceImportPlugin)); add_inspector_plugin(memnew(SpineSkeletonDataResourceInspectorPlugin)); + add_inspector_plugin(memnew(SpineSpriteInspectorPlugin)); } bool SpineSkeletonDataResourceInspectorPlugin::can_handle(Object *object) { @@ -314,4 +315,21 @@ void SpineEditorPropertyAnimationMix::update_property() { updating = false; } +void SpineSpriteInspectorPlugin::_bind_methods() { + ClassDB::bind_method(D_METHOD("button_clicked"), &SpineSpriteInspectorPlugin::button_clicked); +} + +void SpineSpriteInspectorPlugin::button_clicked(const String& button_name) { +} + +bool SpineSpriteInspectorPlugin::can_handle(Object* object) { + return Object::cast_to(object) != nullptr; +} + +void SpineSpriteInspectorPlugin::parse_begin(Object* object) { + sprite = Object::cast_to(object); + if (!sprite) return; + if (!sprite->get_skeleton_data_res().is_valid() || !sprite->get_skeleton_data_res()->is_skeleton_data_loaded()) return; +} + #endif diff --git a/spine-godot/spine_godot/SpineEditorPlugin.h b/spine-godot/spine_godot/SpineEditorPlugin.h index fea75247e..d525058c4 100644 --- a/spine-godot/spine_godot/SpineEditorPlugin.h +++ b/spine-godot/spine_godot/SpineEditorPlugin.h @@ -147,8 +147,6 @@ class SpineSkeletonDataResourceInspectorPlugin: public EditorInspectorPlugin { GDCLASS(SpineSkeletonDataResourceInspectorPlugin, EditorInspectorPlugin) public: - SpineSkeletonDataResourceInspectorPlugin() = default; - bool can_handle(Object *object) override; #if VERSION_MAJOR > 3 bool parse_property(Object *object, Variant::Type type, const String &path, PropertyHint hint, const String &hint_text, uint32_t usage, bool wide) override; @@ -195,6 +193,18 @@ public: void update_property() override; }; +class SpineSpriteInspectorPlugin: public EditorInspectorPlugin { + GDCLASS(SpineSpriteInspectorPlugin, EditorInspectorPlugin) + + SpineSprite *sprite; + + static void _bind_methods(); + void button_clicked(const String &button_name); +public: + bool can_handle(Object *object) override; + void parse_begin(Object *object) override; +}; + #endif #endif//GODOT_SPINEEDITORPLUGIN_H diff --git a/spine-godot/spine_godot/SpineSkeleton.h b/spine-godot/spine_godot/SpineSkeleton.h index 09d3050b6..96cb2040e 100644 --- a/spine-godot/spine_godot/SpineSkeleton.h +++ b/spine-godot/spine_godot/SpineSkeleton.h @@ -50,6 +50,7 @@ class SpineSkeleton : public REFCOUNTED { friend class SpineAnimation; friend class SpineAnimationState; friend class SpineCollisionShapeProxy; + friend class SpineAnimationTrack; protected: static void _bind_methods(); diff --git a/spine-godot/spine_godot/SpineSlotNode.cpp b/spine-godot/spine_godot/SpineSlotNode.cpp index bdd41f39c..5a1d6cf14 100644 --- a/spine-godot/spine_godot/SpineSlotNode.cpp +++ b/spine-godot/spine_godot/SpineSlotNode.cpp @@ -27,7 +27,7 @@ void SpineSlotNode::_notification(int what) { _change_notify("rotation_deg"); _change_notify("scale"); } else { - WARN_PRINT("SpineSlotProxy parent is not a SpineSprite."); + WARN_PRINT("SpineSlotNode parent is not a SpineSprite."); } NOTIFY_PROPERTY_LIST_CHANGED(); break; diff --git a/spine-godot/spine_godot/SpineSprite.cpp b/spine-godot/spine_godot/SpineSprite.cpp index 0b8ef8de3..c0a982eac 100644 --- a/spine-godot/spine_godot/SpineSprite.cpp +++ b/spine-godot/spine_godot/SpineSprite.cpp @@ -58,6 +58,8 @@ void SpineSprite::_bind_methods() { ADD_SIGNAL(MethodInfo("animation_completed", PropertyInfo(Variant::OBJECT, "spine_sprite", PROPERTY_HINT_TYPE_STRING, "SpineSprite"), PropertyInfo(Variant::OBJECT, "animation_state", PROPERTY_HINT_TYPE_STRING, "SpineAnimationState"), PropertyInfo(Variant::OBJECT, "track_entry", PROPERTY_HINT_TYPE_STRING, "SpineTrackEntry"))); ADD_SIGNAL(MethodInfo("animation_disposed", PropertyInfo(Variant::OBJECT, "spine_sprite", PROPERTY_HINT_TYPE_STRING, "SpineSprite"), PropertyInfo(Variant::OBJECT, "animation_state", PROPERTY_HINT_TYPE_STRING, "SpineAnimationState"), PropertyInfo(Variant::OBJECT, "track_entry", PROPERTY_HINT_TYPE_STRING, "SpineTrackEntry"))); ADD_SIGNAL(MethodInfo("animation_event", PropertyInfo(Variant::OBJECT, "spine_sprite", PROPERTY_HINT_TYPE_STRING, "SpineSprite"), PropertyInfo(Variant::OBJECT, "animation_state", PROPERTY_HINT_TYPE_STRING, "SpineAnimationState"), PropertyInfo(Variant::OBJECT, "track_entry", PROPERTY_HINT_TYPE_STRING, "SpineTrackEntry"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_TYPE_STRING, "SpineEvent"))); + ADD_SIGNAL(MethodInfo("before_animation_state_update", PropertyInfo(Variant::OBJECT, "spine_sprite", PROPERTY_HINT_TYPE_STRING, "SpineSprite"))); + ADD_SIGNAL(MethodInfo("before_animation_state_apply", PropertyInfo(Variant::OBJECT, "spine_sprite", PROPERTY_HINT_TYPE_STRING, "SpineSprite"))); ADD_SIGNAL(MethodInfo("before_world_transforms_change", PropertyInfo(Variant::OBJECT, "spine_sprite", PROPERTY_HINT_TYPE_STRING, "SpineSprite"))); ADD_SIGNAL(MethodInfo("world_transforms_changed", PropertyInfo(Variant::OBJECT, "spine_sprite", PROPERTY_HINT_TYPE_STRING, "SpineSprite"))); @@ -158,7 +160,6 @@ void SpineSprite::generate_meshes_for_slots(Ref skeleton_ref) { mesh_instance->set_material(default_materials[spine::BlendMode_Normal]); add_child(mesh_instance); - mesh_instance->set_owner(this); mesh_instances.push_back(mesh_instance); slot_nodes.add(spine::Vector()); } @@ -233,10 +234,12 @@ void SpineSprite::update_skeleton(float delta) { if (!(skeleton.is_valid() && animation_state.is_valid()) || EMPTY(mesh_instances)) return; + emit_signal("before_animation_state_update", this); animation_state->update(delta); if (!is_visible_in_tree()) return; + emit_signal("before_animation_state_apply", this); animation_state->apply(skeleton); emit_signal("before_world_transforms_change", this); skeleton->update_world_transform(); diff --git a/spine-godot/spine_godot/register_types.cpp b/spine-godot/spine_godot/register_types.cpp index 3b3ce37ed..b8920a19e 100644 --- a/spine-godot/spine_godot/register_types.cpp +++ b/spine-godot/spine_godot/register_types.cpp @@ -35,6 +35,7 @@ #include "SpineSprite.h" #include "SpineSkeleton.h" #include "SpineAnimationState.h" +#include "SpineAnimationTrack.h" #include "SpineEventData.h" #include "SpineEvent.h" #include "SpineTrackEntry.h" @@ -98,8 +99,10 @@ void register_spine_godot_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); #if VERSION_MAJOR > 3 atlas_loader.instantiate();