Add markers to animation
This commit is contained in:
@ -164,39 +164,41 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
|
||||
double delta = p_started ? 0 : p_delta * speed;
|
||||
double next_pos = cd.pos + delta;
|
||||
|
||||
double len = cd.from->animation->get_length();
|
||||
double start = get_section_start_time();
|
||||
double end = get_section_end_time();
|
||||
|
||||
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
|
||||
|
||||
switch (cd.from->animation->get_loop_mode()) {
|
||||
case Animation::LOOP_NONE: {
|
||||
if (Animation::is_less_approx(next_pos, 0)) {
|
||||
next_pos = 0;
|
||||
} else if (Animation::is_greater_approx(next_pos, len)) {
|
||||
next_pos = len;
|
||||
if (Animation::is_less_approx(next_pos, start)) {
|
||||
next_pos = start;
|
||||
} else if (Animation::is_greater_approx(next_pos, end)) {
|
||||
next_pos = end;
|
||||
}
|
||||
delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here).
|
||||
} break;
|
||||
|
||||
case Animation::LOOP_LINEAR: {
|
||||
if (Animation::is_less_approx(next_pos, 0) && Animation::is_greater_or_equal_approx(cd.pos, 0)) {
|
||||
if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {
|
||||
looped_flag = Animation::LOOPED_FLAG_START;
|
||||
}
|
||||
if (Animation::is_greater_approx(next_pos, len) && Animation::is_less_or_equal_approx(cd.pos, len)) {
|
||||
if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {
|
||||
looped_flag = Animation::LOOPED_FLAG_END;
|
||||
}
|
||||
next_pos = Math::fposmod(next_pos, (double)len);
|
||||
next_pos = Math::fposmod(next_pos - start, end - start) + start;
|
||||
} break;
|
||||
|
||||
case Animation::LOOP_PINGPONG: {
|
||||
if (Animation::is_less_approx(next_pos, 0) && Animation::is_greater_or_equal_approx(cd.pos, 0)) {
|
||||
if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {
|
||||
cd.speed_scale *= -1.0;
|
||||
looped_flag = Animation::LOOPED_FLAG_START;
|
||||
}
|
||||
if (Animation::is_greater_approx(next_pos, len) && Animation::is_less_or_equal_approx(cd.pos, len)) {
|
||||
if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {
|
||||
cd.speed_scale *= -1.0;
|
||||
looped_flag = Animation::LOOPED_FLAG_END;
|
||||
}
|
||||
next_pos = Math::pingpong(next_pos, (double)len);
|
||||
next_pos = Math::pingpong(next_pos - start, end - start) + start;
|
||||
} break;
|
||||
|
||||
default:
|
||||
@ -208,18 +210,18 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
|
||||
// End detection.
|
||||
if (p_is_current) {
|
||||
if (cd.from->animation->get_loop_mode() == Animation::LOOP_NONE) {
|
||||
if (!backwards && Animation::is_less_or_equal_approx(prev_pos, len) && Math::is_equal_approx(next_pos, len)) {
|
||||
if (!backwards && Animation::is_less_or_equal_approx(prev_pos, end) && Math::is_equal_approx(next_pos, end)) {
|
||||
// Playback finished.
|
||||
next_pos = len; // Snap to the edge.
|
||||
next_pos = end; // Snap to the edge.
|
||||
end_reached = true;
|
||||
end_notify = Animation::is_less_approx(prev_pos, len); // Notify only if not already at the end.
|
||||
end_notify = Animation::is_less_approx(prev_pos, end); // Notify only if not already at the end.
|
||||
p_blend = 1.0;
|
||||
}
|
||||
if (backwards && Animation::is_greater_or_equal_approx(prev_pos, 0) && Math::is_equal_approx(next_pos, 0)) {
|
||||
if (backwards && Animation::is_greater_or_equal_approx(prev_pos, start) && Math::is_equal_approx(next_pos, start)) {
|
||||
// Playback finished.
|
||||
next_pos = 0; // Snap to the edge.
|
||||
next_pos = start; // Snap to the edge.
|
||||
end_reached = true;
|
||||
end_notify = Animation::is_greater_approx(prev_pos, 0); // Notify only if not already at the beginning.
|
||||
end_notify = Animation::is_greater_approx(prev_pos, start); // Notify only if not already at the beginning.
|
||||
p_blend = 1.0;
|
||||
}
|
||||
}
|
||||
@ -231,10 +233,14 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
|
||||
if (p_started) {
|
||||
pi.time = prev_pos;
|
||||
pi.delta = 0;
|
||||
pi.start = start;
|
||||
pi.end = end;
|
||||
pi.seeked = true;
|
||||
} else {
|
||||
pi.time = next_pos;
|
||||
pi.delta = delta;
|
||||
pi.start = start;
|
||||
pi.end = end;
|
||||
pi.seeked = p_seeked;
|
||||
}
|
||||
if (Math::is_zero_approx(pi.delta) && backwards) {
|
||||
@ -378,6 +384,14 @@ void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_b
|
||||
play(p_name, p_custom_blend, -1, true);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play_section_with_markers_backwards(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend) {
|
||||
play_section_with_markers(p_name, p_start_marker, p_end_marker, p_custom_blend, -1, true);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play_section_backwards(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend) {
|
||||
play_section(p_name, p_start_time, p_end_time, -1, true);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
if (auto_capture) {
|
||||
play_with_capture(p_name, auto_capture_duration, p_custom_blend, p_custom_scale, p_from_end, auto_capture_transition_type, auto_capture_ease_type);
|
||||
@ -387,6 +401,10 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
|
||||
}
|
||||
|
||||
void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
play_section_with_markers(p_name, StringName(), StringName(), p_custom_blend, p_custom_scale, p_from_end);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play_section_with_markers(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
StringName name = p_name;
|
||||
|
||||
if (name == StringName()) {
|
||||
@ -395,6 +413,38 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
|
||||
|
||||
ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
|
||||
|
||||
Ref<Animation> animation = animation_set[name].animation;
|
||||
|
||||
ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));
|
||||
ERR_FAIL_COND_MSG(p_start_marker && !animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, name));
|
||||
ERR_FAIL_COND_MSG(p_end_marker && !animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, name));
|
||||
|
||||
double start_time = p_start_marker ? animation->get_marker_time(p_start_marker) : -1;
|
||||
double end_time = p_end_marker ? animation->get_marker_time(p_end_marker) : -1;
|
||||
|
||||
ERR_FAIL_COND_MSG(p_start_marker && p_end_marker && Animation::is_greater_approx(start_time, end_time), vformat("End marker %s is placed earlier than start marker %s in animation: %s.", p_end_marker, p_start_marker, name));
|
||||
|
||||
if (p_start_marker && Animation::is_less_approx(start_time, 0)) {
|
||||
WARN_PRINT_ED(vformat("Negative time start marker: %s is invalid in the section, so the start of the animation: %s is used instead.", p_start_marker, playback.current.from->animation->get_name()));
|
||||
}
|
||||
if (p_end_marker && Animation::is_less_approx(end_time, 0)) {
|
||||
WARN_PRINT_ED(vformat("Negative time end marker: %s is invalid in the section, so the end of the animation: %s is used instead.", p_end_marker, playback.current.from->animation->get_name()));
|
||||
}
|
||||
|
||||
play_section(name, start_time, end_time, p_custom_blend, p_custom_scale, p_from_end);
|
||||
}
|
||||
|
||||
void AnimationPlayer::play_section(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend, float p_custom_scale, bool p_from_end) {
|
||||
StringName name = p_name;
|
||||
|
||||
if (name == StringName()) {
|
||||
name = playback.assigned;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
|
||||
ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Math::is_equal_approx(p_start_time, p_end_time), "Start time and end time must not equal to each other.");
|
||||
ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Animation::is_greater_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));
|
||||
|
||||
Playback &c = playback;
|
||||
|
||||
if (c.current.from) {
|
||||
@ -442,22 +492,27 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
|
||||
|
||||
c.current.from = &animation_set[name];
|
||||
c.current.speed_scale = p_custom_scale;
|
||||
c.current.start_time = p_start_time;
|
||||
c.current.end_time = p_end_time;
|
||||
|
||||
double start = get_section_start_time();
|
||||
double end = get_section_end_time();
|
||||
|
||||
if (!end_reached) {
|
||||
playback_queue.clear();
|
||||
}
|
||||
|
||||
if (c.assigned != name) { // Reset.
|
||||
c.current.pos = p_from_end ? c.current.from->animation->get_length() : 0;
|
||||
c.current.pos = p_from_end ? end : start;
|
||||
c.assigned = name;
|
||||
emit_signal(SNAME("current_animation_changed"), c.assigned);
|
||||
} else {
|
||||
if (p_from_end && Math::is_zero_approx(c.current.pos)) {
|
||||
if (p_from_end && Math::is_equal_approx(c.current.pos, start)) {
|
||||
// Animation reset but played backwards, set position to the end.
|
||||
seek_internal(c.current.from->animation->get_length(), true, true, true);
|
||||
} else if (!p_from_end && Math::is_equal_approx(c.current.pos, (double)c.current.from->animation->get_length())) {
|
||||
seek_internal(end, true, true, true);
|
||||
} else if (!p_from_end && Math::is_equal_approx(c.current.pos, end)) {
|
||||
// Animation resumed but already ended, set position to the beginning.
|
||||
seek_internal(0, true, true, true);
|
||||
seek_internal(start, true, true, true);
|
||||
} else if (playing) {
|
||||
return;
|
||||
}
|
||||
@ -551,6 +606,8 @@ void AnimationPlayer::set_assigned_animation(const String &p_animation) {
|
||||
ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));
|
||||
playback.current.pos = 0;
|
||||
playback.current.from = &animation_set[p_animation];
|
||||
playback.current.start_time = -1;
|
||||
playback.current.end_time = -1;
|
||||
playback.assigned = p_animation;
|
||||
emit_signal(SNAME("current_animation_changed"), playback.assigned);
|
||||
}
|
||||
@ -603,6 +660,12 @@ void AnimationPlayer::seek_internal(double p_time, bool p_update, bool p_update_
|
||||
}
|
||||
}
|
||||
|
||||
double start = get_section_start_time();
|
||||
double end = get_section_end_time();
|
||||
|
||||
// Clamp the seek position.
|
||||
p_time = CLAMP(p_time, start, end);
|
||||
|
||||
playback.seeked = true;
|
||||
playback.internal_seeked = p_is_internal_seek;
|
||||
|
||||
@ -641,6 +704,55 @@ double AnimationPlayer::get_current_animation_length() const {
|
||||
return playback.current.from->animation->get_length();
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_section_with_markers(const StringName &p_start_marker, const StringName &p_end_marker) {
|
||||
ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");
|
||||
ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));
|
||||
ERR_FAIL_COND_MSG(p_start_marker && !playback.current.from->animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));
|
||||
ERR_FAIL_COND_MSG(p_end_marker && !playback.current.from->animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));
|
||||
double start_time = p_start_marker ? playback.current.from->animation->get_marker_time(p_start_marker) : -1;
|
||||
double end_time = p_end_marker ? playback.current.from->animation->get_marker_time(p_end_marker) : -1;
|
||||
if (p_start_marker && Animation::is_less_approx(start_time, 0)) {
|
||||
WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));
|
||||
}
|
||||
if (p_end_marker && Animation::is_less_approx(end_time, 0)) {
|
||||
WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));
|
||||
}
|
||||
set_section(start_time, end_time);
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_section(double p_start_time, double p_end_time) {
|
||||
ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");
|
||||
ERR_FAIL_COND_MSG(Animation::is_greater_or_equal_approx(p_start_time, 0) && Animation::is_greater_or_equal_approx(p_end_time, 0) && Animation::is_greater_or_equal_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));
|
||||
playback.current.start_time = p_start_time;
|
||||
playback.current.end_time = p_end_time;
|
||||
playback.current.pos = CLAMP(playback.current.pos, get_section_start_time(), get_section_end_time());
|
||||
}
|
||||
|
||||
void AnimationPlayer::reset_section() {
|
||||
playback.current.start_time = -1;
|
||||
playback.current.end_time = -1;
|
||||
}
|
||||
|
||||
double AnimationPlayer::get_section_start_time() const {
|
||||
ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.start_time, "AnimationPlayer has no current animation.");
|
||||
if (Animation::is_less_approx(playback.current.start_time, 0) || playback.current.start_time > playback.current.from->animation->get_length()) {
|
||||
return 0;
|
||||
}
|
||||
return playback.current.start_time;
|
||||
}
|
||||
|
||||
double AnimationPlayer::get_section_end_time() const {
|
||||
ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.end_time, "AnimationPlayer has no current animation.");
|
||||
if (Animation::is_less_approx(playback.current.end_time, 0) || playback.current.end_time > playback.current.from->animation->get_length()) {
|
||||
return playback.current.from->animation->get_length();
|
||||
}
|
||||
return playback.current.end_time;
|
||||
}
|
||||
|
||||
bool AnimationPlayer::has_section() const {
|
||||
return Animation::is_greater_or_equal_approx(playback.current.start_time, 0) || Animation::is_greater_or_equal_approx(playback.current.end_time, 0);
|
||||
}
|
||||
|
||||
void AnimationPlayer::set_autoplay(const String &p_name) {
|
||||
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
|
||||
WARN_PRINT("Setting autoplay after the node has been added to the scene has no effect.");
|
||||
@ -665,13 +777,14 @@ void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) {
|
||||
_clear_caches();
|
||||
Playback &c = playback;
|
||||
// c.blend.clear();
|
||||
double start = get_section_start_time();
|
||||
if (p_reset) {
|
||||
c.blend.clear();
|
||||
if (p_keep_state) {
|
||||
c.current.pos = 0;
|
||||
c.current.pos = start;
|
||||
} else {
|
||||
is_stopping = true;
|
||||
seek_internal(0, true, true, true);
|
||||
seek_internal(start, true, true, true);
|
||||
is_stopping = false;
|
||||
}
|
||||
c.current.from = nullptr;
|
||||
@ -763,20 +876,6 @@ Tween::EaseType AnimationPlayer::get_auto_capture_ease_type() const {
|
||||
return auto_capture_ease_type;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
|
||||
const String pf = p_function;
|
||||
if (p_idx == 0 && (pf == "play" || pf == "play_backwards" || pf == "has_animation" || pf == "queue")) {
|
||||
List<StringName> al;
|
||||
get_animation_list(&al);
|
||||
for (const StringName &name : al) {
|
||||
r_options->push_back(String(name).quote());
|
||||
}
|
||||
}
|
||||
AnimationMixer::get_argument_options(p_function, p_idx, r_options);
|
||||
}
|
||||
#endif
|
||||
|
||||
void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) {
|
||||
AnimationMixer::_animation_removed(p_name, p_library);
|
||||
|
||||
@ -863,7 +962,11 @@ void AnimationPlayer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_auto_capture_ease_type"), &AnimationPlayer::get_auto_capture_ease_type);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("play_section_with_markers", "name", "start_marker", "end_marker", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("play_section", "name", "start_time", "end_time", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(StringName()), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("play_section_with_markers_backwards", "name", "start_marker", "end_marker", "custom_blend"), &AnimationPlayer::play_section_with_markers_backwards, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("play_section_backwards", "name", "start_time", "end_time", "custom_blend"), &AnimationPlayer::play_section_backwards, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(StringName()), DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
|
||||
ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
|
||||
ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));
|
||||
@ -893,6 +996,14 @@ void AnimationPlayer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
|
||||
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_section_with_markers", "start_marker", "end_marker"), &AnimationPlayer::set_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()));
|
||||
ClassDB::bind_method(D_METHOD("set_section", "start_time", "end_time"), &AnimationPlayer::set_section, DEFVAL(-1), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("reset_section"), &AnimationPlayer::reset_section);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_section_start_time"), &AnimationPlayer::get_section_start_time);
|
||||
ClassDB::bind_method(D_METHOD("get_section_end_time"), &AnimationPlayer::get_section_end_time);
|
||||
ClassDB::bind_method(D_METHOD("has_section"), &AnimationPlayer::has_section);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("seek", "seconds", "update", "update_only"), &AnimationPlayer::seek, DEFVAL(false), DEFVAL(false));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation");
|
||||
|
||||
Reference in New Issue
Block a user