mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-15 19:41:36 +08:00
230 lines
8.3 KiB
C++
230 lines
8.3 KiB
C++
#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<SpineSprite>(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<SpineAnimationTrack>(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<AnimationPlayer>(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<StringName> 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> animation_ref = create_animation(animation, false);
|
|
animation_player->add_animation(animation_ref->get_name(), animation_ref);
|
|
Ref<Animation> animation_looped_ref = create_animation(animation, true);
|
|
animation_player->add_animation(animation_looped_ref->get_name(), animation_looped_ref);
|
|
}
|
|
Ref<Animation> 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<Animation> SpineAnimationTrack::create_animation(spine::Animation *animation, bool loop) {
|
|
float duration = animation->getDuration();
|
|
if (duration == 0) duration = 0.5;
|
|
|
|
Ref<Animation> 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<SpineSprite>(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;
|
|
}
|