Začínáme s C++ v Unreal Engine
V předchozích kapitolách série o vývoji v softwaru Unreal Engine jsme si ukázali základní principy a nastavení projektu pro vývoj 3D real-time aplikací určených pro běh na standalone VR headsetech.
Nyní je čas se podívat na samotnou logiku v pozadí. Tu lze definovat buď prostřednictvím bloků vizuálního programovacího prostředí Blueprint, nebo skrze kód programovacího jazyka C++. Typicky se přitom používá kombinace těchto dvou rozhraní.
Nastavení vývojového prostředí pro vývoj v Unreal Engine s C++
Na začátku jakékoli funkce v Unreal enginu je lidsky čitelný zdrojový kód (source code), tedy blueprint nebo C++. Ovšem samotné aplikace běží na základě binárně spustitelného souboru (binary executable), což je kód běžící v koncovém zařízení. Samotný převod lidsky čitelného kódu do strojově spustitelného řeší kompilátor (compiler).
V případě programování pouze v Blueprint rozhraní se pohybujeme v uzavřeném prostředí zkompilované výchozí verze Unreal Enginu editoru. Jednou z výhod Unreal enginu je nicméně jeho otevřenost, a tu využijeme právě s využíváním programování v jazyce C++.
V tomto případě zasahujeme (nemusíte se bát, typicky se zasahuje pouze přepisy umístěnými v separátních souborech) do samotného zdrojového kódu enginu, a tedy s každým takovým zásahem je nutné Unreal Engine editor opětovně zkompilovat.
C++ kód Unreal enginu lze psát i prohlížeč v tzv. editoru zdrojového kódu (Source code editor), kterým může být jakýkoliv externí software pro editaci kódu. Já osobně aktuálně preferuji Visual Studio Code. Typicky tedy, jakmile skrze Visual Studio Code provedu jakoukoliv změnu ve zdrojovém kódu, musím provést kompilaci, aby se daná změna projevila v rámci Unreal Engine editoru.
Jak jsem zmínil, kompilace probíhá pomocí kompilátoru. Na platformě Windows jde o Visual Studio, na platformě Max o X code. Samotný compiler poté skládá dohromady zdrojový kód v Unreal enginu spolu s definovanými knihovnami do funkčního strojově spustitelného celku.
Instalace Compileru
Nainstalované compilery nebudeme používat ručně - budou používány Unreal enginem automaticky.
Jak bylo uvedeno výše, na platformě Microsoft půjde o Visual Studio, v případě Mac OS poté o XCode.
1. Visual studio (Windows)
Verzi podporovaného Visual Studia pro konkrétní verzi Unreal Enginu zjistíme na stránce Visual Studio Setup webu Unreal Engine. U poslední verze Unreal Engine (verze 5.1) osobně používám nejnovější Visual Studio 2022.
Instalátor Visual Studia lze stáhnout z produktové stránky oficiálního webu Microsoftu. Jestliže potřebujete stáhnout starší verzi, ve spodní části obrazovky je odkaz “Older downloads”. Pracovat lze s jakoukoliv verzí softwaru, včetně bezplatné Community verze. Po spuštění instalačního souboru, při výběru možností v okně “Workloads” zaškrtněte “Game development With C++” a poté ještě v okně detailů vpravo možnost “Unreal Engine installer”.
V případě, že se nenainstalovaly spolu s Visual Studiem .Net nástroje, je nutné je doinstalovat dodatečně. Použít lze osvědčenou starou verzi 3.1 z dotnet sekce webu Microsoftu, nicméně funguje i novější verze .Net 6. Úplně nejjednodušší je nicméně Unreal Engine spustit, a jestliže .net verze chybí, Unreal Engine vám sám poskytné odkaz pro stažení vyžadované .net verze.
2. XCode (Mac OS)
Verzi podporovaného XCode pro konkrétní verzi Unreal Enginu zjistíme na stránce Ios Setup webu Unreal Engine.
Instalátor XCode lze stáhnout ze sekce pro vývojáře webu Apple. Na stránce najděte verzi Xcode dle potřebné podpory. Poslední verzi Xcode lze získat rovněž přímo z App Store.
Instalace editoru kódu (Visual Studio Code)
Visual Studio Code je multiplatformní editor zdrojového kódu - lze jej používat na platformě Mac, Windows i Linux. Je bezplatný, ke stažení na adrese https://code.visualstudio.com/.
Po instalaci Visual Video Code je vhodné nainstalovat rozšíření (Extensions) usnadňující psaní samotného kódu. Téměř nezbytností je C/C++ rozšíření od Microsoftu a Unreal Engine (4) Snippets od CAPTNCAPS.
Tvorba první C++ Class
Nastavení výchozího editoru zdrojového kódu
Ještě než se pustíme do samotného psaní prvního kódu v C++, nastavíme si pro Unreal Engine výchozí editor zdrojového kódu na náš VS Code.
To provedeme v Unreal Engine editoru prostřednictvím nabídky “EDIT → Editor Preferences”, kde v nabídce “Generals” přejdeme na záložku “Source Code” a zde nastavíme položku “Source Code Editor” na námi v předchozím kroku nainstalovaný editor Visual Studio Code. Pro projevení změny je nutné Unreal Engine restartovat.
Tohle nastavení je pro Unreal Engine globální, zvolený editor je nyní výchozí ve všech současných i budoucích Unreal projektech.
Vytvoření C++ classy
C++ classy lze tvořit / vkládat do Unreal Engine projektů definovaných při tvorbě jako C++ i Blueprint projekty.
Samotná tvorbu lze provést pomocí nabídky “Tools → New C++ Class”, případně ze podsložky C++ okna “Content Drawer”.
Unreal Engine Editor nabízí přidání C++ classy pro různé typy objektů, respektive umožňuje pracovat s jakýmikoli zanořeními (tvorba dětských class rozšiřujících rodičovské). Typ Classy i zanoření přitom volíme vždy na základě zamýšlené funkcionality, kterou bude daná class obhospodařovat.
Při definici nové classy Unreal Engine tvoří dvojici souborů. Header file (končící .h) a implementační file (končící .cpp). Při práci s viditelností (Class Type) - .h file by měl být public a .cpp file private.
Při přidání prvního C++ souboru se zobrazí výzva k uzavření Editoru a vytvoření Buildu z IDE, což je zkratka pro Integrated Development Environment, respektive námi preferovaný Editor zdrojového kódu (Visual Studio Code).
Na zvolení tlačítka OK se zobrazí výzva pro rekompilaci našeho projektu.
Při této liště by jste měli zvolit možnost “Yes”. Pokud se náhodou ukliknete, nic se neděje. Totožnou akci lze spustit z nabídky “Tools → Refresh Visual Studio Code Project” a poté v téže nabídce “Tools → Open Visual Studio Code”. Poslední možností k provedení téže akce je poklepání na soubor s koncovkou “.code-workspace” v root složce OS okna vyvíjeného projektu a jeho spuštění ve Visual Code studiu.
Ovšem, při první vložené class v našem projektu je (dle předchozího upozornění) potřebné recompilovat binary executable kódu editoru, čímž jej rozšíříme nejen o naši první c++ classu, ale především vyvíjený projekt získá k dispozici veškeré potřebné nástroje pro vývoj jak v Blueprint tak C++.
Recompilaci provedeme z Visual Studio Code prostřednictvím nabídky “Terminal → Run Build Task” (kl. Zkratka pro Windows je “Ctrl + Shift + B)” a zvolením možnosti “*název projektu* *Označení platformy, na které vyvíjíte* Development Build” (při první kompilaci obvykle 3tí zdola, poté první shora).
Průběh kompilace je viditelný v okné “Terminál”. Při kompilaci je nutné mít zavřený Unreal Engine Editor.
Po řádně dokončené kompilaci (Log terminálu končí informací “Total execution time: XX seconds), * Terminal will be reused by tasks, press any key to close it.“ můžeme projekt v Unreal Engine Editoru opět otevřít. Nově bychom měli vidět v rámci okna Content Drawer (ctrl + spacebar) v rámci složky “C++ Classes” podsložku se jménem našeho projektu. Jestliže složku C++ Classes nevidíte, zkontrolujte zda máte zaškrtnutou možnost “Show C++ Classes” v rámci nastavení okna.
Rovněž můžeme vidět námi vytvořenou C++ komponentu. Jestliže jde o komponentu (class) “soběstačného” typu (Actor, Scene apod.), můžeme ji přetáhnout do scény. Stejně tak vytvořit na základě teto C++ classy odvozenou Blueprint komponentu (ínstanci). O tom však jindy.
Pokud se vrátíme do Visual Studio Code, a vidíme některou z přidaných class zabarvenou červeně, jako je na obrázku níže, znamená to chybějící propojení s Unreal engine editorem. Oprava je snadná, v Unreal Engine Editoru stačí jít do “Tools → Refresh Visual Studio Code Project”.
Struktura C++ classy
Ještě před samotným popisem jednotlivých sekcí a funkcí obecného C++ souboru se podívejme na prostředí softwaru Visual Studio Code. V levé části máme hiearchickou mapu kódu. V horní části je kód související s vyvíjeným projektem (cpluspluscomponents). Z ní nás zajímá především podsložka “Source” obsahující C++ soubory projektu. Níže pak je v rámci podsložky UE5 kód vztahující se k editoru herního enginu Unreal.
Nyní se můžeme podívat na defaultní obsah header souboru (ActorMovement.h), jak mi byl Unreal Enginem vytvořen. Tento soubor slouží pro definici proměnných a funkcí, jejichž samotný obsah je poté v implementačním .cpp souboru.
.h Header file
#pragma once
= zajišťuje, že v případě duplicity v rámci natahování knihoven skrze #include je každá knihovna načtena pouze jednou, bez ohledu, kolikrát je v rámci příkazu #include použita#include filepath/filename.h
= zpřístupnění knihovny v rámci aktuální classy - po vložení lze z aktuální classy přistupovat k public funkcím a proměnným vkládané classy.Jak můžeme vidět v rámci #include příkazů, header soubor je vždy nadřazen .cpp souboru (.cpp do sebe natahuje .h). Tedy, každá zahrnutá knihovna v .h souboru je spolu s tímto souborem vložena i do .cpp souboru. V momentě složitějších komponent tak mohou vznikat poměrně robustní celky, proto mezi best practices patří využívání tzv. dopředné deklarace.
Dopředná deklarace (Forward Declaration) spočívá v jednoduché úpravě, kdy v rámci deklarace proměnných a funkcí v .h souboru pouze uvedeme, že jde o funkci využívající určitou classu, přičemž samotné plné načtení souborů vztahujících se k dané class provádíme (= vložení #include) až v .cpp souboru. Více o Forward Declaration ve Wiki.
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
UCLASS
je propojení na reflexní systém, jde o identifikaci třídy, prostřednictvím níž lze na danou třídu odkazovat. Parametry v závorce jsou meta atributy.class CPLUPLUSCOMPONENTS_API UActorMovement : public UActorComponent
Specifikace classy - class modulu “CPLUPLUSCOMPONENTS”, komponenta typu U (Componenta) s názvem “ActorMovement”, která je odvozena z veřejné UActorComponentGENERATED_BODY()
= spuštění makra generující danou třídu + zápis do reflexního systému.Public
/protected
/private
- deklarace přístupnosti proměnných a funkcí pod ně spadajícíchUActorMovement()
v public - jde o konstrukční funkci objektu. Je aktivní přímo v editoru, nevyžaduje aktivní běh aplikace - jde prostřednictvím ní tedy definovat cokoli poplatného potřebám dané classe již přímo v editoru.virtual void BeginPlay() override;
= funkce, která se spouští při inicializaci classy při play móduvirtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
= Funkce spouštěná s každou frekvencí obrazovky (timeframe)
.cpp file
V rámci .cpp souboru, jež obsahuje samotný obsah C++ kódu funkcí definovaných v header filu si můžeme všimnout příkazu Super::BeginPlay() a Super::TickComponent(). Funkce, které odkazy Super::NazevFunkce obsahují, nesou v definičním .h souboru parametr override. V praxi jde o rozšíření funkcionality UnrealEnginu, kdy skrze příkaz Super::NazevFunkce() se nejprve provedou defaultní úkony dané funkce v UnrealEnginu a posléze kód zapsaný v našem souboru, právě pod odkazem Super::NazevFunkce();
.
Napsání prvního kódu v C++
Pro začátek si můžeme napsat analogii známého konzolového výpisu “Hello World!”, leč pro ukázku pár dalších principů tento příklad lehce rozšířím.
Na obrázku výše jsem v rámci .h souboru definoval novou proměnnou Message typu FName a dále privátní funkci PrintInitLogMessage().
UPROPERTY makro zde zajišťuje registraci do reflexního systému, parametr EditAnywhere poté možnost editace obsahu proměnné v prostředí Unreal Engine editoru.
Funkci PrintInitLogMessage() následně volám v rámci události UActorMovement::BeginPlay() v .cpp souboru.
Smyslem funkce PrintInitLogMessage() je pouze vypsání informace do konzoly, aniž by docházelo k jakémukoli stavu. Z toho důvodu je funkce definována jako konstantní (const). Výstupem funkce je výpis informace do konzoly, a to konkrétně názvu Actoru, z nějž je vypisován společně s obsahem proměnné Message.
On the fly kompilace
Jestliže chceme funkčnost naší zapsané funkce otestovat, je nutné ji zapsat do Unreal Editoru, což znamená provést opětovnou kompilaci. Tu mohu udělat skrze terminál Visual Studio Code, jak jsem provedl dříve, což je však nepraktické, jelikož je před kompilací nutné ukončit Unreal Editor a po úspěšné kompilaci následně opět otevřít pro možnost dalšího vývoje s využitím interakce v prostředí Unreal Engine Editoru.
Při vývoji se tedy využívá praktičtější metoda kompilace, a to metodou “On the Fly”. Tu spustíme kliknutím na kompilační ikonu vpravo dole.
Pozn: On the fly kompilace, nebo česky také živé kódování je skvělý pomocník, avšak nese s sebou určité úskalí - provedené změny neukládá nativně na disk - je poplatné pouze editoru. Po samotném ukončení Unreal Engine editoru může být tedy vhodné provést kompilaci ještě z Visual Studio Code, a to zejména v případě, kdy jsme v editoru měnili výchozí hodnotu proměnných C++.
Pozn2: Aktivaci, deaktivaci a nastavení On The Fly kompilace lze provádět v menu dostupném pod tlačítkem v liště “Edit → Editor Preferences → Live Coding”.
Otestování funkčnosti
Při vytváření C++ classy jsem zvolil komponentu s parent class typu “Actor Component”. Taková komponenta nemůže být vložena v rámci Outliner okna jako samostatný objekt, nýbrž je vždy rozšířením konkrétního Actoru. Pro otestování ji tedy musím vložit do jakéhokoli Actoru ve scéně.
Jestliže vše funguje, v rámci ActorMovement komponenty by mělo jít editovat obsah proměnné “Message”. Pro ukázku jsem zadal hodnotu “Hello World!”.
Jestliže nyní přejde do Play módu, v “Output Log” okně bych měl vidět výstupní zprávu z funkce PrintInitLogMessage(). Pro lepší přehlednost jsem použil filtraci na výpis pouze logů obsahujících úsek testu “OnBegin”.
Přidání další classy
Postup přidání dalších C++ class je shodný s přidáváním první. Jediným rozdílem je, že není nezbytné provádět první kompilaci z Visual Studio Code - všechny zdroje pro vývoj v C++ jsou v tuto dobu již součástí projektu - vystačíte si tedy čistě jen z kompilací typu “On The Fly”.
Jestliže jsou některé soubory ve Visual Studiu zbarvené červeně, tuto chybu opravíte obnovením napojených class v projektu prostřednictvím nabídky v Unreal Engine Editor “Tools → Refresh Visual Studio Code Project”.