Files
2026-10-31 20:16:32 +01:00

353 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Python w Unrealu — warsztat na godzinę
UE **5.7**, Windows / Linux. Biblioteka `ue_workshop` automatyzuje scenę,
Sequencer, Movie Render Queue i składanie MP4. Nie wymaga kompilacji C++ ani pip.
Plik do pokazu podstaw korzysta z własnego modelu z Blendera i obrotu kamery.
Zadanie z animacjami korzysta z dołączonych postaci Mixamo i nie wymaga Blendera.
Jeśli używasz wcześniejszej kopii projektu z C++ i edytor zgłasza
`Target Upgrade Required`, zamknij UE i usuń pozostałości starej kompilacji:
`Binaries/`, `Intermediate/Build/` oraz `Intermediate/TargetInfo.json`.
Nowy projekt nie ma własnego targetu C++ i nie wymaga aktualizacji `Target.cs`.
## Przygotowanie przed spotkaniem
1. Zainstaluj UE 5.7 i otwórz `ExampleProject.uproject`. Poczekaj na kompilację shaderów.
2. Zainstaluj FFmpeg z enkoderem `libx264` i dodaj do PATH, jeżeli chcesz MP4.
Bez FFmpeg można renderować PNG: `render.start(shot, mp4=False)`.
3. Zwykły Python 3.10+ jest potrzebny tylko do launchera z terminala.
4. Sprawdź `python tools/run_workshop.py --check` i wykonaj ćwiczenie próbnie.
W projekcie są już włączone Python Editor Script Plugin, Editor Scripting Utilities,
Sequencer Scripting, Movie Render Queue oraz Interchange. Python jest narzędziem
automatyzacji edytora; nie służy tu do gameplayu.
## Pierwszy skrypt w edytorze
Pliki do prowadzenia zajęć:
- `exercises/basics.py` — prosty pokaz: `character_custom.blend`, podłoga, światła
i kamera robiąca pełne 360° w 8 sekund. Postać pozostaje nieruchoma.
- `exercises/animation_starter.py` — punkt wyjścia dla studentów: jedna creature,
dwie animacje (`mutant_idle`, `mutant_swiping`) i 15 sekund na eksperymenty. Własną scenę walki
tworzą przez dodanie klipów, ruchu, drugiej postaci i zmian kamery.
- `exercises/my_shot.py` — dotychczasowy rozbudowany plik z wyborem modelu.
- `examples/mixamo_characters.py` — fighter i creature z pełnymi słownikami klipów;
zachowany układ sceny i dotychczasowe klipy, gotowe do własnej sceny walki.
W nowych plikach render jest zakomentowany. Najpierw obejrzyj scenę w Sequencerze,
potem odkomentuj `renderer.render(shot)`. Każdy plik tworzy własną nazwaną mapę.
Przed edycją zrób kopię pliku; ponowne uruchomienie przebudowuje jego scenę.
`REIMPORT = False` używa istniejących modeli i klipów w UE. Brakujące assety
importowane są przy pierwszym uruchomieniu. `REIMPORT = True` wymusza ponowny
import wszystkich wskazanych modeli i klipów, również eksport `.blend` z Blendera.
Po odświeżeniu ustaw ponownie `False`. Zmiany plików nie są wykrywane automatycznie.
Zapisz aktualną mapę. Wybierz **File → Execute Python Script** i wskaż
`exercises/basics.py`. Możesz też wpisać w konsoli UE:
```text
py "C:/sciezka/do/projektu/exercises/basics.py"
```
Powstanie własna postać z Blendera, podłoga, trzy światła i ośmiosekundowy objazd kamerą.
Obejrzyj go w Sequencerze. Zmień model, kolor podłogi, kamerę albo klucze i uruchom
plik ponownie. Skrypt jest odczytywany ponownie z dysku.
W `my_shot.py` ustaw `RENDER = True`, aby wyrenderować film. Preset `preview` to 640×360,
a `final` to 1280×720 i więcej próbek. FPS i długość filmu pochodzą z `Sequence`.
Ustawienia własnego renderera są na końcu ćwiczenia.
Szybkie sprawdzenie obrazu: zamiast pełnego renderu użyj
`renderer.render(shot, single_frame=24)`.
Własne ustawienia wpisujesz bezpośrednio w skrypcie uczestnika:
```python
from ue_workshop import Renderer
renderer = Renderer(width=1920, height=1080, samples=8, warmup=32, mp4=True)
renderer.render(shot)
# Albo zamiast filmu: renderer.render(shot, single_frame=24)
```
`width` i `height` to piksele, `samples` to liczba próbek przestrzennych na klatkę,
a `warmup` to liczba klatek rozgrzewki. Wymiary i próbki muszą być dodatnimi
liczbami całkowitymi, rozgrzewka może wynosić zero. FPS pochodzi z sekwencji.
Można też użyć `Renderer.from_preset("final", width=1920, height=1080)`:
argumenty nadpisują wybrane wartości presetu. Obiektu użyjesz ponownie do kolejnych
sekwencji po zakończeniu poprzedniego renderu. Ustawienia są zapisane w `result.json`.
Dotychczasowe `render.start(...)` nadal działa jako skrót do presetów.
Wyniki: `renders/MyShot/<czas_i_id>/frames/`, `film.mp4`, `result.json`.
Każdy render ma nowy katalog. Nieudane kodowanie zachowuje klatki.
MP4 powstaje dopiero po zakończeniu MRQ; nie zamykaj wtedy edytora.
Na czas przebudowy sceny i renderowania biblioteka zamyka kartę Sequencera.
Render startuje po powrocie ze skryptu. Sekwencję można ponownie otworzyć
z Content Browser; ponowne uruchomienie ćwiczenia też ją otwiera.
## Z terminala, bez okna edytora
Drugi plik obok ćwiczenia uruchamia `my_shot.py` bez okna UE, czeka na zakończenie
renderu oraz kodowania MP4 i automatycznie zamyka uruchomiony proces edytora:
```text
python exercises/my_shot_headless.py
```
Uruchamiaj go **zwykłym Pythonem z terminala**, nie przez Execute Python Script
w Unreal. Korzysta z obiektu `renderer` i ustawień zapisanych w `my_shot.py`.
Wymusza render również przy `RENDER=False`. Wynik trafia do tego samego
`renders/MyShot/<czas_i_id>/film.mp4`. Można dopisać `--editor`, `--timeout`
lub `--check`; ścieżka skryptu jest ustalana względem pliku launchera.
Domyślnie konsola pokazuje etapy: uruchamianie UE, przygotowanie sceny,
renderowanie, składanie MP4 i zamykanie UE, a na końcu wynik i ścieżkę do logu.
Podczas renderowania podaje liczbę zapisanych PNG; przy dłuższym oczekiwaniu
co 10 sekund potwierdza, że proces nadal działa. To nie jest szacowany procent
całego zadania — rozgrzewka, zapis klatek i kodowanie mają różne czasy wykonania.
Pełne logi trafiają do `Saved/Workshop/<id>.log`, także przy błędzie lub timeout.
Żeby widzieć logi UE na żywo w terminalu, dodaj:
```text
python exercises/my_shot_headless.py --verbose
```
Ścieżki filmu i logu są klikalne w obsługujących to terminalach, m.in.
Windows Terminal i terminalu VS Code (zwykle Ctrl+klik). W pozostałych konsolach
oraz przy przekierowaniu wyjścia do pliku wypisywane są zwykłe ścieżki.
Można wymusić linki opcją `--links always` lub wyłączyć je przez `--links never`.
Ogólny launcher do pozostałych skryptów:
```text
python tools/run_workshop.py --check
python tools/run_workshop.py exercises/my_shot.py --render
python tools/run_workshop.py exercises/my_shot.py --single-frame 24
```
Bez `--render` launcher tylko wykonuje skrypt i zapisuje scenę (chyba że sam skrypt
uruchamia render). Zwraca kod 0 przy powodzeniu i 1 przy błędzie. Domyślny limit
to 900 sekund; można zmienić `--timeout`. Nie używaj `-nullrhi` do renderowania.
Tryb bez okna nadal wymaga GPU, sterowników i na Linuksie działającego Vulkan.
Obsługa Linuksa jest przygotowana w kodzie; trzeba sprawdzić ją na docelowej maszynie.
Ścieżki programów można ustawić zmiennymi środowiskowymi:
```powershell
$env:WORKSHOP_UNREAL = 'C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe'
$env:WORKSHOP_BLENDER = 'C:\Program Files\Blender Foundation\Blender 5.1\blender.exe'
$env:WORKSHOP_FFMPEG = 'C:\tools\ffmpeg\bin\ffmpeg.exe'
```
```bash
export WORKSHOP_UNREAL=/opt/UnrealEngine/Engine/Binaries/Linux/UnrealEditor
export WORKSHOP_BLENDER=/usr/bin/blender
export WORKSHOP_FFMPEG=/usr/bin/ffmpeg
python3 tools/run_workshop.py --check --blender
```
Ścieżki nie są zapisane na sztywno w bibliotece. Launcher ma domyślną lokalizację
instalacji UE 5.7 na Windows; na Linuksie korzysta ze zmiennej lub PATH.
## Wybór postaci i model pobrany z internetu
W `exercises/my_shot.py` wybierz `MODEL`:
| Wartość | Postać |
|---|---|
| `"primitive"` | Dotychczasowa postać z brył, bez importu. |
| `"fighter"` | Wojowniczka z `source_assets/character/fighter`. Domyślna postać. |
| `"creature"` | Stwór z `source_assets/character/creature`. |
| `"file"` | Własny plik wskazany w `MODEL_FILE`. |
Przykład własnego modelu:
```python
MODEL = "file"
MODEL_FILE = PROJECT / "source_assets/character/my_character.fbx"
SKELETAL = True
ANIMATION_FILE = None # animacja zawarta w FBX; albo ścieżka do osobnego klipu FBX
MODEL_HEIGHT = 200
MODEL_ROTATION = (0, 0, 90)
```
Rozpakuj pobrany model razem z teksturami. Możesz użyć ścieżki absolutnej
albo ścieżki względem projektu. GLB z osadzonymi teksturami PNG/JPEG pozwala
przenieść podstawowe materiały PBR w jednym pliku. FBX i OBJ wymagają zachowania
plików tekstur i powiązanych FBM/MTL. Nie wczytujemy ZIP ani plików `.uasset`.
Import zawsze odbywa się do wydzielonego katalogu generowanych assetów.
Biblioteka łączy części modelu, ustawia jego wysokość w centymetrach i przesuwa
podstawę na Z=0. `MODEL_HEIGHT=None` zachowuje rozmiar źródłowy. Jeśli model leży
na boku, popraw `MODEL_ROTATION` (roll, pitch, yaw); obrót jest stosowany przed
dopasowaniem wysokości. Pivot animacji znajduje się na środku podstawy.
Dla pojedynczego atlasu FBX/OBJ można dołączyć mapy `NazwaModelu_BC.png`,
`_N`, `_R`, `_M`, `_E` (również JPG/JPEG). Biblioteka odtworzy z nich jeden
materiał dla wszystkich slotów w statycznym przepływie `scene.model(...)`.
Bez pliku `_BC` pozostają materiały zaimportowane przez UE. W GLB zachowywany
jest osobny materiał dla każdego slotu.
Dla `fighter` i `creature` użyj nazwy pliku bez rozszerzenia, ze spacjami zamienionymi
na podkreślenia: np. `great_sword_walk` lub `mutant_swiping`. Dostępny jest
[pełny katalog animacji](docs/animations.md): 51 klipów fighter i 12 klipów creature.
`scene.skeletal_model(...)` samo znajduje wszystkie pliki FBX w folderze postaci
poza modelem. Nie wymaga `animations` ani ręcznych aliasów. Import tworzy SkeletalMesh, szkielet,
materiały i wszystkie klipy. Sequencer odtwarza
animację kości przez 120 klatek (5 s); krótszy klip zapętla się w swojej sekcji.
Klucze `location`, `rotation` i `scale` mogą dodatkowo poruszać całym modelem.
Przykład dwóch postaci z różnymi klipami: `examples/mixamo_characters.py`.
```python
character = scene.skeletal_model(
PROJECT / "source_assets/character/creature/Maw J Laygo.fbx",
name="Creature", height=200, location=(150, 0, 0),
)
shot.animation(character, "mutant_walking")
```
`shot.animation(character, "mutant_walking", start=0, end=48, speed=0.8)` odtwarza klip
w zakresie `[0, 48)` z prędkością 80%. Następny klip może zacząć się w klatce 48.
Nakładające się klipy tej samej postaci automatycznie tworzą płynne przejście
przez cały wspólny zakres. Helper ustawia narastanie wagi nowego klipu
i zanikanie poprzedniego; nie trzeba podawać dodatkowego parametru:
```python
shot.animation(character, "mutant_idle", start=0, end=60)
shot.animation(character, "mutant_walking", start=48, end=120)
```
Przy 24 FPS przejście na klatkach 48–60 trwa 0,5 sekundy. Dodawaj klipy w kolejności
odtwarzania; naraz mogą mieszać się dwie animacje tej samej postaci.
Klipy stykające się końcami przechodzą bez blendu. Nie trzeba ponownie importować animacji.
Przy kolejnych przejściach środkowy klip zachowa zarówno wejście, jak i wyjście.
Pełne zawieranie jednego klipu w drugim oraz identyczne zakresy są odrzucane.
`shot.animation(character)`
wybiera jedyny klip zawarty w modelu. Każda postać zachowuje swój szkielet;
automatyczny retargeting między różnymi postaciami nie jest wykonywany.
W Mixamo pobierz FBX **With Skin**, aby mieć model, szkielet i animację w jednym
pliku. Kolejne klipy pobieraj dla tej samej wybranej postaci; mogą być **Without Skin**.
Umieść je obok pliku modelu — są wykrywane automatycznie, pod nazwami plików.
Opcjonalne `animations={"mutant_idle": path1, "mutant_walking": path2}` ogranicza
wybór do konkretnych klipów. Puste `animations={}` pomija animacje z folderu
i pozostawia tylko klipy zawarte w pliku modelu.
Jeśli samodzielnie animujesz trasę przez `transform_key`, wybierz **In Place**,
gdy jest dostępne, aby animacja kości nie dodawała własnego przesuwania.
Importy szkieletowe trafiają do `Content/Workshop/Generated/Skeletal`.
Przy `REIMPORT=True` aktualizowane są te same assety. Pivot jest na środku podstawy
pozy referencyjnej; podczas animacji obrys i pozycja stóp mogą się zmieniać.
Dla statycznego GLB, FBX, OBJ lub `.blend` ustaw `MODEL="file"`, `SKELETAL=False`.
Dotychczasowe `scene.model(...)` zachowuje statyczny przepływ importu.
Wybrany model działa zarówno w edytorze, jak i przez `my_shot_headless.py`.
### Mixamo → Blender → Unreal
Zaimportuj do Blendera FBX ze szkieletem i animacją. Zmieniaj geometrię, zachowując
Armature, wagi wierzchołków i modyfikator Armature. Siatki umieść w kolekcji
`Character`; muszą być przypisane do jednego Armature. Wybierz aktywną akcję
i ustaw zakres klatek sceny obejmujący animację. Zapisz `.blend`, a w ćwiczeniu ustaw:
```python
MODEL = "file"
MODEL_FILE = PROJECT / "source_assets/character/my_animated_character.blend"
SKELETAL = True
ANIMATION_FILE = None
```
Python uruchomi Blendera w tle, wyeksportuje siatki, szkielet i aktywną animację
do FBX, a następnie zaimportuje je w UE. Eksport obejmuje aktywną akcję w zakresie
sceny; nie eksportuje osobnych akcji i pasków NLA jako wielu klipów. Materiały FBX
przenoszą podstawowe kolory i obsługiwane tekstury. Proceduralne materiały wypal
do tekstur; statyczne modele z pełniejszym PBR nadal eksportuj do GLB.
Blender potrzebuje binarnego FBX 7.1 lub nowszego. Dołączone fighter i creature
są w starym FBX 6.1: UE importuje je bezpośrednio, ale Blender 5.1 ich nie otworzy.
Do edycji w Blenderze pobierz współczesny wariant FBX albo wyeksportuj SkeletalMesh
i animację z UE do nowego FBX.
## Blender → UE → film
Własną kopię wybierzesz też bezpośrednio w `my_shot.py`:
```python
MODEL = "file"
MODEL_FILE = PROJECT / "source_assets/character/character_custom.blend"
SKELETAL = False # ten przykład to statyczne siatki bez Armature
```
Zapisz zmiany w Blenderze i uruchom ćwiczenie z `REIMPORT=True`. Plik
jest eksportowany w tle i aktualizuje ten sam asset `CustomCharacter` w UE.
Nie trzeba ręcznie usuwać poprzedniego modelu. Eksport obejmuje kolekcję `Character`.
Na Windows biblioteka wykrywa też standardowe instalacje w `Blender Foundation`,
jeśli Blender nie jest w PATH; `WORKSHOP_BLENDER` pozwala wybrać inną instalację.
Przykładowy model znajduje się w `source_assets/character/character.blend`.
Można go też odtworzyć (generator nie nadpisuje istniejącego pliku):
```text
blender --background --python-exit-code 1 --python tools/create_character.py
```
Otwórz model w Blenderze, powiększ obiekt `Head`, zmień kolor materiału i **zapisz**.
Uruchom `examples/blender_character.py` w UE albo:
```text
python tools/run_workshop.py examples/blender_character.py
```
Skrypt uruchamia osobny proces Blendera, eksportuje kolekcję `Character` do GLB,
aktualizuje model i materiały w UE, buduje sekwencję i renderuje MP4.
Blender nie musi być otwarty. Eksport czyta ostatni zapis z dysku.
Umowa dla modelu: statyczne siatki w kolekcji `Character`, metry w Blenderze,
stopy na Z=0, przód w +X. Modyfikatory są uwzględniane, obiekty łączone do jednej
siatki z zachowaniem slotów materiałów. Origin eksportu to zero świata.
Podstawowe PBR: Principled BSDF, kolory, roughness, metallic, obsługiwane tekstury.
Tekstury PNG/JPEG są wyciągane z GLB i aktualizowane w wydzielonym katalogu
`WorkshopTextures`. Parametry PBR synchronizujemy jawnie, ponieważ standardowy
reimport siatki w UE zachowuje wcześniejsze materiały. Zmiany w tych importowanych
materiałach wykonuj w Blenderze; skrypt nadpisuje wspierane parametry ich źródłem.
Dowolne grafy proceduralne, włosy i shader skóry nie są przenoszone w tym przepływie.
Materiały proceduralne należy wypalić do tekstur. Modele z Armature obsługuje
`scene.skeletal_model(...)`, opisane wyżej.
## Układ i zasady
- `Content/Python/ue_workshop/` — biblioteka, automatycznie dostępna w Pythonie UE.
- `exercises/basics.py` — pokaz podstaw na własnym modelu z Blendera.
- `exercises/animation_starter.py` — plik startowy zadania z dwiema animacjami.
- `exercises/my_shot.py` — rozbudowany wariant z wyborem postaci.
- `examples/` — dodatkowa kamera i Blender; ćwiczenie startowe jest też rozwiązaniem bazowym.
- `tools/` — launcher, eksport/generator Blendera i testy integracyjne.
- `docs/` — ściąga API i scenariusz spotkania.
- `Content/Workshop/Generated/` — mapy, sekwencje, importy i materiały generowane w UE.
- `build/`, `renders/` — pliki pośrednie i wyniki, pomijane przez Git.
- `archive/legacy_python/` — stare skrypty; poza ścieżką importu UE.
- `DemoAssets/` — zachowane źródłowe modele z poprzedniego projektu, opcjonalne.
`Scene("MyShot")` zarządza mapą `/Game/Workshop/Generated/MyShot/Map`.
Ponowne wykonanie usuwa w niej tylko aktorów z tagiem `Workshop`, a sekwencję
odbudowuje. Własną scenę nazwij inaczej, np. `Scene("AniaShot")`.
Generowana sekwencja jest w całości zastępowana: ręczne zmiany zachowaj w jej kopii.
Mapy spoza warsztatu nie są czyszczone; przy niezapisanej obcej mapie skrypt prosi o zapis.
Nie usuwaj całego folderu Generated podczas pracy — zawiera aktualnie używane assety.
## Weryfikacja
```text
python -m unittest discover -s tests -v
python tools/run_workshop.py tools/smoke_scene.py
python tools/run_workshop.py tools/smoke_blender.py
python tools/run_workshop.py tools/smoke_renderer.py
python tools/run_workshop.py tools/smoke_skeletal.py --render
python tools/run_workshop.py tools/smoke_workshop_files.py
```
Pierwszy test integracyjny wykonuje ćwiczenie dwa razy, sprawdza duplikaty,
renderuje dwie klatki i koduje MP4. Drugi wymaga Blendera i sprawdza powtórny
import zmienionej geometrii oraz materiałów i render.
Opis sprawdzonego środowiska: `docs/validation.md`.