McRogueFace/tests/unit/test_animation_debug.py

236 lines
6.9 KiB
Python

#!/usr/bin/env python3
"""
Animation Debug Tool
====================
Helps diagnose animation timing issues.
"""
import mcrfpy
import sys
# Track all active animations
active_animations = {}
animation_log = []
class AnimationTracker:
"""Tracks animation lifecycle for debugging"""
def __init__(self, name, target, property_name, target_value, duration):
self.name = name
self.target = target
self.property_name = property_name
self.target_value = target_value
self.duration = duration
self.start_time = None
self.animation = None
def start(self):
"""Start the animation with tracking"""
# Log the start
log_entry = f"START: {self.name} - {self.property_name} to {self.target_value} over {self.duration}s"
animation_log.append(log_entry)
print(log_entry)
# Create and start animation
self.animation = mcrfpy.Animation(self.property_name, self.target_value, self.duration, "linear")
self.animation.start(self.target)
# Track it
active_animations[self.name] = self
# Set timer to check completion
check_interval = 100 # ms
mcrfpy.setTimer(f"check_{self.name}", self._check_complete, check_interval)
def _check_complete(self, dt):
"""Check if animation is complete"""
if self.animation and hasattr(self.animation, 'is_complete') and self.animation.is_complete:
# Log completion
log_entry = f"COMPLETE: {self.name}"
animation_log.append(log_entry)
print(log_entry)
# Remove from active
if self.name in active_animations:
del active_animations[self.name]
# Stop checking
mcrfpy.delTimer(f"check_{self.name}")
# Create test scene
mcrfpy.createScene("anim_debug")
# Simple grid
grid = mcrfpy.Grid(grid_x=15, grid_y=10)
for y in range(10):
for x in range(15):
cell = grid.at(x, y)
cell.walkable = True
cell.color = mcrfpy.Color(100, 100, 120)
# Test entity
entity = mcrfpy.Entity(5, 5, grid=grid)
entity.sprite_index = 64
# UI
ui = mcrfpy.sceneUI("anim_debug")
ui.append(grid)
grid.position = (100, 150)
grid.size = (450, 300)
title = mcrfpy.Caption("Animation Debug Tool", 250, 20)
title.fill_color = mcrfpy.Color(255, 255, 255)
ui.append(title)
status = mcrfpy.Caption("Press keys to test animations", 100, 50)
status.fill_color = mcrfpy.Color(200, 200, 200)
ui.append(status)
pos_display = mcrfpy.Caption("", 100, 70)
pos_display.fill_color = mcrfpy.Color(255, 255, 100)
ui.append(pos_display)
active_display = mcrfpy.Caption("Active animations: 0", 100, 90)
active_display.fill_color = mcrfpy.Color(100, 255, 255)
ui.append(active_display)
# Test scenarios
def test_simultaneous():
"""Test multiple animations at once (causes issues)"""
print("\n=== TEST: Simultaneous Animations ===")
status.text = "Testing simultaneous X and Y animations"
# Start both at once
anim1 = AnimationTracker("sim_x", entity, "x", 10.0, 1.0)
anim2 = AnimationTracker("sim_y", entity, "y", 8.0, 1.5)
anim1.start()
anim2.start()
def test_rapid_fire():
"""Test starting new animation before previous completes"""
print("\n=== TEST: Rapid Fire Animations ===")
status.text = "Testing rapid fire animations (overlapping)"
# Start first animation
anim1 = AnimationTracker("rapid_1", entity, "x", 8.0, 2.0)
anim1.start()
# Start another after 500ms (before first completes)
def start_second(dt):
anim2 = AnimationTracker("rapid_2", entity, "x", 12.0, 1.0)
anim2.start()
mcrfpy.delTimer("rapid_timer")
mcrfpy.setTimer("rapid_timer", start_second, 500)
def test_sequential():
"""Test proper sequential animations"""
print("\n=== TEST: Sequential Animations ===")
status.text = "Testing proper sequential animations"
sequence = [
("seq_1", "x", 8.0, 0.5),
("seq_2", "y", 7.0, 0.5),
("seq_3", "x", 6.0, 0.5),
("seq_4", "y", 5.0, 0.5),
]
def run_sequence(index=0):
if index >= len(sequence):
print("Sequence complete!")
return
name, prop, value, duration = sequence[index]
anim = AnimationTracker(name, entity, prop, value, duration)
anim.start()
# Schedule next
delay = int(duration * 1000) + 100 # Add buffer
mcrfpy.setTimer(f"seq_timer_{index}", lambda dt: run_sequence(index + 1), delay)
run_sequence()
def test_conflicting():
"""Test conflicting animations on same property"""
print("\n=== TEST: Conflicting Animations ===")
status.text = "Testing conflicting animations (same property)"
# Start animation to x=10
anim1 = AnimationTracker("conflict_1", entity, "x", 10.0, 2.0)
anim1.start()
# After 1 second, start conflicting animation to x=2
def start_conflict(dt):
print("Starting conflicting animation!")
anim2 = AnimationTracker("conflict_2", entity, "x", 2.0, 1.0)
anim2.start()
mcrfpy.delTimer("conflict_timer")
mcrfpy.setTimer("conflict_timer", start_conflict, 1000)
# Update display
def update_display(dt):
pos_display.text = f"Entity position: ({entity.x:.2f}, {entity.y:.2f})"
active_display.text = f"Active animations: {len(active_animations)}"
# Show active animation names
if active_animations:
names = ", ".join(active_animations.keys())
active_display.text += f" [{names}]"
# Show log
def show_log():
print("\n=== ANIMATION LOG ===")
for entry in animation_log[-10:]: # Last 10 entries
print(entry)
print("===================")
# Input handler
def handle_input(key, state):
if state != "start":
return
key = key.lower()
if key == "q":
sys.exit(0)
elif key == "num1":
test_simultaneous()
elif key == "num2":
test_rapid_fire()
elif key == "num3":
test_sequential()
elif key == "num4":
test_conflicting()
elif key == "l":
show_log()
elif key == "r":
entity.x = 5
entity.y = 5
animation_log.clear()
active_animations.clear()
print("Reset entity and cleared log")
# Setup
mcrfpy.setScene("anim_debug")
mcrfpy.keypressScene(handle_input)
mcrfpy.setTimer("update", update_display, 100)
print("Animation Debug Tool")
print("====================")
print("This tool helps diagnose animation timing issues")
print()
print("Tests:")
print(" 1 - Simultaneous X/Y (may cause issues)")
print(" 2 - Rapid fire (overlapping animations)")
print(" 3 - Sequential (proper chaining)")
print(" 4 - Conflicting (same property)")
print()
print("Other keys:")
print(" L - Show animation log")
print(" R - Reset")
print(" Q - Quit")
print()
print("Watch the console for animation lifecycle events")