Grafika v Unreal Engine - jak na efektivní práci s UMG?

Grafika v Unreal Engine - jak na efektivní práci s UMG? Tagy: Vývoj aplikací
| Publikováno dne

Teoretické zázemí týkající se 2D grafiky v Unreal Engine je dostatečně probráno v sekci technického designu grafického rozhraní začleněného v rámci série článků o vývoji VR aplikací v Unreal Engine. S pouhou teorií toho ovšem moc vyvinout nelze, pojďme se tedy podívat na praktickou práci s nejpoužívanějším grafickým rozhraním v Unreal Engine zvaným UMG (Unreal Motion Graphics) prostřednictvím UI designéru.

Bez 2D grafiky se nelze obejít ani v 3D prostoru. V rámci UMG jde primárně o tvorbu interaktivních menu. Jejich člennitost se liší v závislosti na požadavcích. Může jít o jedno tlačítko stejně jako prostor plný interaktivních elementů a možností blízký moderním webovým stránkám. S členitostí roste exponenciálně čas nezbytný pro implementaci této interaktivní grafiky a tedy i konečná cena vývoje aplikace. V tomto článku popíši, jak k tvorbě nejen menu a jeho samotné architektuře sám přistupuji s ohledem na co nejvyšší efektivitu vývoje.

Nejvyšší přidaná hodnota aplikací není v menu, ale v jejím UX a celkové kvalitě vytvořeného digitálního / virtuálního 3d prostoru. Z hlediska minimalizace nákladů (nepřeje-li si klient jinak) používám jednoduché menu běžně známé z mnoha aplikací - jednoduchou zakřivenou černou plochu s bílými interaktivními prvky. Ničím neurazí a je snadno upravovatelná, především, pokud je tvořena komponentárně, viz ukázka z následujícího videa.

Video ukázka

Komponentární přístup k interaktivním prvkům

Komponentární přístup ke jednotlivím UI prvkům řeší hned 2 problémy:

  • jednoduchá architektura kódu
  • konzistentnost UI prvků napříč aplikací

Co to ten komponentární přístup je? Jde o opětovné používání re-použitelných komponent. Takovéto komponenty jsou vytvořeny s důrazem na maximální jednoduchost užívání - a to z hlediska přizpůsobitelnosti, architektury kódu i výkonové optimalizaci. Jde o komponenty třídy UMG.UserWidget, sám je sdružuji na adrese ../UI/UMG/Reuse. Každá z těchto komponent lze použít samostatně, stejně jako může být součástí jiné komponenty.

Ukázka vydá za tisíc slov, v tomto článku tedy ukážu příklad přepínacího tlačítka umožňujícího mít zvolenou 1 možnost z X dostupných. Komponenta je vytvořena v Blueprint = snadno použitelná a srozumitelná pro většinu vývojářů v Unreal Engine. Tento příklad je rovněž součástí video ukázky výše.

Ukázka komponenty Toggle button (Přepínací tlačítko)

Vizuál přepínacího tlačítka (čárkovaná čára je neviditelná)

Přepínací tlačítko je jednou z nejlepších komponent, na níž lze komponentární přístup k práci s UMG vysvětlit. Jde o univerzálně použitelné tlačítko s dynamicky proměnnými parametry (počet dostupných možností od 1 do X), textem každého pole, a rozdílným zvýrazněným 1 polem (uvažujeme-li, že jen jedno pole může být aktivní). Jak takovou komponentu vytvořit?

Proměnlivý počet polí znamená jediné → komponenta musí být generována dynamicky na základě vývojářem určených parametrů. To je možné vrámci události Event Pre Construct umožňující zprocesovat počáteční setup ještě před zobrazením samotného wedgetu.

