Update Animation System wiki with .animate() method, Easing enum, verified properties, callback signature
parent
222ee83009
commit
dd2a24f623
1 changed files with 248 additions and 159 deletions
|
|
@ -1,159 +1,248 @@
|
|||
# Animation System
|
||||
|
||||
The Animation System provides property-based animations with 24+ easing functions for smooth transitions on all UI elements.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
**Related Issues:**
|
||||
- [#120](../issues/120) - Animation Property Locking (Tier 1 - Active)
|
||||
- [#119](../issues/119) - Animation Completion Callbacks (Closed - Implemented)
|
||||
- [#100](../issues/100) - Add Rotation Support
|
||||
|
||||
**Key Files:**
|
||||
- `src/AnimationManager.h` / `src/AnimationManager.cpp` - Animation execution engine
|
||||
- `src/Animation.h` - Base Animation class
|
||||
- `src/UIDrawable.h` - Animatable properties defined here
|
||||
|
||||
**API Reference:**
|
||||
- See [mcrfpy.animate()](../docs/api_reference_dynamic.html#animate) in generated API docs
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Property-Based Animation
|
||||
|
||||
Animations modify any numeric property on UI objects over time:
|
||||
|
||||
```python
|
||||
import mcrfpy
|
||||
|
||||
sprite = mcrfpy.Sprite("player.png", 100, 100)
|
||||
|
||||
# Animate position with easing
|
||||
mcrfpy.animate(sprite, "x", 300, 1000, "ease_in_out_cubic")
|
||||
|
||||
# Animate multiple properties
|
||||
mcrfpy.animate(sprite, "opacity", 0.5, 500, "ease_out_quad")
|
||||
mcrfpy.animate(sprite, "scale_x", 2.0, 800, "bounce_out")
|
||||
```
|
||||
|
||||
**Supported Properties:**
|
||||
- Position: `x`, `y`, `pos`
|
||||
- Size: `w`, `h`, `size`, `scale_x`, `scale_y`
|
||||
- Colors: `r`, `g`, `b`, `a`, `fill_color`, `outline_color`
|
||||
- Grid-specific: `zoom`, `left_edge`, `top_edge`
|
||||
- Caption-specific: `font_size`
|
||||
|
||||
**Implementation:** See `src/AnimationManager.cpp::createAnimation()`
|
||||
|
||||
### Easing Functions
|
||||
|
||||
24+ easing functions available:
|
||||
|
||||
**Families:**
|
||||
- Linear
|
||||
- Quad, Cubic, Quart, Quint
|
||||
- Sine, Expo, Circ
|
||||
- Elastic, Back, Bounce
|
||||
|
||||
**Variants:** `ease_in_*`, `ease_out_*`, `ease_in_out_*`
|
||||
|
||||
**Implementation:** All easing functions in `src/Animation.cpp`
|
||||
|
||||
### Execution Model
|
||||
|
||||
**Pure C++ Execution:**
|
||||
- Animations run in C++ update loop - no Python callbacks per frame
|
||||
- High performance: thousands of concurrent animations possible
|
||||
- Frame-independent: adjusts for variable frame times
|
||||
|
||||
**Lifecycle:**
|
||||
1. Created via `mcrfpy.animate()`
|
||||
2. AnimationManager updates each frame
|
||||
3. Auto-destroyed on completion or target destruction
|
||||
4. Weak pointer tracking prevents use-after-free
|
||||
|
||||
**See:** `src/AnimationManager.cpp::update()` for frame update logic
|
||||
|
||||
## Current Issues & Limitations
|
||||
|
||||
**Known Issues:**
|
||||
- API inconsistency: position vs frame animation differs (see [FINAL_RECOMMENDATIONS.md](../FINAL_RECOMMENDATIONS.md))
|
||||
- Property locking: Multiple animations can conflict on same property ([#120](../issues/120))
|
||||
- Rotation not yet supported ([#100](../issues/100))
|
||||
|
||||
**Recommendations from Strategic Review:**
|
||||
Per FINAL_RECOMMENDATIONS.md, animation bugs contributed to tutorial burnout. Current system is feature-complete but needs:
|
||||
1. API consistency audit
|
||||
2. Bug fixes for known issues
|
||||
3. Comprehensive testing
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Basic Animation
|
||||
|
||||
```python
|
||||
# Linear movement
|
||||
mcrfpy.animate(entity, "x", 500, 2000, "linear")
|
||||
|
||||
# Smooth bounce
|
||||
mcrfpy.animate(frame, "y", 300, 1000, "ease_out_bounce")
|
||||
```
|
||||
|
||||
### Color Fades
|
||||
|
||||
```python
|
||||
# Fade to red
|
||||
mcrfpy.animate(caption, "r", 255, 500, "ease_in_quad")
|
||||
|
||||
# Fade out
|
||||
mcrfpy.animate(sprite, "a", 0, 1000, "ease_out_cubic")
|
||||
```
|
||||
|
||||
### Delta Animations
|
||||
|
||||
```python
|
||||
# Move relative to current position
|
||||
current_x = entity.x
|
||||
mcrfpy.animate(entity, "x", current_x + 100, 500, "ease_in_out_quad")
|
||||
```
|
||||
|
||||
### Completion Callbacks
|
||||
|
||||
```python
|
||||
def on_complete(runtime_ms):
|
||||
print(f"Animation completed after {runtime_ms}ms")
|
||||
# Trigger next action
|
||||
|
||||
mcrfpy.animate(sprite, "x", 400, 1000, "linear", callback=on_complete)
|
||||
```
|
||||
|
||||
**Implementation:** [#119](../issues/119) - Callbacks fire when animation completes
|
||||
|
||||
## Related Systems
|
||||
|
||||
- [[UI-Component-Hierarchy]] - All UIDrawable objects are animatable
|
||||
- [[Grid-System]] - Grid viewport animations (zoom, pan)
|
||||
- [[Performance-and-Profiling]] - Animation time tracked separately
|
||||
|
||||
## Design Decisions
|
||||
|
||||
**Why Property-Based?**
|
||||
- Flexible: Any numeric property animatable
|
||||
- Type-safe: Properties validated at C++ level
|
||||
- Performant: No Python overhead during animation
|
||||
|
||||
**Why Weak Pointers?**
|
||||
- Prevents crashes when animated objects destroyed
|
||||
- Automatic cleanup on target death
|
||||
- See: `src/AnimationManager.cpp` RAII overhaul (commit e9e9cd2)
|
||||
|
||||
**Tradeoffs:**
|
||||
- More complex than tween libraries
|
||||
- Requires property exposure in Python bindings
|
||||
- API surface larger than simple position tweening
|
||||
|
||||
---
|
||||
|
||||
**Historical Notes:**
|
||||
- ANIMATION_FIX_IMPLEMENTATION.md documents segfault fix (race conditions resolved)
|
||||
- Major refactor July 2025: Weak pointer tracking eliminated use-after-free bugs
|
||||
# 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](../issues/120) - Animation Property Locking (Tier 1 - Active)
|
||||
- [#119](../issues/119) - Animation Completion Callbacks (Closed - Implemented)
|
||||
- [#229](../issues/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:
|
||||
|
||||
```python
|
||||
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)`:
|
||||
|
||||
```python
|
||||
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:
|
||||
|
||||
```python
|
||||
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](../issues/120)) is planned to prevent this.
|
||||
|
||||
---
|
||||
|
||||
## Color Animations
|
||||
|
||||
Animate colors by targeting `fill_color` or `outline_color`:
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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:
|
||||
|
||||
```python
|
||||
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)`.
|
||||
|
||||
---
|
||||
|
||||
## Related Systems
|
||||
|
||||
- [[UI-Component-Hierarchy]] - All UIDrawable objects are animatable
|
||||
- [[Grid-System]] - Grid viewport animations (zoom, pan)
|
||||
- [[Input-and-Events]] - Trigger animations from input callbacks
|
||||
- [[Performance-and-Profiling]] - Animation time tracked separately
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-02-07*
|
||||
Loading…
Add table
Add a link
Reference in a new issue