AnimationPlayer call frame in loop

Godot Version

4.6 stable

Question

How can I call keyframes from AnimationPlayer in script ?
I keep looking in documentation but not seeing word how to do a such thing .
I found markers and sections but this be extra work.

Use case : make sprite for each keyframe from current animation.

The track where is keyframes : Armature/Skeleton3D

So far I got this from docs :


However no word about keyframe or frame on track and what is track_idx and key_idx parameters ?

What exact information do you need to extract?

Animation::track_get_key_time() should get you the keyframe time. idx things are indexes of a track and a key. So if you want to consecutively position the animation player to keyframes, iterate from zero to Animation::track_get_key_count() get time of that keyframe and use AnimationPlayer::seek() to set the animation position.

1 Like

At a guess that would be track_get_key_value and loop through each key with the number from track_get_key_count.

Or is that too obvious :rofl:

1 Like

Good logic ,
I’ll try make it to code tomorrow

Tried a multiple things with this SpriteSheet generator , this seem to work for now as the code .
But one thing I noticed if I don’t use play and pause animation_player it gets confused and reacted only when I scrolled in script window, and also only used current_animation .

Is there some reason why is this working this way ?
small demo

@tool
extends SubViewport
@export_tool_button("Generate Sprite")
var generate_sprite = func():
	var animation_player = $Barbarian/AnimationPlayer
	var format = get_texture().get_image().get_format()
	var animation_list = animation_player.get_animation_list()
	for current_animation_number in animation_list.size():
		var anim_name = animation_list[current_animation_number]
		var anim = animation_player.get_animation(anim_name)
		var track_idx = 0
		var anim_frame_count = anim.track_get_key_count(track_idx)
		var columns: int = 8 if anim_frame_count > 8 else anim_frame_count
		var rows: int = anim_frame_count / columns
		var name_of_spritesheet = "res://spritesheets/" + str(anim_name.replace('/','')) + ".png"

		print("Animation: ", anim_name)
		print("Frame count: ", anim_frame_count)
		print("Animations count: ", animation_list.size())

		var result = Image.create_empty(size.x * columns, size.y * rows, false, format)
		var frame_index = 0
		animation_player.play(anim_name)
		animation_player.pause()
		
		for r in rows:
			for c in columns:
				var key_time = anim.track_get_key_time(track_idx, frame_index)
				animation_player.seek(key_time, true)
				await get_tree().process_frame
				await RenderingServer.frame_post_draw
				var image = get_texture().get_image()
				result.blit_rect(image, Rect2i(0, 0, size.x, size.y), Vector2i(size.x * c, size.y * r))
				frame_index += 1
		result.save_png(name_of_spritesheet)
		print("completed one spritesheet"+ str(anim_name.replace('/','')))

with those two added lines

		animation_player.play(anim_name)
		animation_player.pause()

is there way I should organise animation_library and sprite2d texture switch of assets to make it seamless ? - end goal is make ready character2d with AnimationPlayer and loaded Sprite2D with change for current animation .

get_animation_list() doesn’t get you list of animations. Check the reference. To get all animation you need to query animationplayer’s animation library.

1 Like