[godot] Replaced bind nodes in SpineSprite with much more powerful SpineSlotNode. Added example scene.

This commit is contained in:
badlogic 2022-04-22 20:09:26 +02:00
parent b24f7e53c6
commit a2b2189faa
6 changed files with 68 additions and 105 deletions

View File

@ -1,6 +1,11 @@
extends Node2D
onready var spineboy: SpineSprite = $Spineboy
onready var raptor: SpineSprite = $Spineboy/SlotNodeGun/Raptor
onready var tiny_spineboy: SpineSprite = $Spineboy/SlotNodeFrontFist/TinySpineboy
func _ready():
spineboy.get_animation_state().set_animation("walk", true, 0)
func _ready():
var entry = spineboy.get_animation_state().set_animation("run", true, 0)
entry.set_time_scale(0.1)
raptor.get_animation_state().set_animation("walk", true, 0)
tiny_spineboy.get_animation_state().set_animation("walk", true, 0)

View File

@ -2,7 +2,7 @@
[ext_resource path="res://examples/07-slot-node/slot-node.gd" type="Script" id=1]
[ext_resource path="res://assets/spineboy/spinebody-data-res.tres" type="SpineSkeletonDataResource" id=2]
[ext_resource path="res://icon.png" type="Texture" id=3]
[ext_resource path="res://assets/raptor/raprot-data.tres" type="SpineSkeletonDataResource" id=3]
[node name="Node2D" type="Node2D"]
script = ExtResource( 1 )
@ -12,11 +12,24 @@ position = Vector2( 506, 480 )
scale = Vector2( 0.560712, 0.560712 )
skeleton_data_res = ExtResource( 2 )
[node name="SpineSlotNode" type="SpineSlotNode" parent="Spineboy"]
[node name="SlotNodeGun" type="SpineSlotNode" parent="Spineboy"]
position = Vector2( 40.8752, -276.036 )
rotation = 0.837234
scale = Vector2( 1, 1 )
slot_name = "gun"
[node name="Sprite" type="Sprite" parent="Spineboy/SpineSlotNode"]
texture = ExtResource( 3 )
[node name="Raptor" type="SpineSprite" parent="Spineboy/SlotNodeGun"]
position = Vector2( 84.6909, -67.9174 )
scale = Vector2( 0.193472, 0.193472 )
skeleton_data_res = ExtResource( 3 )
[node name="SlotNodeFrontFist" type="SpineSlotNode" parent="Spineboy"]
position = Vector2( -29.0298, -241.577 )
rotation = 0.995187
scale = Vector2( 1, 1 )
slot_name = "front-fist"
[node name="TinySpineboy" type="SpineSprite" parent="Spineboy/SlotNodeFrontFist"]
position = Vector2( -2.64624, -10.8111 )
scale = Vector2( 0.193389, 0.193389 )
skeleton_data_res = ExtResource( 2 )

View File

