No keyframes or spritesheets involved.
Shaked Lotan, an Indie Game Developer and the creator behind Tyto, an atmospheric 2D platformer featuring hand-painted artwork, has recently offered a behind-the-scenes look at his upcoming game, demonstrating his unconventional approach to animating its characters.
Shaked explains that, having no formal animation training, he instead uses math and code in Godot to bring Tyto's characters to life. By applying sine functions with different offsets, multipliers, and delays, he adjusts the position, scale, and rotation of body parts – all without keyframes or spritesheets. To demonstrate the process, he shared a short demo, embedded above, along with the code he wrote to animate both crab's legs and the crab as a whole.
"[Coded animation] allows you simpler and smoother transitions between states," the developer comments. "You don't need to make unique frames for transitioning between walking and attacking and stagger, etc. The math does it for you!"
Legs:
@export var leg_offset := 0.0
@export_range(0.0, 1.0, 0.01) var rotation_amount: float
@export var original_base_rotation: float
@export var end_base_rotation: float
@export var original_mid_rotation: float
@export var end_mid_rotation: float
@export var original_tip_rotation: float
@export var end_tip_rotation: float
@export var is_back_leg = false
var time = 0
var time_mult = 0.1
func _process(delta: float) -> void:
var total_time = time*time_mult + deg_to_rad(leg_offset)
if is_back_leg:
rotation_amount = clamp(sin(total_time), -1.0, 0.5)
else:
rotation_amount = clamp(sin(total_time), -0.5, 1.0)
var x_amount = 0.15
scale.x = 1.0 + sin(total_time + PI/2)*x_amount - x_amount
%"Leg Base".rotation_degrees = lerp(original_base_rotation, end_base_rotation, rotation_amount)
%"Leg Mid".rotation_degrees = lerp(original_mid_rotation, end_mid_rotation, rotation_amount)
%"Leg Tip".rotation_degrees = lerp(original_tip_rotation, end_tip_rotation, rotation_amount)
The rest of the crab:
@export var speed_mult = 0.1
var time = 0
var original_body_pos: Vector2
var original_left_claw_position: Vector2
var original_right_claw_position: Vector2
var original_right_claw_angle: float
func _ready() -> void:
original_body_pos = %Body.position
original_left_claw_position = %"Left Claw".position
original_right_claw_position = %"Right Claw".position
original_right_claw_angle = %"Right Claw".rotation_degrees
func _physics_process(delta: float) -> void:
time += 1
set_legs()
set_body()
set_eyes()
set_claws()
func set_legs():
for leg: CrawlerLeg in %Legs.get_children():
leg.time = time
leg.time_mult = speed_mult
func set_body():
%Body.position = original_body_pos + Vector2.UP*sin(time*speed_mult + PI)*3.0
%Body.rotation_degrees = sin(time*speed_mult - PI/2)*1.2
func set_eyes():
%Eyerod1.rotation_degrees = sin(time*speed_mult)*2.0
%Eye1.rotation_degrees = sin(time*speed_mult - PI/2)*3.5
%Eyerod2.rotation_degrees = sin(time*speed_mult + 0.9)*2.0
%Eye2.rotation_degrees = sin(time*speed_mult - PI/2 + 0.9)*3.5
func set_claws():
%"Left Claw".position = original_left_claw_position + Vector2.UP*sin(time*speed_mult + PI/2)*3.0
%"Left Claw".rotation_degrees = sin(time*speed_mult - PI/2 + 0.9)*2.5
%"Left Bottom Claw".rotation_degrees = sin(time*speed_mult + PI/2)*2
%"Right Claw".position = original_right_claw_position + Vector2.UP*sin(time*speed_mult + PI/2 + 0.3)*2.0
%"Right Claw".rotation_degrees = original_right_claw_angle + sin(time*speed_mult + PI/2 + 0.3)*1.1
%"Right Bottom Claw".rotation_degrees = sin(time*speed_mult + PI/2 - 0.3)*1.1
Earlier, the developer also demonstrated an adorable owl, animated using the same technique:
You can wishlist Tyto on Steam and follow Shaked on Twitter for more dev-related insights. Don't forget to join our 80 Level Talent platform and our Discord server. Follow us on Instagram, Twitter, LinkedIn, Telegram, TikTok, and Threads for breakdowns, the latest news, awesome artworks, and more.