Proměnné komponenty W_ToggleButton

  • W_SettingsButtons [W_BoundedButton array](private) - proměnná sdružující vytvořená tlačítka v rámci toggle button lišty
  • ButtonAction [EButtonAction](public) - přiřazení lišty k určitému úkolu = v jakém ohledu byla tato komponenta použita (např. změna stránky, povolení zvuku, změna vizuálu...
  • ButtonsText [Text array](public) - Vložení textu pro jednotlivé možnosti → velikost pole definuje počet možností na liště
  • SelectedOptionIndex [Integer](public) - Index aktivní možnosti
Blueprint kód přepínacího tlačítka

V rámci jeho kódu lze spatřit layout panel "Wrapper", což je "Horizontal box", do nějž je generován definovaný počet tlačítek prostřednictvím funkce "Create Toggle Button". Aby se předešlo replikaci prvků při opětovných spuštěních události, hned v prvním kroku dochází k promazání veškerých stávajících prvků Wrapper elementu. K vkládání dochází ve smyčce, po jednotlivých tlačítcích, prostřednictvím funkce "Create Toggle Button". Po dokončení úkolů ve smyčce je nakonec zvýrazněno pole o zvoleném indexu.

Blueprint kód funkce "Create Toggle Button"

V rámci funkce "Create Toggle Button" je využíváno jiné repoužitelné komponenty, a to "W_BoundedButton", což je jednoduché tlačítko umožňující aktivaci / deaktivaci ohraničení (bílého rámečku), volbu textu a především - sbírajícího události o interakci uživatelem prostřednictvím nativní Unreal komponenty "Button" (On Clicked / Pressed / Released / Hovered...). Ještě před vložením tohoto tlačítka do elementu "Wrapper" se provádí core nastavení tlačítka (Value = index tlačítka v toggle liště jakožto hodnota tlačítka, ButtonAction jakožto akce / podpis tlačítka), a to přímou formou (vyžadováno pro správnou funkcionalitu), a po samotném vložení tlačítka do Wrapperu poté i zprostředkovaně Text tlačítka a vlastnosti Size na "fill" (pro responsivitu).

Vizuál přepínacího tlačítka (čárkovaná čára je neviditelná)

Proměnné komponenty W_BoundedButton

  • ButtonAction [EButtonAction](public) - přiřazení tlačítka k určitému úkolu = v jakém ohledu bylo toto tlačítko použito
  • ButtonText [Text](public) - text tlačítka
  • ButtonOn [boolean](public) - aktivace / deaktivace ohraničení (zvýraznění) tlačítka
  • Value [integer](private) - hodnota přiřazená k tlačítku. Například index tlačítka v rámci toggle lišty. Defaultní hodnota je -1 → nepoužíváno v rámci parent komponenty
Blueprint kód komponenty "W_BoundedButton"

U W_BoundedButton je důležité snad jen to, že zpracovává kliknutí uživatele a odesílá je do centrální komponenty, v níž dochází k zprocesování daných požadavků (v příkladu výše funkce "OnUIUserAction" v objektu XRrig_PawnController. Jestliže hodnota tlačítka není záporná - tedy nejde o samostatné tlačítko, je dále prostřednictvím "Event Dispatcher" zaslána událost o kliknutí s parametrem tlačítka i do rodičovského UMG, v našem případě "W_ToggleButton", v němž je odchycena událostí "SetActiveButton". Tato funkce přitom okamžitě mění aktivní tlačítko možností naší "W_ToggleButton" lišty. S UI není po kliku třeba nijak dále pracovat, vše je procesováno automaticky. Jediné, co se provádí, je zprocesování příslušné akce k danému tlačítku a jeho hodnotě, dříve zaslané to XRrig_PawnControlleru.

V rámci BestPracticies je dobrou volbou provést případné změny již v XRrig_PawnControlleru, nicméně mě přijde z hlediska udržení univerzálnosti a repoužitelnosti XRrig_PawnControlleru napříč projekty mnohem lepší volbou přeposlání dané informace dále, a to do relevantního nadřazeného widgetu (sdružuji v ../UI/UMG/HUD), jehož součástí daná toggle lišta je, a až z ní provádět příslušné změny. Vyjma změn definovaných uživatelem totiž takový widget se stará i o nastavení W_ToggleButton lišty (a jiných komponent) při startu aplikace / reotevření okna, a to nastavením dat komponent daty z paměti / databáze vztahující se k uživateli. Pro určitou představu si pusťe video výše. Právě pro možnost těchto nastavení jsou obě repoužitelné komponenty W_BoundedButton a W_ToggleButton - rozšířeny i o souhrn událostí určených k úpravě jejich nastavení z widgetů, jimiž jsou součástí (události typu "SetButtonText", "SetButtonOn"...).

Při dodržování principů repoužitelných komponent se dosahuje vysoké čistoty a funkčnosti kódu s minimálními riziky chyb a potřeby zdlouhavého debugování. Jednoduše:

  1. V rámci celého uživatelského rozhraní je využíváno a tedy nutné řešit jen nízký počet unikátních komponent → Veškeré grafické události (změna vzhledu, animace, efekty atd.) jsou procesovány na základě nastavení dané komponenty (1 komponenta může částečně vypadat a chovat se jinak na základě svého nastavení)
  2. Základní funkcionalita je nativně definována v komponentě a o tuto funkci se není třeba nijak jinak starat (např. jen jedno tlačítko může být aktivní v toggle liště)
  3. S komponentou se interaguje pouze z 1 místa (nadřazeného widgetu) a za 1 konkrétním účelem - počáteční nastavení hodnot hodnotami z DB / paměti
  4. Veškerá interakce s UI je sdružována na jednom centrálním místě (v příkladu výše XRrig_PawnController), z něhož je přímo zpracována (změnou proměnných), případně dále poslána do příslušného widgetu, z něhož případně do dalších podwidgetů, v nichž je konečně zpracována. To důležité je, že UI část je oddělena od procesní části, přičemž obě tyto části jsou naprosto jednoduché, a tedy snadno editovatelné s minimálním rizikem vzbniku nekonečných smyček, ztracení se v kódu a dalších negativních efektů.