@ -9,7 +9,7 @@ void SpineSlotNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_on_world_transforms_changed", "spine_sprite"), &SpineSlotNode::on_world_transforms_changed);
}
SpineSlotNode::SpineSlotNode(): sprite(nullptr) {
SpineSlotNode::SpineSlotNode(): slot_index(-1), sprite(nullptr) {
}
void SpineSlotNode::_notification(int what) {
@ -87,7 +87,12 @@ void SpineSlotNode::on_world_transforms_changed(const Variant& _sprite) {
void SpineSlotNode::update_transform(SpineSprite *sprite) {
if (!sprite) return;
auto slot = sprite->get_skeleton()->find_slot(slot_name);
if (!slot.is_valid()) return;
if (!slot.is_valid()) {
slot_index = -1;
return;
} else {
slot_index = slot->get_data()->get_index();
}
auto bone = slot->get_bone();
if (!bone.is_valid()) return;
this->set_global_transform(bone->get_global_transform());

View File

@ -10,6 +10,7 @@ class SpineSlotNode: public Node2D {
protected:
String slot_name;
int slot_index;
SpineSprite *sprite;
static void _bind_methods();
@ -25,6 +26,8 @@ public:
void set_slot_name(const String &_slot_name);
String get_slot_name();
int get_slot_index() { return slot_index; }
};
#endif

View File

@ -32,6 +32,7 @@
#include "SpineTrackEntry.h"
#include "SpineSkeleton.h"
#include "SpineRendererObject.h"
#include "SpineSlotNode.h"
Ref<CanvasItemMaterial> SpineSprite::default_materials[4] = {};
static int sprite_count = 0;
@ -42,8 +43,6 @@ void SpineSprite::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_skeleton"), &SpineSprite::get_skeleton);
ClassDB::bind_method(D_METHOD("get_animation_state"), &SpineSprite::get_animation_state);
ClassDB::bind_method(D_METHOD("on_skeleton_data_changed"), &SpineSprite::on_skeleton_data_changed);
ClassDB::bind_method(D_METHOD("get_bind_slot_nodes"), &SpineSprite::get_bind_slot_nodes);
ClassDB::bind_method(D_METHOD("set_bind_slot_nodes", "v"), &SpineSprite::set_bind_slot_nodes);
ClassDB::bind_method(D_METHOD("get_global_bone_transform", "bone_name"), &SpineSprite::get_global_bone_transform);
ClassDB::bind_method(D_METHOD("set_global_bone_transform", "bone_name", "global_transform"), &SpineSprite::set_global_bone_transform);
@ -162,6 +161,7 @@ void SpineSprite::generate_meshes_for_slots(Ref<SpineSkeleton> skeleton_ref) {
add_child(mesh_instance);
mesh_instance->set_owner(this);
mesh_instances.push_back(mesh_instance);
slot_nodes.add(spine::Vector<SpineSlotNode*>());
}
}
@ -171,6 +171,33 @@ void SpineSprite::remove_meshes() {
memdelete(mesh_instances[i]);
}
mesh_instances.clear();
slot_nodes.clear();
}
void SpineSprite::sort_slot_nodes() {
for (int i = 0; i < slot_nodes.size(); i++) {
slot_nodes[i].setSize(0, nullptr);
}
auto draw_order = skeleton->get_spine_object()->getDrawOrder();
for (int i = 0; i < get_child_count(); i++) {
auto slot_node = Object::cast_to<SpineSlotNode>(get_child(i));
if (!slot_node) continue;
if (slot_node->get_slot_index() == -1 || slot_node->get_slot_index() >= draw_order.size()) {
continue;
}
slot_nodes[slot_node->get_slot_index()].add(slot_node);
}
for (int i = 0; i < draw_order.size(); i++) {
int slot_index = draw_order[i]->getData().getIndex();
int mesh_index = mesh_instances[i]->get_index();
spine::Vector<SpineSlotNode*> &nodes = slot_nodes[slot_index];
for (int j = 0; j < nodes.size(); j++) {
auto node = nodes[j];
move_child(node, mesh_index + 1);
}
}
}
Ref<SpineSkeleton> SpineSprite::get_skeleton() {
@ -217,88 +244,7 @@ void SpineSprite::update_skeleton(float delta) {
emit_signal("world_transforms_changed", this);
update_meshes(skeleton);
update();
update_bind_slot_nodes();
}
void SpineSprite::update_bind_slot_nodes() {
if (animation_state.is_valid() && skeleton.is_valid()) {
for (int i = 0, n = bind_slot_nodes.size(); i < n; ++i) {
auto a = bind_slot_nodes[i];
if (a.get_type() == Variant::DICTIONARY) {
auto d = (Dictionary) a;
if (d.has("slot_name") && d.has("node_path")) {
NodePath node_path = d["node_path"];
Node *node = get_node_or_null(node_path);
if (node && node->is_class("Node2D")) {
auto *node2d = (Node2D *) node;
String slot_name = d["slot_name"];
auto slot = skeleton->find_slot(slot_name);
if (slot.is_valid()) {
auto bone = slot->get_bone();
if (bone.is_valid()) {
bone->apply_world_transform_2d(node2d);
update_bind_slot_node_draw_order(slot_name, node2d);
}
}
}
}
} else if (a.get_type() == Variant::ARRAY) {
auto as = (Array) a;// 0: slot_name, 1: node_path
if (as.size() >= 2 && as[0].get_type() == Variant::STRING && as[1].get_type() == Variant::NODE_PATH) {
NodePath node_path = as[1];
Node *node = get_node_or_null(node_path);
if (node && node->is_class("Node2D")) {
auto *node2d = (Node2D *) node;
String slot_name = as[0];
auto slot = skeleton->find_slot(slot_name);
if (slot.is_valid()) {
auto bone = slot->get_bone();
if (bone.is_valid()) {
bone->apply_world_transform_2d(node2d);
update_bind_slot_node_draw_order(slot_name, node2d);
}
}
}
}
}
}
}
}
void SpineSprite::update_bind_slot_node_draw_order(const String &slot_name, Node2D *node2d) {
#if VERSION_MAJOR > 3
auto nodes = find_nodes(slot_name);
if (!nodes.is_empty()) {
auto mesh_ins = Object::cast_to<MeshInstance2D>(nodes[0]);
if (mesh_ins) {
auto pos = mesh_ins->get_index();
// get child
auto node = find_child_node_by_node(node2d);
if (node && node->get_index() != pos + 1) {
move_child(node, pos + 1);
}
}
}
#else
auto mesh_ins = find_node(slot_name);
if (mesh_ins) {
auto pos = mesh_ins->get_index();
// get child
auto node = find_child_node_by_node(node2d);
if (node && node->get_index() != pos + 1) {
move_child(node, pos + 1);
}
}
#endif
}
Node *SpineSprite::find_child_node_by_node(Node *node) {
if (node == nullptr) return nullptr;
while (node && node->get_parent() != this) node = node->get_parent();
return node;
sort_slot_nodes();
}
#define TEMP_COPY(t, get_res) \
@ -534,14 +480,6 @@ void SpineSprite::callback(spine::AnimationState *state, spine::EventType type,
}
}
Array SpineSprite::get_bind_slot_nodes() {
return bind_slot_nodes;
}
void SpineSprite::set_bind_slot_nodes(Array v) {
bind_slot_nodes = v;
}
Transform2D SpineSprite::get_global_bone_transform(const String &bone_name) {
if (!animation_state.is_valid() && !skeleton.is_valid()) {
return get_global_transform();

View File

@ -36,6 +36,8 @@
#include "scene/2d/mesh_instance_2d.h"
#include "scene/resources/texture.h"
class SpineSlotNode;
class SpineSprite : public Node2D, public spine::AnimationStateListenerObject {
GDCLASS(SpineSprite, Node2D)
@ -50,9 +52,9 @@ protected:
Ref<SpineSkeletonDataResource> skeleton_data_res;
Ref<SpineSkeleton> skeleton;
Ref<SpineAnimationState> animation_state;
Array bind_slot_nodes;
UpdateMode update_mode;
spine::Vector<spine::Vector<SpineSlotNode*> > slot_nodes;
Vector<MeshInstance2D *> mesh_instances;
spine::SkeletonClipping *skeleton_clipper;
static Ref<CanvasItemMaterial> default_materials[4];
@ -62,12 +64,9 @@ protected:
void generate_meshes_for_slots(Ref<SpineSkeleton> skeleton_ref);
void remove_meshes();
void sort_slot_nodes();
void update_meshes(Ref<SpineSkeleton> skeleton);
void update_bind_slot_nodes();
void update_bind_slot_node_draw_order(const String &slot_name, Node2D *node2d);
Node *find_child_node_by_node(Node *node);
void callback(spine::AnimationState *state, spine::EventType type, spine::TrackEntry *entry, spine::Event *event);
public: