basic advanced example

This commit is contained in:
Vysyr committed 2026-10-31 23:11:57 +01:00
1 parent 6fe57915e8
commit bd8edf5afb
29 files changed
+1501 -38

No files matched your search

+181
View File
@@ -0,0 +1,181 @@
# 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`.
```python
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:
```python
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:
```python
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
```python
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:
```python
# 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ę:
```python
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:
```python
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:
```python
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:
```python
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:
```python
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ć:
```python
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:
```text
python tools/run_workshop.py tools/update_animation_catalog.py
```
To narzędzie również zachowuje katalogi własnych modeli studentów.
+32
View File
@@ -7,6 +7,33 @@ Import: `from ue_workshop import Scene, Sequence, Renderer, camera, lighting, ma
Własny skrypt: `scene.skeletal_model(..., reimport=True)` lub `scene.model(..., reimport=True)`.
Nie ma automatycznego wykrywania zmian źródeł.
Enumy i długości bez uruchamiania UE:
```python
from ue_workshop.animations import FighterAnimations
clip = FighterAnimations.GREAT_SWORD_ATTACK
clip.seconds
clip.frames(shot.fps, speed=1.0)
shot.animation(character, clip, start=48) # jeden przebieg; end obliczony z długości
```
Jawne `end` nadal pozwala zapętlić klip. `scene.skeletal_model(path, name="Ninja")`
automatycznie wygeneruje `NinjaAnimations` po pierwszym imporcie w UE, także dla
własnych plików studentów. [Katalog offline i jego odświeżanie](animation_catalog.md).
`shot.animation(character, clip, start=57, keep_end_position=True)` zapisuje poziomy
ruch z klipu jako klucze Transform; końcowy klucz utrzymuje pozycję po ataku.
Źródłowy klip pozostaje bez zmian. Opcja dotyczy ruchu kości głównej.
`shot.position(character, frame=80)` odczytuje pozycję aktora z krzywych sekwencji.
`shot.move(character, start=80, end=120, offset=(0, 50, 0))` dodaje klucze ruchu
o 50 cm względem pozycji w klatce 80, także po `keep_end_position`.
`move` zastępuje klucze pozycji w tym odcinku i odpowiednio przesuwa późniejsze.
`shot.animation(character, clip, reverse=True, keep_end_position=True, start=80, end=120)`
odtwarza kroki i przeniesiony ruch do tyłu. Odległość można nadpisać przez `shot.move`.
`shot.animation(character, CreatureAnimations.MUTANT_DYING, start=150, keep_end_pose=True)`
odtwarza śmierć raz i zachowuje końcową pozę na dalszą część sekwencji.
`camera.create(..., fov=60)` ustawia poziomy kąt widzenia w stopniach: większy FOV
daje szerszy kadr. Zakres: `0 < fov < 180`. Domyślne `43.6` odpowiada obiektywowi
45 mm na sensorze o szerokości 36 mm.
@@ -24,6 +51,9 @@ daje szerszy kadr. Zakres: `0 < fov < 180`. Domyślne `43.6` odpowiada obiektywo
| Korekta osi modelu przed dopasowaniem | `actor = scene.model("hero.fbx", rotation=(0, 0, 90))` |
| Światła studyjne | `lights = lighting.studio(scene)` |
| Pojedyncze światło | `lighting.area(scene, location=(200, -200, 350), intensity=10000, color=(1, 0.5, 0.2))` |
| Żarówka | `lamp = lighting.point(scene, location=(0, 0, 250), intensity=10000, color=(1, 0.5, 0.2))` |
| Reflektor | `spot = lighting.spot(scene, location=(200, -200, 400), target=(0, 0, 100), angle=30)` |
| Słońce (liczy się tylko kierunek) | `sun = lighting.sun(scene, location=(300, -300, 500), target=(0, 0, 0), intensity=500)` |
| Kamera | `cam = camera.create(scene, location=(450, -550, 300), target=(0, 0, 100), fov=43.6)` |
| Zmiana kierunku | `camera.look_at(cam, (0, 0, 150))` |
| Sekwencja | `shot = Sequence(scene, fps=24, frames=48)` |
@@ -31,6 +61,8 @@ daje szerszy kadr. Zakres: `0 < fov < 180`. Domyślne `43.6` odpowiada obiektywo
| Klucz pozycji | `shot.transform_key(actor, 0, location=(0, 0, 0))` |
| Klucz rotacji | `shot.transform_key(actor, 47, rotation=(0, 0, 360))` |
| Klucz skali | `shot.transform_key(actor, 24, scale=(1.2, 1.2, 1.2), interpolation="auto")` |
| Klucz koloru światła | `shot.light_key(spot, 0, color=(1, 0, 0))` |
| Klucz jasności światła | `shot.light_key(spot, 47, intensity=40000, interpolation="auto")` |
| Zapis + otwarcie Sequencera | `shot.save()` |
| Własny renderer | `renderer = Renderer(width=1920, height=1080, samples=8, warmup=32, mp4=True)` |
| Preset z własnymi wartościami | `renderer = Renderer.from_preset("final", samples=8)` |
+73
View File
@@ -100,3 +100,76 @@ Nie sprawdzano retargetingu, Animation Blueprintów ani eksportu wielu akcji/NLA
nakładania; stykające się klipy nie dostają fade. Sprawdzone łańcuchy trzech
klipów oraz odrzucanie niejednoznacznych zakresów i dodawania w odwrotnej kolejności.
- Parametr `blend_in` usunięty z API i przykładów. Zakresy scen pozostają takie same.
## Katalog enumów i długości dostępnych offline
- 34 testy poza UE: statyczne enumy obejmują wszystkie pliki źródłowe, importują
się bez `unreal`, udostępniają sekundy i przeliczenie klatek z FPS i prędkością.
- Sprawdzone zaokrąglanie czasu float32 bez dodawania zbędnej klatki, automatyczny
koniec dla enuma, jawny koniec oraz odrzucenie sekcji dłuższej niż sekwencja.
- `tools/smoke_animation_catalog.py` w UE: porównane długości wszystkich 63 klipów
z rzeczywistym `get_play_length()`, odtworzone enumy, w tym `speed=2`, oraz render
klatki ze środka automatycznego przejścia. Katalog pochodzi z eksportu assetów UE.
- Po przypisaniu parametrów sekcji przywracany jest zakres podany w Pythonie,
ponieważ edytor może przeskalować go przy zmianie prędkości.
## Automatyczne katalogi własnych modeli
- 41 testów poza UE: generowanie nowej klasy, metadane bez `unreal`, zachowanie
innych modeli, aktualizacja podzbioru, usuniętych klipów i niezmienionych danych.
- Sprawdzone odrzucanie kolizji nazw i nieprawidłowych czasów bez naruszenia pliku
oraz dodanie nowej klasy do już załadowanego modułu.
- `tools/smoke_automatic_catalog.py` zaliczony w UE: pierwszy import pod nazwą
StudentCatalogSmoke, automatyczny zapis dwóch klipów, odczyt wygenerowanego enuma
i użycie go do animacji z automatycznym końcem. Katalog testowy zapisano w build.
- Zwykły hook odświeżył także pełne katalogi Fighter i Creature z istniejących assetów.
## keep_end_position
- 45 testów poza UE: dodatkowo pozioma interpolacja ruchu, sumowanie pętli,
włączenie przeniesienia do Transform i ponowne użycie jednej kopii klipu.
- `tools/smoke_keep_end_position.py` zaliczony w UE: slash_2 od klatki 57 przy
30 FPS, wejście slash_3 w klatce 161. Zapisane rzeczywiste klucze rodzica;
po klatce 163 aktor zachowuje około 354 cm przesunięcia dla modelu wysokości 200 cm.
- Sprawdzone ciągłe położenie bioder przy przejściu, brak podwojenia przesunięcia,
niezmieniona źródłowa animacja, ponowne wykonanie i render klatki po ataku.
- Przenoszony jest poziomy ruch pierwszej kości szkieletu (Hips w tym modelu).
Pionowe ruchy i obroty kości zostają w klipie; kopie są w wygenerowanym folderze RootMotion.
## Odczyt pozycji i ruch względny
- 49 testów poza UE: odczyt istniejących kanałów pozycji, pozycja startowa bez
ścieżki, dodanie względnego offsetu oraz walidacja bez tworzenia częściowych kluczy.
- `tools/smoke_relative_move.py` zaliczony w UE: creature wysokości 300 cm,
start `(0, -850, 0)`, MUTANT_RUN z keep_end_position, swiping i walking.
- Pozycja w klatce 80 odczytana z natywnych krzywych Sequencera. Ruch 80–120
zwiększył Y dokładnie o 50 cm, zachował punkt początkowy i pozycję po klatce 120.
Sprawdzono też interpolowaną pozycję w środku ruchu.
## Cofanie i zastępowanie trasy
- 53 testy poza UE: odwrócone odtwarzanie, cofanie ruchu po nierównomiernej krzywej,
sumowanie pętli do tyłu, usuwanie kluczy wewnątrz move i przesuwanie późniejszych.
- `tools/smoke_reverse_walk.py` zaliczony w UE: run → swiping → walking z reverse,
naturalny ruch wstecz oraz zastąpienie trasy cofnięciem o 200 cm.
- Sprawdzone pozycje początkowa, środkowa i końcowa, brak wewnętrznych kluczy
z poprzedniej trasy i zachowanie względnego ruchu późniejszego klucza.
## Zachowanie końcowej pozy
- 55 testów poza UE: natywne Keep State, zamrożony odcinek końcowej pozy oraz
skracanie tego odcinka, gdy pojawi się następny klip tej samej postaci.
- `tools/smoke_keep_end_pose.py`: śmierć mutanta od klatki 0 przy 30 FPS, render
klatki 299 po zakończeniu klipu. Wizualnie sprawdzono, że mutant leży na ziemi.
- Samo Keep State dawało w takim renderze pozę referencyjną; dodatkowy odcinek
z prędkością 0 i przesunięciem czasu do końca klipu utrzymuje właściwą pozę.
## Rodzaje świateł i klucze światła
- 60 testów poza UE: klucze koloru i jasności, ponowne użycie tracków oraz
odrzucanie błędnych argumentów bez tworzenia tracka.
- `tools/smoke_lights.py` w UE: point, spot i sun w jednej scenie, render klatek
0, 24 i 47. Wizualnie sprawdzono zmianę koloru reflektora z czerwonego na niebieski
oraz wzrost jasności reflektora i żarówki.
- Domyślne `intensity=500` słońca dobrano do stałej ekspozycji `camera.create`;
przy `intensity=1` słońce nie było widoczne na renderze.