Sprite animation FPS in runtime? (SOLVED)

Hi all!

I’ve found 2 similar topics tackling this question …but I need to be sure.

[How to change FPS of an animation, in runtime? (DEF-2249) (SOLVED)]
[Defold 1.2.151 has been released]


Let’s say I want to reduce the FPS of a walking animation when the character object slows down.

Does something exist to easily change this property in code:
image

Or do I need to use the cursor thing? (which allows to play a specific frame of an animation, if I understand well) In this case, I’ll have to play each frame “manually” and update the time between each frame, right? (not a problem in itself, but I want to make sure I won’t do it while a super easy solution already exists :grinning: even though practicing is never a bad thing for a newcomer)

Or maybe something else?

Thanks!

Rag’

Edit: Completely read the topic wrong.

sprite.play_flipbook() allows you to set the playback_rate you should be able to manually count the frames based on the set rate and then play the animation again with the proper frame offset set when you want to change the speed so that you can slowmo/speed up animations without them appearing to restart.

1 Like

Thank for your answer! :slight_smile: All the keywords were included in it, but I struggled with almost every tiny thing (flipbook? playback_rate? offset? everything is new to me :baby: I’m currently tweaking the second tutorial ahah)… but I suppose this struggle is unavoidable.

Anyway, I ended up making it work by controlling each frame and playing them at my own pace, I can know which frame is currently played etc.

I no longer use the “FPS” property nor the playback_rate you mentioned in your message. I don’t know if this is how I should do, since this is so far away from the tutorial… but it works and at least I know why.

For now, every second the offset is increased by 0.1 (temporary values)… So I was wondering:

