"""Horizontal root travel, interpolation and accumulated playback loops.""" import importlib.util from pathlib import Path import sys from types import SimpleNamespace import unittest from unittest.mock import MagicMock, patch class Vector: def __init__(self, x, y, z): self.x, self.y, self.z = x, y, z class RootMotionTests(unittest.TestCase): def setUp(self): unreal = MagicMock() unreal.Vector = Vector path = Path(__file__).resolve().parents[1] / "Content/Python/ue_workshop/root_motion.py" spec = importlib.util.spec_from_file_location("ue_workshop._root_motion_test", path) self.module = importlib.util.module_from_spec(spec) with patch.dict(sys.modules, {"unreal": unreal}): spec.loader.exec_module(self.module) self.poses = [SimpleNamespace(translation=Vector(0, 0, 100)), SimpleNamespace(translation=Vector(0, 50, 110)), SimpleNamespace(translation=Vector(0, 100, 100))] def test_horizontal_interpolation_keeps_vertical_motion_in_clip(self): value = self.module.displacement(self.poses, 2.0, 0.5) self.assertEqual((value.x, value.y, value.z), (0, 25, 0)) def test_end_and_repeated_playback_accumulate_instead_of_reset(self): self.assertEqual(self.module.displacement(self.poses, 2.0, 2).y, 100) self.assertEqual(self.module.displacement(self.poses, 2.0, 5).y, 250) def test_before_start_has_no_displacement(self): self.assertEqual(self.module.displacement(self.poses, 2.0, -1).y, 0) def test_reverse_accumulates_backwards_without_initial_jump(self): self.assertEqual(self.module.displacement(self.poses, 2.0, 0, reverse=True).y, 0) self.assertEqual(self.module.displacement(self.poses, 2.0, 1, reverse=True).y, -50) self.assertEqual(self.module.displacement(self.poses, 2.0, 5, reverse=True).y, -250) def test_reverse_samples_the_end_of_a_nonuniform_trajectory(self): self.poses[1].translation.y = 20 self.assertEqual(self.module.displacement(self.poses, 2.0, 0.5, reverse=True).y, -40)