Skip to content

Instantly share code, notes, and snippets.

@belzecue
Last active January 1, 2024 17:20
Show Gist options
  • Save belzecue/c6625d1cf9257997fdfe16047f06dacc to your computer and use it in GitHub Desktop.
Save belzecue/c6625d1cf9257997fdfe16047f06dacc to your computer and use it in GitHub Desktop.
Capture an animation to a PNG sequence, e.g. to turn into an Animated Sprite.
"""
Attach this script to the parent node of an AnimationPlayer.
"""
extends Node2D
export(NodePath) var anim_player_path: NodePath
export(String) var track_name: String
onready var anim_player: AnimationPlayer = get_node(anim_player_path) as AnimationPlayer
onready var tree: SceneTree = get_tree()
onready var viewport: Viewport = get_viewport()
var animation: Animation
var animation_pos_seconds: float = 0
var animation_length: float
var img_idx: int = 0
const frame_rate = 1.0 / 60 # Capture at 60 frames per second.
func _ready():
anim_player.stop(true) # Reset animation player
anim_player.autoplay = ""
animation = anim_player.get_animation(track_name)
animation.loop = false
anim_player.current_animation = track_name
animation_length = anim_player.current_animation_length
# Trigger next save at next idle
tree.connect("idle_frame", self, "export_anim_frames", [], CONNECT_ONESHOT)
func export_anim_frames():
# Save current image before advancing
save_img()
# Advance to next frame
animation_pos_seconds = clamp(
animation_pos_seconds + frame_rate,
0,
animation_length
)
anim_player.seek(animation_pos_seconds) # Seek uses absolute timeline pos in seconds
# Check if this is last frame
if animation_pos_seconds == animation_length:
# Save the last frame before we quit
save_img() # Save image
print("finished.")
else:
# Wait for idle then export next frame
tree.call_deferred("connect", "idle_frame", self, "export_anim_frames", [], CONNECT_ONESHOT)
func save_img():
var img = viewport.get_texture().get_data()
img.flip_y()
# Saves to Godot User data folder
img.save_png("%s/img_%s.png" % [OS.get_user_data_dir(), img_idx])
img_idx += 1
@belzecue
Copy link
Author

belzecue commented Jan 1, 2024

FYI: uses AnimationPlayer 'seek' method because it needs to process the frames asyncronously. This means animation events are skipped, if you are using them. The 'advance' method uses delta time to move forward, whereas 'seek' uses the absolute timeline position.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment