diff options
Diffstat (limited to 'scene')
-rw-r--r-- | scene/2d/animated_sprite_2d.cpp | 73 | ||||
-rw-r--r-- | scene/2d/animated_sprite_2d.h | 6 | ||||
-rw-r--r-- | scene/3d/sprite_3d.cpp | 69 | ||||
-rw-r--r-- | scene/3d/sprite_3d.h | 6 | ||||
-rw-r--r-- | scene/resources/sprite_frames.cpp | 62 | ||||
-rw-r--r-- | scene/resources/sprite_frames.h | 32 |
6 files changed, 144 insertions, 104 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 50892b72e2..a4a965aa41 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -72,7 +72,7 @@ bool AnimatedSprite2D::_edit_use_rect() const { Ref<Texture2D> t; if (animation) { - t = frames->get_frame(animation, frame); + t = frames->get_frame_texture(animation, frame); } return t.is_valid(); } @@ -92,7 +92,7 @@ Rect2 AnimatedSprite2D::_get_rect() const { Ref<Texture2D> t; if (animation) { - t = frames->get_frame(animation, frame); + t = frames->get_frame_texture(animation, frame); } if (t.is_null()) { return Rect2(); @@ -172,17 +172,21 @@ void AnimatedSprite2D::_notification(int p_what) { return; } - double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); - if (speed == 0) { - return; // Do nothing. - } - int last_frame = frames->get_frame_count(animation) - 1; - double remaining = get_process_delta_time(); + int i = 0; while (remaining) { - if (timeout <= 0) { - timeout = _get_frame_duration(); + // Animation speed may be changed by animation_finished or frame_changed signals. + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + if (speed == 0) { + return; // Do nothing. + } + + // Frame count may be changed by animation_finished or frame_changed signals. + int fc = frames->get_frame_count(animation); + + if (timeout <= 0) { + int last_frame = fc - 1; if (!playing_backwards) { // Forward. if (frame >= last_frame) { @@ -217,14 +221,21 @@ void AnimatedSprite2D::_notification(int p_what) { } } + timeout = _get_frame_duration(); + queue_redraw(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } - double to_process = MIN(timeout, remaining); + double to_process = MIN(timeout / speed, remaining); + timeout -= to_process * speed; remaining -= to_process; - timeout -= to_process; + + i++; + if (i > fc) { + return; // Prevents freezing if to_process is each time much less than remaining. + } } } break; @@ -233,7 +244,7 @@ void AnimatedSprite2D::_notification(int p_what) { return; } - Ref<Texture2D> texture = frames->get_frame(animation, frame); + Ref<Texture2D> texture = frames->get_frame_texture(animation, frame); if (texture.is_null()) { return; } @@ -312,7 +323,6 @@ void AnimatedSprite2D::set_frame(int p_frame) { frame = p_frame; _reset_timeout(); queue_redraw(); - emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -320,22 +330,12 @@ int AnimatedSprite2D::get_frame() const { return frame; } -void AnimatedSprite2D::set_speed_scale(double p_speed_scale) { - if (speed_scale == p_speed_scale) { - return; - } - - double elapsed = _get_frame_duration() - timeout; - +void AnimatedSprite2D::set_speed_scale(float p_speed_scale) { speed_scale = p_speed_scale; playing_backwards = signbit(speed_scale) != backwards; - - // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed. - _reset_timeout(); - timeout -= elapsed; } -double AnimatedSprite2D::get_speed_scale() const { +float AnimatedSprite2D::get_speed_scale() const { return speed_scale; } @@ -379,8 +379,8 @@ bool AnimatedSprite2D::is_flipped_v() const { void AnimatedSprite2D::_res_changed() { set_frame(frame); - queue_redraw(); + notify_property_list_changed(); } void AnimatedSprite2D::set_playing(bool p_playing) { @@ -388,7 +388,7 @@ void AnimatedSprite2D::set_playing(bool p_playing) { return; } playing = p_playing; - _reset_timeout(); + playing_backwards = signbit(speed_scale) != backwards; set_process_internal(playing); notify_property_list_changed(); } @@ -414,23 +414,18 @@ void AnimatedSprite2D::play(const StringName &p_animation, bool p_backwards) { void AnimatedSprite2D::stop() { set_playing(false); + backwards = false; + _reset_timeout(); } double AnimatedSprite2D::_get_frame_duration() { if (frames.is_valid() && frames->has_animation(animation)) { - double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); - if (speed > 0) { - return 1.0 / speed; - } + return frames->get_frame_duration(animation, frame); } return 0.0; } void AnimatedSprite2D::_reset_timeout() { - if (!playing) { - return; - } - timeout = _get_frame_duration(); is_over = false; } @@ -444,8 +439,8 @@ void AnimatedSprite2D::set_animation(const StringName &p_animation) { } animation = p_animation; - _reset_timeout(); set_frame(0); + _reset_timeout(); notify_property_list_changed(); queue_redraw(); } @@ -455,12 +450,10 @@ StringName AnimatedSprite2D::get_animation() const { } PackedStringArray AnimatedSprite2D::get_configuration_warnings() const { - PackedStringArray warnings = Node::get_configuration_warnings(); - + PackedStringArray warnings = Node2D::get_configuration_warnings(); if (frames.is_null()) { warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite2D to display frames.")); } - return warnings; } diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index 492917cf89..c1d35f3a2f 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -43,7 +43,7 @@ class AnimatedSprite2D : public Node2D { bool backwards = false; StringName animation = "default"; int frame = 0; - float speed_scale = 1.0f; + float speed_scale = 1.0; bool centered = true; Point2 offset; @@ -94,8 +94,8 @@ public: void set_frame(int p_frame); int get_frame() const; - void set_speed_scale(double p_speed_scale); - double get_speed_scale() const; + void set_speed_scale(float p_speed_scale); + float get_speed_scale() const; void set_centered(bool p_center); bool is_centered() const; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 864a5332dc..71d76c0d1c 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -837,7 +837,7 @@ void AnimatedSprite3D::_draw() { return; } - Ref<Texture2D> texture = frames->get_frame(animation, frame); + Ref<Texture2D> texture = frames->get_frame_texture(animation, frame); if (texture.is_null()) { set_base(RID()); return; @@ -921,17 +921,21 @@ void AnimatedSprite3D::_notification(int p_what) { return; } - double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); - if (speed == 0) { - return; // Do nothing. - } - int last_frame = frames->get_frame_count(animation) - 1; - double remaining = get_process_delta_time(); + int i = 0; while (remaining) { - if (timeout <= 0) { - timeout = _get_frame_duration(); + // Animation speed may be changed by animation_finished or frame_changed signals. + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + if (speed == 0) { + return; // Do nothing. + } + + // Frame count may be changed by animation_finished or frame_changed signals. + int fc = frames->get_frame_count(animation); + + if (timeout <= 0) { + int last_frame = fc - 1; if (!playing_backwards) { // Forward. if (frame >= last_frame) { @@ -966,14 +970,21 @@ void AnimatedSprite3D::_notification(int p_what) { } } + timeout = _get_frame_duration(); + _queue_redraw(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } - double to_process = MIN(timeout, remaining); + double to_process = MIN(timeout / speed, remaining); + timeout -= to_process * speed; remaining -= to_process; - timeout -= to_process; + + i++; + if (i > fc) { + return; // Prevents freezing if to_process is each time much less than remaining. + } } } break; } @@ -1028,7 +1039,6 @@ void AnimatedSprite3D::set_frame(int p_frame) { frame = p_frame; _reset_timeout(); _queue_redraw(); - emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -1036,22 +1046,12 @@ int AnimatedSprite3D::get_frame() const { return frame; } -void AnimatedSprite3D::set_speed_scale(double p_speed_scale) { - if (speed_scale == p_speed_scale) { - return; - } - - double elapsed = _get_frame_duration() - timeout; - +void AnimatedSprite3D::set_speed_scale(float p_speed_scale) { speed_scale = p_speed_scale; playing_backwards = signbit(speed_scale) != backwards; - - // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed. - _reset_timeout(); - timeout -= elapsed; } -double AnimatedSprite3D::get_speed_scale() const { +float AnimatedSprite3D::get_speed_scale() const { return speed_scale; } @@ -1065,7 +1065,7 @@ Rect2 AnimatedSprite3D::get_item_rect() const { Ref<Texture2D> t; if (animation) { - t = frames->get_frame(animation, frame); + t = frames->get_frame_texture(animation, frame); } if (t.is_null()) { return Rect2(0, 0, 1, 1); @@ -1086,8 +1086,8 @@ Rect2 AnimatedSprite3D::get_item_rect() const { void AnimatedSprite3D::_res_changed() { set_frame(frame); - _queue_redraw(); + notify_property_list_changed(); } void AnimatedSprite3D::set_playing(bool p_playing) { @@ -1095,7 +1095,7 @@ void AnimatedSprite3D::set_playing(bool p_playing) { return; } playing = p_playing; - _reset_timeout(); + playing_backwards = signbit(speed_scale) != backwards; set_process_internal(playing); notify_property_list_changed(); } @@ -1121,23 +1121,18 @@ void AnimatedSprite3D::play(const StringName &p_animation, bool p_backwards) { void AnimatedSprite3D::stop() { set_playing(false); + backwards = false; + _reset_timeout(); } double AnimatedSprite3D::_get_frame_duration() { if (frames.is_valid() && frames->has_animation(animation)) { - double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); - if (speed > 0) { - return 1.0 / speed; - } + return frames->get_frame_duration(animation, frame); } return 0.0; } void AnimatedSprite3D::_reset_timeout() { - if (!playing) { - return; - } - timeout = _get_frame_duration(); is_over = false; } @@ -1145,13 +1140,14 @@ void AnimatedSprite3D::_reset_timeout() { void AnimatedSprite3D::set_animation(const StringName &p_animation) { ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation)); ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation)); + if (animation == p_animation) { return; } animation = p_animation; - _reset_timeout(); set_frame(0); + _reset_timeout(); notify_property_list_changed(); _queue_redraw(); } @@ -1165,7 +1161,6 @@ PackedStringArray AnimatedSprite3D::get_configuration_warnings() const { if (frames.is_null()) { warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); } - return warnings; } diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 276c4fe1cc..c5509aa723 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -214,7 +214,7 @@ class AnimatedSprite3D : public SpriteBase3D { bool backwards = false; StringName animation = "default"; int frame = 0; - float speed_scale = 1.0f; + float speed_scale = 1.0; bool centered = false; @@ -248,8 +248,8 @@ public: void set_frame(int p_frame); int get_frame() const; - void set_speed_scale(double p_speed_scale); - double get_speed_scale() const; + void set_speed_scale(float p_speed_scale); + float get_speed_scale() const; virtual Rect2 get_item_rect() const override; diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index 4892f4cd17..c101974248 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -32,19 +32,40 @@ #include "scene/scene_string_names.h" -void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos) { +void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_texture, float p_duration, int p_at_pos) { HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); + p_duration = MAX(0.0, p_duration); + + Frame frame = { p_texture, p_duration }; + if (p_at_pos >= 0 && p_at_pos < E->value.frames.size()) { - E->value.frames.insert(p_at_pos, p_frame); + E->value.frames.insert(p_at_pos, frame); } else { - E->value.frames.push_back(p_frame); + E->value.frames.push_back(frame); } emit_changed(); } +void SpriteFrames::set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_texture, float p_duration) { + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); + ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); + ERR_FAIL_COND(p_idx < 0); + if (p_idx >= E->value.frames.size()) { + return; + } + + p_duration = MAX(0.0, p_duration); + + Frame frame = { p_texture, p_duration }; + + E->value.frames.write[p_idx] = frame; + + emit_changed(); +} + int SpriteFrames::get_frame_count(const StringName &p_anim) const { HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); @@ -57,6 +78,7 @@ void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) { ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); E->value.frames.remove_at(p_idx); + emit_changed(); } @@ -65,6 +87,7 @@ void SpriteFrames::clear(const StringName &p_anim) { ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); E->value.frames.clear(); + emit_changed(); } @@ -151,7 +174,10 @@ Array SpriteFrames::_get_animations() const { d["loop"] = anim.loop; Array frames; for (int i = 0; i < anim.frames.size(); i++) { - frames.push_back(anim.frames[i]); + Dictionary f; + f["texture"] = anim.frames[i].texture; + f["duration"] = anim.frames[i].duration; + frames.push_back(f); } d["frames"] = frames; anims.push_back(d); @@ -175,8 +201,21 @@ void SpriteFrames::_set_animations(const Array &p_animations) { anim.loop = d["loop"]; Array frames = d["frames"]; for (int j = 0; j < frames.size(); j++) { + // For compatibility. Ref<Resource> res = frames[j]; - anim.frames.push_back(res); + if (res.is_valid()) { + Frame frame = { res, 1.0 }; + anim.frames.push_back(frame); + continue; + } + + Dictionary f = frames[j]; + + ERR_CONTINUE(!f.has("texture")); + ERR_CONTINUE(!f.has("duration")); + + Frame frame = { f["texture"], f["duration"] }; + anim.frames.push_back(frame); } animations[d["name"]] = anim; @@ -191,17 +230,20 @@ void SpriteFrames::_bind_methods() { ClassDB::bind_method(D_METHOD("get_animation_names"), &SpriteFrames::get_animation_names); - ClassDB::bind_method(D_METHOD("set_animation_speed", "anim", "speed"), &SpriteFrames::set_animation_speed); + ClassDB::bind_method(D_METHOD("set_animation_speed", "anim", "fps"), &SpriteFrames::set_animation_speed); ClassDB::bind_method(D_METHOD("get_animation_speed", "anim"), &SpriteFrames::get_animation_speed); ClassDB::bind_method(D_METHOD("set_animation_loop", "anim", "loop"), &SpriteFrames::set_animation_loop); ClassDB::bind_method(D_METHOD("get_animation_loop", "anim"), &SpriteFrames::get_animation_loop); - ClassDB::bind_method(D_METHOD("add_frame", "anim", "frame", "at_position"), &SpriteFrames::add_frame, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_frame_count", "anim"), &SpriteFrames::get_frame_count); - ClassDB::bind_method(D_METHOD("get_frame", "anim", "idx"), &SpriteFrames::get_frame); - ClassDB::bind_method(D_METHOD("set_frame", "anim", "idx", "txt"), &SpriteFrames::set_frame); + ClassDB::bind_method(D_METHOD("add_frame", "anim", "texture", "duration", "at_position"), &SpriteFrames::add_frame, DEFVAL(1.0), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("set_frame", "anim", "idx", "texture", "duration"), &SpriteFrames::set_frame, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("remove_frame", "anim", "idx"), &SpriteFrames::remove_frame); + + ClassDB::bind_method(D_METHOD("get_frame_count", "anim"), &SpriteFrames::get_frame_count); + ClassDB::bind_method(D_METHOD("get_frame_texture", "anim", "idx"), &SpriteFrames::get_frame_texture); + ClassDB::bind_method(D_METHOD("get_frame_duration", "anim", "idx"), &SpriteFrames::get_frame_duration); + ClassDB::bind_method(D_METHOD("clear", "anim"), &SpriteFrames::clear); ClassDB::bind_method(D_METHOD("clear_all"), &SpriteFrames::clear_all); diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h index 56fd2b8b56..61bead6948 100644 --- a/scene/resources/sprite_frames.h +++ b/scene/resources/sprite_frames.h @@ -36,10 +36,15 @@ class SpriteFrames : public Resource { GDCLASS(SpriteFrames, Resource); + struct Frame { + Ref<Texture2D> texture; + float duration = 1.0; + }; + struct Anim { double speed = 5.0; bool loop = true; - Vector<Ref<Texture2D>> frames; + Vector<Frame> frames; }; HashMap<StringName, Anim> animations; @@ -65,9 +70,13 @@ public: void set_animation_loop(const StringName &p_anim, bool p_loop); bool get_animation_loop(const StringName &p_anim) const; - void add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos = -1); + void add_frame(const StringName &p_anim, const Ref<Texture2D> &p_texture, float p_duration = 1.0, int p_at_pos = -1); + void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_texture, float p_duration = 1.0); + void remove_frame(const StringName &p_anim, int p_idx); + int get_frame_count(const StringName &p_anim) const; - _FORCE_INLINE_ Ref<Texture2D> get_frame(const StringName &p_anim, int p_idx) const { + + _FORCE_INLINE_ Ref<Texture2D> get_frame_texture(const StringName &p_anim, int p_idx) const { HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, Ref<Texture2D>(), "Animation '" + String(p_anim) + "' doesn't exist."); ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>()); @@ -75,19 +84,20 @@ public: return Ref<Texture2D>(); } - return E->value.frames[p_idx]; + return E->value.frames[p_idx].texture; } - void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_frame) { - HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); - ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - ERR_FAIL_COND(p_idx < 0); + _FORCE_INLINE_ float get_frame_duration(const StringName &p_anim, int p_idx) const { + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); + ERR_FAIL_COND_V_MSG(!E, 0.0, "Animation '" + String(p_anim) + "' doesn't exist."); + ERR_FAIL_COND_V(p_idx < 0, 0.0); if (p_idx >= E->value.frames.size()) { - return; + return 0.0; } - E->value.frames.write[p_idx] = p_frame; + + return E->value.frames[p_idx].duration; } - void remove_frame(const StringName &p_anim, int p_idx); + void clear(const StringName &p_anim); void clear_all(); |