Is there a way to get (in code) the number of frames of a given animation? (from that I could calculate what’s missing - edit : in the meantime I’m using local variables (1 per animation), and it seems to work fine. :nerd_face:

Looks like everything is not working as intended after all :sweat_smile:

I noticed that a frame was “subtly” longer than the others and I decided to add the frame number directly on the image (in red), while the frame number in code is displayed in blue… (as well as the offset and the timer)
image image

As you can see, the image 1 appears twice in a row and the last image (5) is ignored… (while in code the frame numbers goes from 1 to 5 as expected)

I thought “offset = 1” would display the last frame, but does it mean that it displays the 1st frame instead (just like “offset = 0”) ? Like a loop or something?

Or is something just wrong with my calculations or how I understand/use this function? :baby:

Can you share some code so we can see what you are trying to do?

Hi!

edit (spoiler / if you don’t want to read the wall of text :grin:):
there is no blocking issue anymore, but 2 questions

I just wanted to “manually” control how the animation frames at played, at my own pace, so I could speed up or slow it down the animation in runtime (ex: when the character himself slows down)
=> originally I just wanted to change the animation FPS in runtime, but it was not possible.

It’s probably not the most “elegant” way to do so (I don’t know… see code below if you want to take a look), but it works (and as said above, “I know why”, which is super important to me at this point)

My previous message said that “offset = 0” and “offset = 1” were targeting the same animation frame (Question: is it normal?), so I had to “exclude” offset =1 and consider than my max offset was no longer 1 but [1/(nb_frames_anim)*(nb_frames_anim-1)] … and it works.

Note: I don’t use playerback_rate to control the animation speed, and I ignore the FPS property that I wanted originally change.

Actually I think there is no blocking issue anymore. :thinking:
I would have another question though: is there a way to get (in code) the number of frames of a given animation? (this way I wouldn’t have to manually update the dedicated local variables when adding a frame to an animation)


--- Anim "local" variables
local timer_max_anim_idle = 0.15
local timer_max_anim_walk_up = 0.1
local timer_max_anim_walk_down = timer_max_anim_walk_up
local timer_max_anim_walk_left = timer_max_anim_walk_up
local timer_max_anim_walk_right = timer_max_anim_walk_up

local nb_frames_anim_idle = 5
local nb_frames_anim_walk_up = 6
local nb_frames_anim_walk_down = 6
local nb_frames_anim_walk_left = 6
local nb_frames_anim_walk_right  = 6

local play_properties = { -- char ation properties
	offset  = 0.0,  -- normalized initial value of the animation cursor , 0 = 1st frame, 1 = last frame
	playback_rate = 0
}

function init(self)
	self.anim_timer = 0

function update(self, dt)
-----------------------------
--========== ANIM =========--
-----------------------------
	local timer_max_anim = 0
	local nb_frames_anim = 0

	if self.direction_x == 0 and self.direction_y == 0 then
		anim = hash("idle")
		timer_max_anim = timer_max_anim_idle	
		nb_frames_anim = nb_frames_anim_idle
	elseif self.direction_x > 0 then
		anim = hash("right")
		timer_max_anim = timer_max_anim_walk_right
		nb_frames_anim = nb_frames_anim_walk_right
	elseif self.direction_x < 0 then
		anim = hash("left")
		timer_max_anim = timer_max_anim_walk_left
		nb_frames_anim = nb_frames_anim_walk_left
	elseif self.direction_y > 0 then
		anim = hash("top")
		timer_max_anim = timer_max_anim_walk_up
		nb_frames_anim = nb_frames_anim_walk_up
	elseif self.direction_y < 0 then
		anim = hash("down")
		timer_max_anim = timer_max_anim_walk_down
		nb_frames_anim = nb_frames_anim_walk_down
	end

	if self.anim_timer < timer_max_anim then
		self.anim_timer = self.anim_timer + dt
	elseif self.anim_timer >= timer_max_anim*self.anim_speed_factor then
		self.anim_timer = 0
		play_properties["offset"] = play_properties["offset"]+1/(nb_frames_anim)
		--play_properties["offset"] = play_properties["offset"]+0.1
	end
	
	if play_properties["offset"] > 1/(nb_frames_anim)*(nb_frames_anim-1) then
		play_properties["offset"] = 0
	end
	
	local color = vmath.vector4(0, 1, 1, 1)
	debugdraw.text("Frame #: "..play_properties["offset"]*(nb_frames_anim)+1,p.x+40,p.y+30,color)
	debugdraw.text("Offset: "..play_properties["offset"],p.x+40,p.y+15,color)
	debugdraw.text("Timer: "..lume.round(self.anim_timer,0.1),p.x+40,p.y+0,color)
	debugdraw.text("Speed_Factor: "..lume.round(self.anim_speed_factor,0.1),p.x+40,p.y-15,color)

	--if anim ~= self.current_anim then
		-- msg.post("#sprite", "play_animation", { id = anim })
		sprite.play_flipbook("#sprite", anim, nil, play_properties)
		--debugdraw.text("offset-frame: "..offset,p.x,p.y+100,color_white)
		self.current_anim = anim
	--end
1 Like

Hi again!

Before marking this topic as “solved”, I’d like to know if someone had the answer to this question:

Is there a way to get (in code) the number of frames of an animation?

Thanks :slight_smile:

Hello Ragetto,

I don’t think there is any way to do that, I also think it’s a pretty unintuitive thing to need to do, if you have setup your animations correctly I don’t see why you would want to know the specific frame you are at in an animation.

The “animation_done” message can be used to know when you have finished an animation (once forward, once backward, once ping pong modes), and it can be used to change the playback speed each time you complete the animation if you want to adjust it for the speed of the sprite.

Cheers,
Spen

2 Likes

For something like a fighting game (ie. move A can link into move B on frames 6-9), or even just determining whether something can be cancel linked into from another action in something like a platformer(ie. you can only jump out of a dash between frames 7-14), this would be highly important.

2 Likes

If you require this kind of support fine grained control I’d probably not use flipbook animations at all and instead create my own animation system where I manually track and set animation frames.

Unless it is good enough to go from exact frame count to percentages in which case reading the animation cursor might be good enough:

“You can only jump out of a dash between 0.5 to 1.0”

1 Like

As @rocamocha said, you may want to know at which frame your character will hit an enemy, trigger an FX/SFX, or something… I suppose you could split an attack animation into multiple sub-animations, and use “animation_done” to trigger the FX and play the next part etc… but isn’t it more complicated? (or just another way to do it? I don’t know)

Actually, I wanted to know if there was a way to know the total number of frames of an animation (in runtime), but this may not be that useful after all… It was just out of curiosity, but it looks like this useless question will remain unanswered forever :grin:

In my “example” above, I set the frames manually, converted the frame number to offset, and used flipbook to play the animation. Not sure this is the only or most elegant way to proceed, but it seemed to work fine since I know at any moment which frame is being played.

I would have just one last question for you, @britzl:

When I set the offset to 1, it displays the same frame as when the offset is set to 0 => I suppose this is how it is supposed to work? (quick example with a 5-frame animation)


5 frames… offset divided into 5 parts… well it becomes obvious with this quick sketch :see_no_evil:
(but you can still confirm this is how it works, so I can move on peacefully :pray:)

1 Like

This is the current behavior at least :slight_smile: And it makes sense if you have a looping animation. But in the case of a “once forward” animation I would perhaps also have expected offset = 1.0 to result in the last frame.

In the future we might implement a frame_offset or similar to allow you to set the a specific frame of a flipbook animation, or perhaps the ability to get the number of frames or the length of a single frame.

1 Like