Sprite frames by code
An eight frame walk cycle painted into Image buffers at startup and fed to an AnimatedSprite2D. The strip at the bottom is the SpriteFrames itself.
The editor above is running this project. Change a line and it reloads.
Godot classes used
AnimatedSprite2DSpriteFramesImageTexture
The code
scripts/main.gd
extends Node
const FRAME_SIZE: int = 24
const FRAME_COUNT: int = 8
const FPS: float = 10.0
const STRIP_Y: float = 570.0
const STRIP_STEP: float = 96.0
const SKIN: Color = Color('#8da5f3')
const LEG: Color = Color('#5f74b8')
const EYE: Color = Color('#12141a')
var sprite: AnimatedSprite2D
var highlight: ColorRect
func _ready() -> void:
_add_background()
_add_label('every frame is an Image painted pixel by pixel at startup', Vector2(40, 40))
var frames: SpriteFrames = SpriteFrames.new()
frames.set_animation_speed('default', FPS)
frames.set_animation_loop('default', true)
for i in FRAME_COUNT:
frames.add_frame('default', _make_frame(i))
sprite = AnimatedSprite2D.new()
sprite.sprite_frames = frames
sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
sprite.scale = Vector2(11, 11)
sprite.position = Vector2(640, 300)
add_child(sprite)
sprite.play()
_add_strip(frames)
print('the strip below is the SpriteFrames, the highlight is the frame on screen')
func _process(_delta: float) -> void:
highlight.position.x = _strip_x(sprite.frame) - STRIP_STEP * 0.5
func _make_frame(index: int) -> ImageTexture:
var image: Image = Image.create(FRAME_SIZE, FRAME_SIZE, false, Image.FORMAT_RGBA8)
image.fill(Color(0, 0, 0, 0))
var phase: float = TAU * index / FRAME_COUNT
var bob: int = int(round(sin(phase) * 1.5))
var swing: int = int(round(sin(phase) * 2.0))
var body: Vector2 = Vector2(12, 11 + bob)
_fill_rect(image, Vector2i(8 + swing, 17), Vector2i(3, 6), LEG)
_fill_rect(image, Vector2i(14 - swing, 17), Vector2i(3, 6), LEG)
for y in FRAME_SIZE:
for x in FRAME_SIZE:
if Vector2(x, y).distance_to(body) <= 7.2:
image.set_pixel(x, y, SKIN)
_fill_rect(image, Vector2i(9, 9 + bob), Vector2i(2, 2), EYE)
_fill_rect(image, Vector2i(14, 9 + bob), Vector2i(2, 2), EYE)
return ImageTexture.create_from_image(image)
func _fill_rect(image: Image, at: Vector2i, size: Vector2i, color: Color) -> void:
for y in range(at.y, min(at.y + size.y, FRAME_SIZE)):
for x in range(at.x, min(at.x + size.x, FRAME_SIZE)):
image.set_pixel(x, y, color)
func _add_strip(frames: SpriteFrames) -> void:
highlight = ColorRect.new()
highlight.color = Color('#2a2f3d')
highlight.size = Vector2(STRIP_STEP, STRIP_STEP)
highlight.position = Vector2(_strip_x(0) - STRIP_STEP * 0.5, STRIP_Y - STRIP_STEP * 0.5)
add_child(highlight)
for i in FRAME_COUNT:
var thumb: Sprite2D = Sprite2D.new()
thumb.texture = frames.get_frame_texture('default', i)
thumb.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
thumb.scale = Vector2(3, 3)
thumb.position = Vector2(_strip_x(i), STRIP_Y)
add_child(thumb)
func _strip_x(index: int) -> float:
return 640.0 + (index - (FRAME_COUNT - 1) * 0.5) * STRIP_STEP
func _add_background() -> void:
var background: ColorRect = ColorRect.new()
background.color = Color('#12141a')
add_child(background)
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
func _add_label(text: String, at: Vector2) -> void:
var label: Label = Label.new()
label.text = text
label.position = at
label.add_theme_color_override('font_color', Color('#999999'))
add_child(label)