Files
PythonInUnreal/Content/Python/ue_workshop/root_motion.py
T
2026-10-31 23:11:57 +01:00

59 lines
2.8 KiB
Python

"""Transfer horizontal root motion to Sequencer actor keys without changing source clips."""
import math
import hashlib
import unreal
from .paths import GENERATED
def prepare(clip, child, scene_name):
component = child.skeletal_mesh_component
bone = component.get_bone_name(0)
count = int(clip.get_editor_property("number_of_sampled_keys"))
duration = clip.get_play_length()
if count < 2 or duration <= 0:
raise ValueError("keep_end_position wymaga animacji z co najmniej dwiema probkami.")
poses = [unreal.AnimationLibrary.get_bone_pose_for_time(
clip, bone, duration * index / (count - 1), False) for index in range(count)]
first = poses[0].translation
# Extract only horizontal travel. Keep vertical motion (jumps/bobbing),
# every root rotation and scale, and all other bone tracks unchanged.
positions = [unreal.Vector(first.x, first.y, pose.translation.z) for pose in poses]
folder = f"{GENERATED}/{scene_name}/RootMotion"
identity = hashlib.sha1(clip.get_path_name().encode("utf-8")).hexdigest()[:8]
name = clip.get_name() + "_" + identity + "_InPlace"
path = folder + "/" + name
if unreal.EditorAssetLibrary.does_asset_exist(path):
# This path contains only our generated derivative for this scene.
if not unreal.EditorAssetLibrary.delete_asset(path):
raise RuntimeError("Nie mozna odswiezyc wygenerowanego klipu " + path)
copy = unreal.AssetToolsHelpers.get_asset_tools().duplicate_asset(name, folder, clip)
if copy is None:
raise RuntimeError("Nie mozna utworzyc kopii animacji dla keep_end_position.")
controller = copy.get_editor_property("controller")
if not controller.set_bone_track_keys(bone, positions, [pose.rotation for pose in poses],
[pose.scale3d for pose in poses], should_transact=False):
raise RuntimeError("Nie mozna przeniesc ruchu kosci glownej do Transform.")
unreal.EditorAssetLibrary.save_loaded_asset(copy)
return copy, poses, duration
def displacement(poses, duration, elapsed, reverse=False):
"""Interpolate horizontal root travel, accumulating complete playback loops."""
elapsed = max(0.0, elapsed)
loops = math.floor(elapsed / duration)
time = elapsed - loops * duration
if reverse:
time = duration - time
index = min(time / duration * (len(poses) - 1), len(poses) - 1)
lower = int(index)
upper = min(lower + 1, len(poses) - 1)
alpha = index - lower
a, b = poses[lower].translation, poses[upper].translation
start, end = poses[0].translation, poses[-1].translation
if reverse:
start, end = end, start
return unreal.Vector(a.x + (b.x - a.x) * alpha - start.x + loops * (end.x - start.x),
a.y + (b.y - a.y) * alpha - start.y + loops * (end.y - start.y), 0)