3 Animation System
John McCardle edited this page 2026-02-07 22:15:31 +00:00

Animation System

The Animation System provides property-based animations with 30+ easing functions for smooth transitions on all UI elements.

Quick Reference

Related Issues:

  • #120 - Animation Property Locking (Tier 1 - Active)
  • #119 - Animation Completion Callbacks (Closed - Implemented)
  • #229 - Animation callbacks pass (target, property, value)

Key Files:

  • src/AnimationManager.h / src/AnimationManager.cpp - Animation execution engine
  • src/Animation.h - Base Animation class
  • src/UIDrawable.h - Animatable properties defined here

The .animate() Method

The primary way to create animations is the .animate() method available on all UI elements:

import mcrfpy

frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150))

# animate(property, target_value, duration_seconds, easing)
frame.animate("x", 500.0, 2.0, mcrfpy.Easing.EASE_IN_OUT)
frame.animate("opacity", 0.5, 1.0, mcrfpy.Easing.EASE_OUT_QUAD)

Parameters:

  • property (str): Name of the property to animate
  • target_value: End value (float, int, or Color tuple)
  • duration (float): Duration in seconds
  • easing (Easing): Easing function from mcrfpy.Easing enum
  • callback (optional): Function called on completion

Animatable Properties by Type

Frame

Property Type Notes
x, y float Position
w, h float Size
outline float Outline thickness
opacity float 0.0 to 1.0
fill_color Color Animate as (r, g, b, a) tuple
outline_color Color Animate as (r, g, b, a) tuple

Caption

Property Type Notes
x, y float Position
opacity float 0.0 to 1.0
outline float Text outline thickness
fill_color Color Text fill color
outline_color Color Text outline color

Sprite

Property Type Notes
x, y float Position
scale float Uniform scale factor
sprite_index int Sprite sheet index (truncated to int)
opacity float 0.0 to 1.0

Grid

Property Type Notes
x, y float Position on screen
w, h float Viewport size
center_x, center_y float Camera pan position
zoom float Camera zoom level

Entity (must be attached to a Grid)

Property Type Notes
x, y float Alias for draw position
draw_x, draw_y float Visual position in tile coordinates
sprite_index int Sprite sheet index
sprite_scale float Entity sprite scale

Easing Functions (mcrfpy.Easing)

30+ easing functions are available:

Basic:

  • Easing.LINEAR
  • Easing.EASE_IN, Easing.EASE_OUT, Easing.EASE_IN_OUT

Families (each has _IN, _OUT, _IN_OUT variants):

  • QUAD - Quadratic
  • CUBIC - Cubic
  • QUART - Quartic
  • SINE - Sinusoidal
  • EXPO - Exponential
  • CIRC - Circular
  • ELASTIC - Elastic spring
  • BACK - Overshoot
  • BOUNCE - Bouncing

Example: Easing.EASE_IN_OUT_CUBIC, Easing.EASE_OUT_BOUNCE, Easing.EASE_IN_ELASTIC


Completion Callbacks

Animation callbacks receive (target, property, final_value):

def on_complete(target, prop, value):
    print(f"{type(target).__name__}.{prop} reached {value}")

frame.animate("x", 500.0, 2.0, mcrfpy.Easing.EASE_IN_OUT, callback=on_complete)

Callback arguments:

  • target: The animated object (Frame, Sprite, Entity, etc.)
  • prop (str): Property name that was animated (e.g., "x", "opacity")
  • value: Final value (float, int, or (r, g, b, a) tuple for colors)

Chaining Animations

Use callbacks to create animation sequences:

def step2(target, prop, value):
    target.animate("y", 300.0, 1.0, mcrfpy.Easing.EASE_OUT_BOUNCE)

def step1(target, prop, value):
    target.animate("x", 500.0, 1.0, mcrfpy.Easing.EASE_IN_OUT, callback=step2)

frame.animate("opacity", 1.0, 0.5, mcrfpy.Easing.EASE_IN, callback=step1)

Note: It is safe to start new animations from within callbacks, including animations on the same target and property.


Execution Model

Pure C++ Execution:

  • Animations run in the C++ update loop - no Python callbacks per frame
  • High performance: thousands of concurrent animations
  • Frame-independent: adjusts for variable frame times

Lifecycle:

  1. Created via .animate() call
  2. AnimationManager updates each frame
  3. Auto-destroyed on completion or target destruction
  4. Weak pointer tracking prevents use-after-free

Conflicting Animations: Multiple animations on the same property will conflict. The most recently created animation takes precedence. Property locking (#120) is planned to prevent this.


Color Animations

Animate colors by targeting fill_color or outline_color:

# Fade to red fill
frame.animate("fill_color", (255, 0, 0, 255), 1.0, mcrfpy.Easing.EASE_IN)

# Animate outline to transparent
frame.animate("outline_color", (255, 255, 255, 0), 0.5, mcrfpy.Easing.LINEAR)

Color animations interpolate each channel (R, G, B, A) independently.


Common Patterns

Smooth Entity Movement

def move_entity(entity, target_x, target_y, speed=0.3):
    entity.animate("x", float(target_x), speed, mcrfpy.Easing.EASE_OUT_QUAD)
    entity.animate("y", float(target_y), speed, mcrfpy.Easing.EASE_OUT_QUAD)

Fade In/Out

# Fade in
frame.opacity = 0.0
frame.animate("opacity", 1.0, 0.5, mcrfpy.Easing.EASE_IN)

# Fade out and remove
def remove_on_fade(target, prop, value):
    # Remove from parent scene
    pass  # implement removal logic

frame.animate("opacity", 0.0, 0.5, mcrfpy.Easing.EASE_OUT, callback=remove_on_fade)

Pulse/Breathing Effect

def pulse(target, prop, value):
    if value > 0.8:
        target.animate("opacity", 0.3, 1.0, mcrfpy.Easing.EASE_IN_OUT_SINE, callback=pulse)
    else:
        target.animate("opacity", 1.0, 1.0, mcrfpy.Easing.EASE_IN_OUT_SINE, callback=pulse)

frame.animate("opacity", 0.3, 1.0, mcrfpy.Easing.EASE_IN_OUT_SINE, callback=pulse)

Camera Pan

grid.animate("center_x", target_x * 16.0, 0.5, mcrfpy.Easing.EASE_IN_OUT)
grid.animate("center_y", target_y * 16.0, 0.5, mcrfpy.Easing.EASE_IN_OUT)

Animation Object (Advanced)

The mcrfpy.Animation class can be used directly for more control, though .animate() is preferred:

anim = mcrfpy.Animation("x", 500.0, 2.0, mcrfpy.Easing.LINEAR, callback=my_callback)
anim.start(frame)

This is functionally equivalent to frame.animate("x", 500.0, 2.0, mcrfpy.Easing.LINEAR, callback=my_callback).



Last updated: 2026-02-07