basic advanced example
This commit is contained in:
1 parent
6fe57915e8
commit
bd8edf5afb
29 files changed
+1501
-38
No files matched your search
@@ -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.
|
||||
@@ -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)` |
|
||||
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user