McRogueFace/tests/unit/test_animation_chaining.py

222 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""
Test Animation Chaining
=======================
Demonstrates proper animation chaining to avoid glitches.
"""
import mcrfpy
import sys
class PathAnimator:
"""Handles step-by-step path animation with proper chaining"""
def __init__(self, entity, path, step_duration=0.3, on_complete=None):
self.entity = entity
self.path = path
self.current_index = 0
self.step_duration = step_duration
self.on_complete = on_complete
self.animating = False
self.check_timer_name = f"path_check_{id(self)}"
def start(self):
"""Start animating along the path"""
if not self.path or self.animating:
return
self.current_index = 0
self.animating = True
self._animate_next_step()
def _animate_next_step(self):
"""Animate to the next position in the path"""
if self.current_index >= len(self.path):
# Path complete
self.animating = False
mcrfpy.delTimer(self.check_timer_name)
if self.on_complete:
self.on_complete()
return
# Get target position
target_x, target_y = self.path[self.current_index]
# Create animations
self.anim_x = mcrfpy.Animation("x", float(target_x), self.step_duration, "easeInOut")
self.anim_y = mcrfpy.Animation("y", float(target_y), self.step_duration, "easeInOut")
# Start animations
self.anim_x.start(self.entity)
self.anim_y.start(self.entity)
# Update visibility if entity has this method
if hasattr(self.entity, 'update_visibility'):
self.entity.update_visibility()
# Set timer to check completion
mcrfpy.setTimer(self.check_timer_name, self._check_completion, 50)
def _check_completion(self, dt):
"""Check if current animation is complete"""
if hasattr(self.anim_x, 'is_complete') and self.anim_x.is_complete:
# Move to next step
self.current_index += 1
mcrfpy.delTimer(self.check_timer_name)
self._animate_next_step()
# Create test scene
mcrfpy.createScene("chain_test")
# Create grid
grid = mcrfpy.Grid(grid_x=20, grid_y=15)
grid.fill_color = mcrfpy.Color(20, 20, 30)
# Simple map
for y in range(15):
for x in range(20):
cell = grid.at(x, y)
if x == 0 or x == 19 or y == 0 or y == 14:
cell.walkable = False
cell.transparent = False
cell.color = mcrfpy.Color(60, 40, 40)
else:
cell.walkable = True
cell.transparent = True
cell.color = mcrfpy.Color(100, 100, 120)
# Create entities
player = mcrfpy.Entity(2, 2, grid=grid)
player.sprite_index = 64 # @
enemy = mcrfpy.Entity(17, 12, grid=grid)
enemy.sprite_index = 69 # E
# UI setup
ui = mcrfpy.sceneUI("chain_test")
ui.append(grid)
grid.position = (100, 100)
grid.size = (600, 450)
title = mcrfpy.Caption("Animation Chaining Test", 300, 20)
title.fill_color = mcrfpy.Color(255, 255, 255)
ui.append(title)
status = mcrfpy.Caption("Press 1: Animate Player | 2: Animate Enemy | 3: Both | Q: Quit", 100, 50)
status.fill_color = mcrfpy.Color(200, 200, 200)
ui.append(status)
info = mcrfpy.Caption("Status: Ready", 100, 70)
info.fill_color = mcrfpy.Color(100, 255, 100)
ui.append(info)
# Path animators
player_animator = None
enemy_animator = None
def animate_player():
"""Animate player along a path"""
global player_animator
# Define path
path = [
(2, 2), (3, 2), (4, 2), (5, 2), (6, 2), # Right
(6, 3), (6, 4), (6, 5), (6, 6), # Down
(7, 6), (8, 6), (9, 6), (10, 6), # Right
(10, 7), (10, 8), (10, 9), # Down
]
def on_complete():
info.text = "Player animation complete!"
player_animator = PathAnimator(player, path, step_duration=0.2, on_complete=on_complete)
player_animator.start()
info.text = "Animating player..."
def animate_enemy():
"""Animate enemy along a path"""
global enemy_animator
# Define path
path = [
(17, 12), (16, 12), (15, 12), (14, 12), # Left
(14, 11), (14, 10), (14, 9), # Up
(13, 9), (12, 9), (11, 9), (10, 9), # Left
(10, 8), (10, 7), (10, 6), # Up
]
def on_complete():
info.text = "Enemy animation complete!"
enemy_animator = PathAnimator(enemy, path, step_duration=0.25, on_complete=on_complete)
enemy_animator.start()
info.text = "Animating enemy..."
def animate_both():
"""Animate both entities simultaneously"""
info.text = "Animating both entities..."
animate_player()
animate_enemy()
# Camera follow test
camera_follow = False
def update_camera(dt):
"""Update camera to follow player if enabled"""
if camera_follow and player_animator and player_animator.animating:
# Smooth camera follow
center_x = player.x * 30 # Assuming ~30 pixels per cell
center_y = player.y * 30
cam_anim = mcrfpy.Animation("center", (center_x, center_y), 0.25, "linear")
cam_anim.start(grid)
# Input handler
def handle_input(key, state):
global camera_follow
if state != "start":
return
key = key.lower()
if key == "q":
sys.exit(0)
elif key == "num1":
animate_player()
elif key == "num2":
animate_enemy()
elif key == "num3":
animate_both()
elif key == "c":
camera_follow = not camera_follow
info.text = f"Camera follow: {'ON' if camera_follow else 'OFF'}"
elif key == "r":
# Reset positions
player.x, player.y = 2, 2
enemy.x, enemy.y = 17, 12
info.text = "Positions reset"
# Setup
mcrfpy.setScene("chain_test")
mcrfpy.keypressScene(handle_input)
# Camera update timer
mcrfpy.setTimer("cam_update", update_camera, 100)
print("Animation Chaining Test")
print("=======================")
print("This test demonstrates proper animation chaining")
print("to avoid simultaneous position updates.")
print()
print("Controls:")
print(" 1 - Animate player step by step")
print(" 2 - Animate enemy step by step")
print(" 3 - Animate both (simultaneous)")
print(" C - Toggle camera follow")
print(" R - Reset positions")
print(" Q - Quit")
print()
print("Notice how each entity moves one tile at a time,")
print("waiting for each step to complete before the next.")