7.4 KiB
Nazwy i długości animacji w VS Code
W ue_workshop.animations są statycznie zapisane enumy FighterAnimations i
CreatureAnimations. Działają w zwykłym Pythonie, bez unreal. Po wpisaniu kropki
VS Code może podpowiedzieć wszystkie klipy. Opisy członków podają długość w sekundach.
Nazwy odpowiadają nazwom plików; nie ma skrótów typu attack.
from ue_workshop.animations import FighterAnimations, CreatureAnimations
draw = FighterAnimations.DRAW_A_GREAT_SWORD_1
attack = FighterAnimations.GREAT_SWORD_ATTACK
draw.key # "draw_a_great_sword_1"
draw.seconds # 0.5 sekundy
draw.frames(24) # 12 klatek przy 24 FPS
attack.seconds # około 1.2 sekundy
attack.frames(24) # 29 klatek przy 24 FPS, zaokrąglone w górę
attack.frames(24, speed=2)
# Bez end klip wybrany enumem gra raz, z automatycznie policzonym końcem.
shot.animation(character_fighter, draw, start=0)
next_start = draw.frames(shot.fps) - 6
shot.animation(character_fighter, attack, start=next_start)
W tym przykładzie przejście trwa 6 wspólnych klatek. Przy zmianie klipu albo FPS
nie trzeba pamiętać jego długości. speed jest uwzględniane zarówno przez frames,
jak i przez automatyczny koniec w shot.animation.
Podanie end nadal ustawia własny zakres i pozwala zapętlić klip:
shot.animation(character_fighter, FighterAnimations.GREAT_SWORD_IDLE, start=0, end=120)
Dotychczasowe nazwy tekstowe nadal działają. Dla tekstu lub bez wskazania klipu
pominięte end zachowuje dotychczasowe znaczenie: gra do końca sekwencji.
Koniec sekcji musi mieścić się w Sequence(..., frames=...).
Zachowanie pozycji po ataku
Jeśli klip przesuwa postać do przodu, włącz opcję na tym klipie:
shot.animation(character_fighter, FighterAnimations.GREAT_SWORD_SLASH_2,
start=57, keep_end_position=True)
shot.animation(character_fighter, FighterAnimations.GREAT_SWORD_SLASH_3, start=161)
animation() przenosi poziomy ruch kości głównej do zwykłych kluczy pozycji
aktora na ścieżce Transform. Ostatni klucz zachowuje osiągnięte przesunięcie,
więc następny atak zaczyna się w nowym miejscu. Klucze możesz edytować w Sequencerze.
Ruch pionowy i obroty kości pozostają w animacji.
Pozostawienie postaci na ziemi po śmierci
shot.animation(character_creature, CreatureAnimations.MUTANT_DYING,
start=150, keep_end_pose=True)
Enum bez end odtwarza klip raz. keep_end_pose=True dodaje po nim odcinek
z zamrożoną końcową pozą aż do końca sekwencji. Postać pozostaje na ziemi także
przy renderowaniu pojedynczej późniejszej klatki. Kolejna animacja tej samej
postaci kończy utrzymywanie pozy. Oryginalny klip nie jest zmieniany.
keep_end_pose dotyczy ułożenia kości; keep_end_position dotyczy przesunięcia
aktora. Można włączyć obie opcje. Jeśli jawnie wydłużysz zakres przez end,
klip nadal może się zapętlać w tym zakresie — dla śmierci pozostaw pojedynczy przebieg.
Aby uniknąć podwójnego przesuwania, sekcja korzysta z wygenerowanej kopii klipu
bez jego poziomego przesunięcia. Oryginalny asset animacji pozostaje bez zmian.
Opcja domyślnie jest wyłączona. Jeśli chcesz własne klucze pozycji, dodaj je po
wywołaniach animation() — zastąpią wygenerowane wartości na tych samych klatkach.
Ruch względem aktualnej pozycji
Po keep_end_position nie musisz obliczać nowych współrzędnych ręcznie:
# Po wywołaniach shot.animation(...): przesunięcie o 50 cm w osi Y.
shot.move(character_creature, start=80, end=120, offset=(0, 50, 0))
Helper odczyta pozycję aktora w klatce 80 z krzywych Sequencera, doda klucz
początkowy, a w klatce 120 klucz przesunięty o offset. To współrzędne świata
w centymetrach. Ostatnia klatka ruchu jest kluczem, więc musi być mniejsza od
shot.frames. Dodawaj ruch po animacjach. move() zastępuje klucze pozycji wewnątrz
odcinka, także wygenerowane przez keep_end_position. Późniejsze klucze pozycji są
przesuwane o wynikową różnicę, żeby zachować dalszy ruch. Klucze rotacji i skali zostają.
Cofanie podczas chodzenia
reverse=True odtwarza klip od końca. W połączeniu z keep_end_position=True
odwraca także przesunięcie zapisane w klipie i zachowuje nową pozycję:
shot.animation(character_creature, CreatureAnimations.MUTANT_WALKING,
start=80, end=120, reverse=True, keep_end_position=True)
Postać odtwarza kroki do tyłu bez zmiany kierunku, w którym patrzy. Jeśli chcesz ustalić odległość cofnięcia samodzielnie, dopisz po animacjach:
shot.move(character_creature, start=80, end=120, offset=(0, -200, 0))
To ustawia cofnięcie o 200 cm w osi Y; reverse odpowiada za kierunek odtwarzania
ruchu nóg, a move zastępuje trasę pozycjami wybranymi przez autora sceny.
Do własnych obliczeń służy:
x, y, z = shot.position(character_creature, frame=80)
Odczyt uwzględnia klucze Transform, keep_end_position i interpolację. Nie przestawia
kursora w Sequencerze. Dotyczy pozycji aktora; ruchy samych kości wewnątrz klipu
bez keep_end_position nie zmieniają pozycji jego aktora. Odczyt wykonuje się w UE
podczas uruchamiania skryptu, więc VS Code nie musi znać liczbowych współrzędnych.
Listę z danymi też można odczytać bez UE:
for clip in FighterAnimations:
print(clip.key, clip.seconds, clip.frames(24))
Własny model studenta — generowanie automatyczne
Każde scene.skeletal_model(...) automatycznie zapisuje nazwy i długości jego
zaimportowanych klipów do Content/Python/ue_workshop/animations.py. Dotyczy to
także modeli pobranych przez studentów, FBX z animacją w środku i .blend.
Umieść model i jego animacje FBX w osobnym folderze, np. source_assets/ninja.
Przy pierwszym uruchomieniu w UE:
character = scene.skeletal_model(
PROJECT / "source_assets/ninja/model.fbx",
name="Ninja",
reimport=False,
)
Powstanie NinjaAnimations — nazwa enuma wynika z name. VS Code zobaczy nowy
statyczny kod i będzie mógł go podpowiadać. W kolejnej edycji skryptu możesz użyć:
from ue_workshop.animations import NinjaAnimations
# Jeśli w folderze był walking.fbx:
clip = NinjaAnimations.WALKING
clip.seconds
clip.frames(shot.fps)
shot.animation(character, clip, start=0)
Pierwszy import w UE jest potrzebny do odczytania rzeczywistych długości.
Późniejsze pisanie sceny korzysta z zapisanego katalogu i nie wymaga komend w UE.
Różnym modelom nadaj różne name — to również nazwa ich assetów w projekcie.
Po dodaniu FBX do folderu uruchom swój skrypt ponownie: brakujący klip zostanie
zaimportowany i dopisany do enuma. Po zmianie istniejącego klipu użyj REIMPORT=True;
nowa długość zapisze się automatycznie. Nie trzeba uruchamiać osobnego generatora.
REIMPORT=False nadal używa istniejących assetów. Nie ma sprawdzania dat źródeł.
Katalogi innych modeli pozostają w pliku. Jawny wybór animations={...} aktualizuje
tylko wybrane wpisy, zachowując inne znane klipy tej postaci. Automatyczny odczyt
całego folderu odświeża pełną listę. Jeśli dane się nie zmieniły, plik nie jest
zapisywany ponownie.
Opcjonalna ręczna aktualizacja dołączonego fightera i creature:
python tools/run_workshop.py tools/update_animation_catalog.py
To narzędzie również zachowuje katalogi własnych modeli studentów.