Implement Entity3D.animate(), closes #242
Replaced the NotImplementedError stub with a full animation implementation. Entity3D now supports animating: x, y, z, world_x, world_y, world_z, rotation, rot_y, scale, scale_x, scale_y, scale_z, sprite_index, visible. Added Entity3D as a third target type in the Animation system (alongside UIDrawable and UIEntity), with startEntity3D(), applyValue(Entity3D*), and proper callback support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9e2444da69
commit
ef05152ea0
3 changed files with 203 additions and 5 deletions
94
tests/unit/test_entity3d_animate.py
Normal file
94
tests/unit/test_entity3d_animate.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
"""Test Entity3D.animate() (issue #242)"""
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
errors = []
|
||||
|
||||
vp = mcrfpy.Viewport3D(pos=(0,0), size=(100,100))
|
||||
vp.set_grid_size(16, 16)
|
||||
|
||||
# Test 1: Basic x animation
|
||||
e = mcrfpy.Entity3D(pos=(0,0), scale=1.0)
|
||||
vp.entities.append(e)
|
||||
start_x = e.world_pos[0]
|
||||
anim = e.animate('x', 10.0, 1.0, 'linear')
|
||||
if anim is None:
|
||||
errors.append("animate() should return Animation object")
|
||||
for _ in range(5):
|
||||
mcrfpy.step(0.25)
|
||||
if abs(e.world_pos[0] - 10.0) > 0.5:
|
||||
errors.append(f"x animation: expected ~10.0, got {e.world_pos[0]}")
|
||||
|
||||
# Test 2: Scale animation
|
||||
e2 = mcrfpy.Entity3D(pos=(0,0), scale=1.0)
|
||||
vp.entities.append(e2)
|
||||
e2.animate('scale', 5.0, 0.5, 'linear')
|
||||
for _ in range(3):
|
||||
mcrfpy.step(0.25)
|
||||
if abs(e2.scale - 5.0) > 0.5:
|
||||
errors.append(f"scale animation: expected ~5.0, got {e2.scale}")
|
||||
|
||||
# Test 3: Rotation animation
|
||||
e3 = mcrfpy.Entity3D(pos=(0,0), scale=1.0)
|
||||
vp.entities.append(e3)
|
||||
e3.animate('rotation', 90.0, 0.5, 'easeInOut')
|
||||
for _ in range(3):
|
||||
mcrfpy.step(0.25)
|
||||
if abs(e3.rotation - 90.0) > 0.5:
|
||||
errors.append(f"rotation animation: expected ~90.0, got {e3.rotation}")
|
||||
|
||||
# Test 4: Delta animation
|
||||
e4 = mcrfpy.Entity3D(pos=(3,3), scale=1.0)
|
||||
vp.entities.append(e4)
|
||||
start_x = e4.world_pos[0]
|
||||
e4.animate('x', 2.0, 0.5, 'linear', delta=True)
|
||||
for _ in range(3):
|
||||
mcrfpy.step(0.25)
|
||||
expected = start_x + 2.0
|
||||
if abs(e4.world_pos[0] - expected) > 0.5:
|
||||
errors.append(f"delta animation: expected ~{expected}, got {e4.world_pos[0]}")
|
||||
|
||||
# Test 5: Invalid property raises ValueError
|
||||
try:
|
||||
e.animate('nonexistent', 1.0, 1.0, 'linear')
|
||||
errors.append("Invalid property should raise ValueError")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Test 6: Invalid target type raises TypeError
|
||||
try:
|
||||
e.animate('x', "not_a_number", 1.0, 'linear')
|
||||
errors.append("String target should raise TypeError")
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
# Test 7: Callback
|
||||
callback_called = [False]
|
||||
def on_complete(target, prop, value):
|
||||
callback_called[0] = True
|
||||
|
||||
e5 = mcrfpy.Entity3D(pos=(0,0), scale=1.0)
|
||||
vp.entities.append(e5)
|
||||
e5.animate('x', 5.0, 0.25, 'linear', callback=on_complete)
|
||||
for _ in range(3):
|
||||
mcrfpy.step(0.15)
|
||||
if not callback_called[0]:
|
||||
errors.append("Animation callback was not called")
|
||||
|
||||
# Test 8: Easing enum
|
||||
e6 = mcrfpy.Entity3D(pos=(0,0), scale=1.0)
|
||||
vp.entities.append(e6)
|
||||
try:
|
||||
e6.animate('x', 5.0, 1.0, mcrfpy.Easing.EASE_IN_OUT)
|
||||
except Exception as ex:
|
||||
errors.append(f"Easing enum should work: {ex}")
|
||||
for _ in range(5):
|
||||
mcrfpy.step(0.25)
|
||||
|
||||
if errors:
|
||||
for err in errors:
|
||||
print(f"FAIL: {err}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("PASS: Entity3D.animate() (issue #242)", file=sys.stderr)
|
||||
sys.exit(0)
|
||||
Loading…
Add table
Add a link
Reference in a new issue