Merge pull request #65188 from Mickeon/animated-texture-speed-scale

Rework AnimatedTexture's `fps` into `speed_scale`
This commit is contained in:
Yuri Sizov 2022-09-08 18:25:35 +03:00 committed by GitHub
commit 141fdac36c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 47 deletions

View File

@ -5,18 +5,18 @@
</brief_description>
<description>
[AnimatedTexture] is a resource format for frame-based animations, where multiple textures can be chained automatically with a predefined delay for each frame. Unlike [AnimationPlayer] or [AnimatedSprite2D], it isn't a [Node], but has the advantage of being usable anywhere a [Texture2D] resource can be used, e.g. in a [TileSet].
The playback of the animation is controlled by the [member fps] property as well as each frame's optional delay (see [method set_frame_delay]). The animation loops, i.e. it will restart at frame 0 automatically after playing the last frame.
The playback of the animation is controlled by the [member speed_scale] property, as well as each frame's duration (see [method set_frame_duration]). The animation loops, i.e. it will restart at frame 0 automatically after playing the last frame.
[AnimatedTexture] currently requires all frame textures to have the same size, otherwise the bigger ones will be cropped to match the smallest one.
[b]Note:[/b] AnimatedTexture doesn't support using [AtlasTexture]s. Each frame needs to be a separate [Texture2D].
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_frame_delay" qualifiers="const">
<method name="get_frame_duration" qualifiers="const">
<return type="float" />
<param index="0" name="frame" type="int" />
<description>
Returns the given frame's delay value.
Returns the given [param frame]'s duration, in seconds.
</description>
</method>
<method name="get_frame_texture" qualifiers="const">
@ -26,19 +26,12 @@
Returns the given frame's [Texture2D].
</description>
</method>
<method name="set_frame_delay">
<method name="set_frame_duration">
<return type="void" />
<param index="0" name="frame" type="int" />
<param index="1" name="delay" type="float" />
<param index="1" name="duration" type="float" />
<description>
Sets an additional delay (in seconds) between this frame and the next one, that will be added to the time interval defined by [member fps]. By default, frames have no delay defined. If a delay value is defined, the final time interval between this frame and the next will be [code]1.0 / fps + delay[/code].
For example, for an animation with 3 frames, 2 FPS and a frame delay on the second frame of 1.2, the resulting playback will be:
[codeblock]
Frame 0: 0.5 s (1 / fps)
Frame 1: 1.7 s (1 / fps + 1.2)
Frame 2: 0.5 s (1 / fps)
Total duration: 2.7 s
[/codeblock]
Sets the duration of any given [param frame]. The final duration is affected by the [member speed_scale]. If set to [code]0[/code], the frame is skipped during playback.
</description>
</method>
<method name="set_frame_texture">
@ -55,10 +48,6 @@
<member name="current_frame" type="int" setter="set_current_frame" getter="get_current_frame">
Sets the currently visible frame of the texture.
</member>
<member name="fps" type="float" setter="set_fps" getter="get_fps" default="4.0">
Animation speed in frames per second. This value defines the default time interval between two frames of the animation, and thus the overall duration of the animation loop based on the [member frames] property. A value of 0 means no predefined number of frames per second, the animation will play according to each frame's frame delay (see [method set_frame_delay]).
For example, an animation with 8 frames, no frame delay and a [code]fps[/code] value of 2 will run for 4 seconds, with each frame lasting 0.5 seconds.
</member>
<member name="frames" type="int" setter="set_frames" getter="get_frames" default="1">
Number of frames to use in the animation. While you can create the frames independently with [method set_frame_texture], you need to set this value for the animation to take new frames into account. The maximum number of frames is [constant MAX_FRAMES].
</member>
@ -68,6 +57,9 @@
<member name="pause" type="bool" setter="set_pause" getter="get_pause" default="false">
If [code]true[/code], the animation will pause where it currently is (i.e. at [member current_frame]). The animation will continue from where it was paused when changing this property to [code]false[/code].
</member>
<member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0">
The animation speed is multiplied by this value. If set to a negative value, the animation is played in reverse.
</member>
</members>
<constants>
<constant name="MAX_FRAMES" value="256">

View File

