advanced example

This commit is contained in:
Vysyr committed 2026-10-31 20:16:32 +01:00
1 parent 8c0a639fba
commit 6fe57915e8
99 files changed
+1329 -56

No files matched your search

+79
View File
@@ -1,4 +1,6 @@
"""Frame-based Sequencer helpers. Ranges are [start, end), rotations roll/pitch/yaw."""
import math
import unreal
from .paths import GENERATED, slug
@@ -26,6 +28,7 @@ class Sequence:
self.asset.set_playback_end(frames)
self._bindings = {}
self._transforms = {}
self._animations = {}
def bind(self, actor):
key = actor.get_path_name()
@@ -45,6 +48,82 @@ class Sequence:
binding_id.set_editor_property("guid", binding.get_id())
section.set_camera_binding_id(binding_id)
def _animation_blend(self, section, start, end, fade_in, fade_out):
# The skeletal animation section exposes its Weight channel to Python.
# add_key uses display frames and converts them to the sequence tick rate.
weight = section.get_all_channels()[0]
for key in weight.get_keys():
weight.remove_key(key)
weight.set_default(1.0)
if fade_in or fade_out:
keys = {start: 0.0 if fade_in else 1.0}
if fade_in:
keys[start + fade_in] = 1.0
if fade_out:
keys[end - fade_out] = 1.0
keys[end] = 0.0
for frame, value in sorted(keys.items()):
weight.add_key(unreal.FrameNumber(frame), value,
interpolation=unreal.MovieSceneKeyInterpolation.LINEAR)
def animation(self, actor, clip=None, start=0, end=None, speed=1.0):
"""Play/loop an imported clip over [start, end) in sequence frames.
clip is an alias from scene.skeletal_model(animations=...), an AnimSequence,
or None for the only embedded animation. Transform keys still move the parent.
Overlapping clips automatically crossfade over their entire shared range.
Add clips in timeline order; at most two clips can overlap at a time.
"""
end = self.frames if end is None else end
if type(start) is not int or type(end) is not int or not 0 <= start < end <= self.frames:
raise ValueError(f"Zakres animacji: 0 <= start < end <= {self.frames}.")
if not isinstance(speed, (int, float)) or not math.isfinite(speed) or speed <= 0:
raise ValueError("Predkosc animacji musi byc dodatnia.")
entry = self.scene._skeletal_models.get(actor.get_path_name())
if entry is None:
raise ValueError("Animacja wymaga aktora utworzonego przez scene.skeletal_model().")
child, imported = entry
if clip is None:
if len(imported.animations) != 1:
raise ValueError("Wybierz animacje: " + ", ".join(imported.animations))
clip = next(iter(imported.animations.values()))
elif isinstance(clip, str):
if clip not in imported.animations:
raise ValueError("Nieznana animacja. Dostepne: " + ", ".join(imported.animations))
clip = imported.animations[clip]
if not isinstance(clip, unreal.AnimSequence):
raise TypeError("clip musi byc nazwa zaimportowanej animacji lub AnimSequence.")
if clip.get_editor_property("skeleton") != imported.mesh.get_editor_property("skeleton"):
raise ValueError("Animacja i model musza uzywac tego samego szkieletu.")
key = child.get_path_name()
sections = self._animations.setdefault(key, [])
overlaps = [old for old in sections if start < old[1] and old[0] < end]
previous = None
blend_frames = 0
if overlaps:
if (len(overlaps) != 1 or not overlaps[0][0] < start or
overlaps[0][1] >= end or
overlaps[0][0] + overlaps[0][4] > start):
raise ValueError("Dodawaj klipy w kolejnosci odtwarzania. Blend wymaga czesciowego "
"nakladania dwoch klipow, bez trzech jednoczesnych animacji.")
previous = overlaps[0]
blend_frames = previous[1] - start
track = sections[0][2] if sections else self.bind(child).add_track(unreal.MovieSceneSkeletalAnimationTrack)
section = track.add_section()
section.set_range(start, end)
params = section.get_editor_property("params")
params.set_editor_property("animation", clip)
rate = unreal.MovieSceneTimeWarpVariant()
rate.set_fixed_play_rate(float(speed))
params.set_editor_property("play_rate", rate)
params.set_editor_property("force_custom_mode", True)
section.set_editor_property("params", params)
self._animation_blend(section, start, end, blend_frames, 0)
if previous:
self._animation_blend(previous[3], previous[0], previous[1], previous[4], blend_frames)
sections.append((start, end, track, section, blend_frames))
return section
def transform_key(self, actor, frame, location=None, rotation=None, scale=None, interpolation="linear"):
if not isinstance(frame, int) or not 0 <= frame < self.frames:
raise ValueError(f"Klatka musi nalezec do 0..{self.frames - 1}.")