# 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. Pierwsze ćwiczenie korzysta wyłącznie z brył silnika. Blender jest opcjonalny. 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 Zapisz aktualną mapę. Wybierz **File → Execute Python Script** i wskaż `exercises/my_shot.py`. Możesz też wpisać w konsoli UE: ```text py "C:/sciezka/do/projektu/exercises/my_shot.py" ``` Powstanie prosta postać, podłoga, trzy światła, kamera i dwusekundowa animacja. Obejrzyj ją w Sequencerze. Zmień kolor, `head_scale`, kamerę albo klucze i uruchom plik ponownie. Skrypt jest odczytywany ponownie z dysku. Ustaw `RENDER = True`, aby wyrenderować film. Domyślny preset `preview` to 640×360, 24 FPS, 48 klatek. `final` to 1280×720 i więcej próbek. 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//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//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/.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. | | `"robot"` | RobotPolice z dołączonego `DemoAssets`. | | `"pilot"` | Humanoid z `DemoAssets/pilot`. | | `"file"` | Własny plik wskazany w `MODEL_FILE`. | Przykład własnego modelu: ```python MODEL = "file" MODEL_FILE = PROJECT / "source_assets/models/my_character.glb" 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. Robot i pilot korzystają z tej konwencji. Bez pliku `_BC` pozostają materiały zaimportowane przez UE. W GLB zachowywany jest osobny materiał dla każdego slotu. Po zmianie postaci reszta skryptu działa tak samo: klucze `location`, `rotation` i `scale` animują cały model. W przykładzie jest też opcjonalny podskok. Modele z rigami są w tej wersji importowane jako statyczna geometria: animacje kości, chodzenie z Mixamo i retargeting nie są jeszcze obsługiwane. Wybrany model działa zarówno w edytorze, jak i przez `my_shot_headless.py`. ## 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" ``` Zapisz zmiany w Blenderze i ponownie uruchom ćwiczenie. Za każdym razem 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, rig, animacje szkieletowe, włosy i shader skóry nie wchodzą w zakres v1. Materiały proceduralne należy wypalić do tekstur. ## Układ i zasady - `Content/Python/ue_workshop/` — biblioteka, automatycznie dostępna w Pythonie UE. - `exercises/my_shot.py` — jedyny wymagany plik uczestnika; zrób kopię przed edycją. - `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 ``` 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`.