@ -2617,26 +2617,30 @@ void AnimatedTexture::_update_proxy() {
time += delta;
float limit;
if (fps == 0) {
limit = 0;
} else {
limit = 1.0 / fps;
}
float speed = speed_scale == 0 ? 0 : abs(1.0 / speed_scale);
int iter_max = frame_count;
while (iter_max && !pause) {
float frame_limit = limit + frames[current_frame].delay_sec;
float frame_limit = frames[current_frame].duration * speed;
if (time > frame_limit) {
current_frame++;
if (speed_scale > 0.0) {
current_frame++;
} else {
current_frame--;
}
if (current_frame >= frame_count) {
if (one_shot) {
current_frame = frame_count - 1;
} else {
current_frame = 0;
}
} else if (current_frame < 0) {
if (one_shot) {
current_frame = 0;
} else {
current_frame = frame_count - 1;
}
}
time -= frame_limit;
@ -2710,30 +2714,30 @@ Ref<Texture2D> AnimatedTexture::get_frame_texture(int p_frame) const {
return frames[p_frame].texture;
}
void AnimatedTexture::set_frame_delay(int p_frame, float p_delay_sec) {
void AnimatedTexture::set_frame_duration(int p_frame, float p_duration) {
ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
RWLockRead r(rw_lock);
frames[p_frame].delay_sec = p_delay_sec;
frames[p_frame].duration = p_duration;
}
float AnimatedTexture::get_frame_delay(int p_frame) const {
float AnimatedTexture::get_frame_duration(int p_frame) const {
ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0);
RWLockRead r(rw_lock);
return frames[p_frame].delay_sec;
return frames[p_frame].duration;
}
void AnimatedTexture::set_fps(float p_fps) {
ERR_FAIL_COND(p_fps < 0 || p_fps >= 1000);
void AnimatedTexture::set_speed_scale(float p_scale) {
ERR_FAIL_COND(p_scale < -1000 || p_scale >= 1000);
fps = p_fps;
speed_scale = p_scale;
}
float AnimatedTexture::get_fps() const {
return fps;
float AnimatedTexture::get_speed_scale() const {
return speed_scale;
}
int AnimatedTexture::get_width() const {
@ -2812,24 +2816,24 @@ void AnimatedTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_one_shot", "one_shot"), &AnimatedTexture::set_one_shot);
ClassDB::bind_method(D_METHOD("get_one_shot"), &AnimatedTexture::get_one_shot);
ClassDB::bind_method(D_METHOD("set_fps", "fps"), &AnimatedTexture::set_fps);
ClassDB::bind_method(D_METHOD("get_fps"), &AnimatedTexture::get_fps);
ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &AnimatedTexture::set_speed_scale);
ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedTexture::get_speed_scale);
ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture);
ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture);
ClassDB::bind_method(D_METHOD("set_frame_delay", "frame", "delay"), &AnimatedTexture::set_frame_delay);
ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay);
ClassDB::bind_method(D_METHOD("set_frame_duration", "frame", "duration"), &AnimatedTexture::set_frame_duration);
ClassDB::bind_method(D_METHOD("get_frame_duration", "frame"), &AnimatedTexture::get_frame_duration);
ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames");
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_frame", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_frame", "get_current_frame");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pause"), "set_pause", "get_pause");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-60,60,0.1,or_greater,or_lesser"), "set_speed_scale", "get_speed_scale");
for (int i = 0; i < MAX_FRAMES; i++) {
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_texture", "get_frame_texture", i);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_delay", "get_frame_delay", i);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "frame_" + itos(i) + "/duration", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_duration", "get_frame_duration", i);
}
BIND_CONSTANT(MAX_FRAMES);

View File

@ -922,7 +922,7 @@ private:
struct Frame {
Ref<Texture2D> texture;
float delay_sec = 0.0;
float duration = 1.0;
};
Frame frames[MAX_FRAMES];
@ -930,7 +930,7 @@ private:
int current_frame = 0;
bool pause = false;
bool one_shot = false;
float fps = 4.0;
float speed_scale = 1.0;
float time = 0.0;
@ -958,11 +958,11 @@ public:
void set_frame_texture(int p_frame, const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_frame_texture(int p_frame) const;
void set_frame_delay(int p_frame, float p_delay_sec);
float get_frame_delay(int p_frame) const;
void set_frame_duration(int p_frame, float p_duration);
float get_frame_duration(int p_frame) const;
void set_fps(float p_fps);
float get_fps() const;
void set_speed_scale(float p_scale);
float get_speed_scale() const;
virtual int get_width() const override;
virtual int get_height() const override;