From 870dfa9a2d5cfeb6f91da3e6c8d661159548f417 Mon Sep 17 00:00:00 2001 From: Oleg Petruny Date: Mon, 19 May 2025 22:09:17 +0200 Subject: [PATCH 1/8] wip --- ch2.tex | 142 ++++++++++++------------------ img/InteractableScreenCapture.pdf | Bin 0 -> 37712 bytes refs.bib | 60 ------------- 3 files changed, 56 insertions(+), 146 deletions(-) create mode 100644 img/InteractableScreenCapture.pdf diff --git a/ch2.tex b/ch2.tex index 99a0da8..a3c14b8 100644 --- a/ch2.tex +++ b/ch2.tex @@ -1,103 +1,73 @@ -\chapter{More complicated chapter} -\label{chap:math} +\chapter{Vývoj hry} +\label{chap:main} -After the reader gained sufficient knowledge to understand your problem in \cref{chap:refs}, you can jump to your own advanced material and conclusions. +Přenos starého projektu na~další major verzi~UE nebyl nijak problematický, dokonce v~editoru je~možnost rychlého exportu assetů do~jiného projektu. Rozhodně tomu pomohlo, že~se~C++~kód nepřenášel, ale~napsal znovu. UE~citelně rozšířil seznam dostupných tříd, ale~zaroveň některé jsou~již deprecated nebo~odstraněny úplně. Tak~například byly~odstraněny třídy Matinee (bývalý format animací pohybu objektů) pro~podporu novejší třídy Sequencer nebo~starý PhysX rozhraní (Fyzický engine), které~je nahrazeno systémem Chaos. Přesto se~něco pokazilo při~exportu objektů s~dynamickou fyzikou (závěsy, které~reagují na~simulaci větru, se~museli předělat). -You will need definitions (see \cref{defn:x} below in \cref{sec:demo}), theorems (\cref{thm:y}), general mathematics, algorithms (\cref{alg:w}), and tables (\cref{tab:z})\todo{See documentation of package \texttt{booktabs} for hints on typesetting tables. As a main rule, \emph{never} draw a vertical line.}. \Cref{fig:f,fig:g} show how to make a nice figure. See \cref{fig:schema} for an example of TikZ-based diagram. Cross-referencing helps to keep the necessary parts of the narrative close --- use references to the previous chapter with theory wherever it seems that the reader could have forgotten the required context. Conversely, it is useful to add a few references to theoretical chapters that point to the sections which use the developed theory, giving the reader easy access to motivating application examples. +Přenos byl~odůvodněn převážně malou velikostí starého projektu a~taky lákavou nabídkou nových technologií zejména Nanite a~Lumen. Navíc pátá verze Unrealu -- přesněji verze~5.5 -- přinesla značná vylepšení jako: +\begin{itemize} +\item Nový systém zpracování vstupu Enhanced Input\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/enhanced-input-in-unreal-engine}. Místy má~příliš mnoho objektové abstrakce, ale~rozhodně velký krok vpřed. Umožňuje snadné dynamické přepínání různých sad ovládání (multiplayer hry, gameplay/menu), dynamické modifikátory a~spouštěče vstupu (změna senzitivity, víceklik, podržení tlačítka určitou dobu), podpora vstupu více než~jedné periferie naraz a~podpora přeřazení vstupu (např.~změna tlačítka odpovídající za~skok herní postavy). Předtím tohle a~spoustu dalšího se~muselo naprogramovat ručně. +\item Podporu vektorové grafiky v~UI. Veškeré staré UI~elementy byly~tvořeny pomocí základních vektorových obdélníkových tvarů práve proto, aby~se~vyhnulo použití rastrové grafiky, která~je~hodně závislá na~rozlišení. Aktuálně všechny UI~elementy jsou tvořeny ještě starou metodou, pro~udržení konzistentního vzhledu. Dnes bychom určitě využili této~možnosti. +\item Přepracované vykreslování textů -- rychlejší vykreslování a~efektivnější využití paměti. +\item MetaSound\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/metasounds-the-next-generation-sound-sources-in-unreal-engine} pro~přehrávaní nebo procedurální generování zvuků, který~nahrazuje starou třídu~Cue. De-facto se~jedná o~Digital Signal Processing (DSP) grafový engine a~editor. Bohůžel jsem nestihl tento nastroj využit v~práci. +\item World partition systém\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/world-partition-in-unreal-engine} který~dokáže automaticky rozdělit jeden velký svět na~streamovací kousky a~propojit sdílení dat mezi nimi i~při multiplayer hře přes internet. Taky není využit v~této práci. +\end{itemize} +Samozřejmě je~toho daleko víc, ale~většina ostatních vylepšení jako~například nový systémy animací postav Motion Matching\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/motion-matching-in-unreal-engine} nebo~nový fyzikální engine Chaos\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/destruction-overview} neměli vliv na~rozhodnutí. -\section{Example with some mathematics} -\label{sec:demo} +\section{Herní logika a systémy} +\subsection{Architektura} +Protože Unreal Engine byl na začátku vývíjen primárně pro online multiplayer hru Unreal Tournament a dnes stejně tak vyvíjen spolu s extremně velkou online multiplayer hrou Fortnite, je backend enginu dost abstraktní. -\begin{defn}[Triplet]\label{defn:x} -Given stuff $X$, $Y$ and $Z$, we will write a \emph{triplet} of the stuff as $(X,Y,Z)$. -\end{defn} +Například tak každý svět musí obsahovat vlastní game mode (třída AGameMode), který funguje jako správce dáneho světu -- přestože svět se může spravovát sám. Veškerá funkcionalita používaná v singlplayer hrách mohla by být přímo v kódu levelu nebo v globální instanci celé hry. Důvodem teto abstrakce je právě nativní podpora multiplayer her, která potom vývoj takových her zjednodušuje. -\newcommand{\Col}{\textsc{Colour}} +Protože tento projekt je zaměřen pro hru jednoho hráče, práce je často programováná navzdory ustaleným UE C++ guidelines pro pohodlí a rychlost vývoje. Proto často třídy managerů systémů, game modů, instance hráče a další jsou na architektuře singletonu. Šetří od tří do desetí různých volání getteru, castu reflexe, vyhledávání v hash tabulkach a iteraci polí v každém místě použití. Přesto nemá žádný vliv na stabilitu programu a dokonce šetří výkonem zařízení. -\begin{thm}[Car coloring]\label{thm:y} -All cars have the same color. More specifically, for any set of cars $C$, we have -$$(\forall c_1, c_2 \in C)\:\Col(c_1) = \Col(c_2).$$ -\end{thm} +Jen pro představu co obnaší klasické ziskání reference na instanci vlastní třídy hráče na pozadí. GetGameMode() \textrightarrow GetPlayerController(index) \textrightarrow GetPlayerPawn() \textrightarrow Cast(). A blueprinty disponují zkrácenou verzí GetPlayerPawn(index) \textrightarrow Cast(). V případě C++ je potřeba navíc ověřovat zda nějaké z volání nevratílo nullptr. -\begin{proof} -Use induction on sets of cars $C$. The statement holds trivially for $|C|\leq1$. For larger $C$, select 2 overlapping subsets of $C$ smaller than $|C|$ (thus same-colored). Overlapping cars need to have the same color as the cars outside the overlap, thus also the whole $C$ is same-colored.\todo{This is plain wrong though.} -\end{proof} +\subsection{Scény a ukládání hry} +Scény resp. levely (třídy UWorld a ALevelScriptActor)\footnote{UWorld instance je přímo celý svět, který funguje jako balík metadat a kontejner pro veškeré instancované objekty v něm. ALevelScriptActor je objekt instancovaný v UWorld automaticky a obsahuje uživatelskou logiku světa.} taktéž lze získat v podobě singletonu. Základní implementace byla rozšířená pro high level mechanismus volání událostí ve scéně. Ten je za potřebí například, když hráč aktivuje spínací plošinu ve hře a ta následně otevře dveře. Narozdíl od jiných enginů, v UE objekty ve světě nemůžou referencovat jiné nezávislé objekty. Jednoduše referenci nejde přiřadít z důvodu abstrakce popsané v předchozí podsekci, protože každý objekt může mít vlastní herní svět (a nejen to). Proto pokud plošina z našeho príkladu chce otevřít dveře, musí zkusit poslat požádavek správcí herního světa, ten požadavek se nějak zpracuje a jen potom správce buď provede akci otevírání dveří samostatně nebo zavolá odpovidající funkci v instanci objektu. Pokud by byla potřeba zbavit se použití singleton architektury pro podporu paralelní existenci světů, stači předělat member funkci instance světa na statický multicast delegate\footnote{Delegate jsou obaly na C++ funkce, lambdy, funkce s reflexi a funkce z Blueprintu. Delegate může být typu single, pro uložení reference na jednou funkci, nebo multicast pro uložení dynamického seznamu funkcí. Podrobněji jsou popsané v programatorské dokumentaci.}, ke kterému se každý svět při konstrukci bude vázat. -\begin{table} -% uncomment the following line if you use the fitted top captions for tables -% (see the \floatsetup[table] comments in `macros.tex`. -%\floatbox{table}[\FBwidth]{ -\centering\footnotesize\sf -\begin{tabular}{llrl} -\toprule -Column A & Column 2 & Numbers & More \\ -\midrule -Asd & QWERTY & 123123 & -- \\ -Asd qsd 1sd & \textcolor{red}{BAD} & 234234234 & This line should be helpful. \\ -Asd & \textcolor{blue}{INTERESTING} & 123123123 & -- \\ -Asd qsd 1sd & \textcolor{violet!50}{PLAIN WEIRD} & 234234234 & -- \\ -Asd & QWERTY & 123123 & -- \\ -\addlinespace % a nice non-intrusive separator of data groups (or final table sums) -Asd qsd 1sd & \textcolor{green!80!black}{GOOD} & 234234299 & -- \\ -Asd & NUMBER & \textbf{123123} & -- \\ -Asd qsd 1sd & \textcolor{orange}{DANGEROUS} & 234234234 & (no data) \\ -\bottomrule -\end{tabular} -%}{ % uncomment if you use the \floatbox (as above), erase otherwise -\caption{An example table. Table caption should clearly explain how to interpret the data in the table. Use some visual guide, such as boldface or color coding, to highlight the most important results (e.g., comparison winners).} -%} % uncomment if you use the \floatbox -\label{tab:z} -\end{table} +Navíc hra byla rozšířena o implementaci obnovení hry z úložených dat. Při návrhu byla snaha udělat postup nejvíc triviální. Hru ukládá globální herní instance (třída GameInstance)\footnote{GameInstance je první objekt vytvořený enginem hned po úvodním načtení základu enginu a taky poslední objekt na destrukci při vypínání hry.} při vypínání hry a načíta taktéž při spouštění. Využit byl existující v UE systém serializace, který ukládá inventař postavy, jmeno levlu a checkpoint na něm spolu s posledním stavem levlu. Podrobný proces obnovení uložené hry a práce se serializací je popsan v programatorské dokumentaci. + +\subsection{Interakce} +Interakce jsou často řešeny pomoci architektury interface tříd\footnote{Interface třídy jsou konkurenčním přístupem komponentní architektuře. Interface je běžná praktika v OOP, která funguje jako domluva, že objekt bude obsahovat určité member funkce. Komponentny jsou samostatné určité podinstance objektu. Komponentní přístup se osvědčuje jako intuitivnější a více flexibilní zatímco Interface přístup při vývoji her je spíše nepřijemné vynucení ze světu OOP.} především kvůli populárním návodům vyzdvihující tuto metodu jako nejlepší ještě z doby, kdy jen vzníkal Unreal Engine 4. Nevýhodou je potřeba v obsáhlem a repetetivním nastavení každeho objektu, který chceme zapojit do mechaniky interakce. + +Byla navržena celá třída pro kompletní spravu každého intekačního objektu a komponent. Objekt dědicí třídu AInteractable při instancování samostatně nastaví a následně přepína potřebné kolize. Navíc spravuje interakční komponenty a reaguje na požadavky k ním. Komponenty se dělí na aktivatory a modifikatory. + +\paragraph{AInteractableActivator} Aktivatory jsou komponenty s úrčitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro sebe jednu podinstanci každého aktivatoru zaregistrovaného v enginu. Libovolný objekt taky může obsahovat libovolný aktivator. Podrobně o aktivatorech je popsano v dokumentaci. Práce obsahuje klasický způsob detekce objektů pomoci raytracingu a castu objektu v případě nárazu paprsku. + +Navíc je k dispozici detekce objektů v zornem poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy s vynecháním většiny render pipliny a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Nasledně maska v grafickém vlakně je zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec na nalezené komponenty se promítné paprsek a zachytí objekt (viz. výsledek \Cref{fig:InteractableScreenCapture}). Původně \begin{figure} \centering -\includegraphics[width=.6\linewidth]{img/ukazka-obr02.pdf} -\caption{A figure with a plot, not entirely related to anything. If you copy the figures from anywhere, always refer to the original author, ideally by citation (if possible). In particular, this picture --- and many others, also a lot of surrounding code --- was taken from the example bachelor thesis of MFF, originally created by Martin Mareš and others.} -\label{fig:g} +\includegraphics[width=1\linewidth]{img/InteractableScreenCapture.pdf} +\caption{.} +\label{fig:InteractableScreenCapture} \end{figure} -\begin{figure} -\centering -\tikzstyle{box}=[rectangle,draw,rounded corners=0.5ex,fill=green!10] -\begin{tikzpicture}[thick,font=\sf\scriptsize] -\node[box,rotate=45] (a) {A test.}; -\node[] (b) at (4,0) {Node with no border!}; -\node[circle,draw,dashed,fill=yellow!20, text width=6em, align=center] (c) at (0,4) {Ugly yellow node.\\Is this the Sun?}; -\node[box, right=1cm of c] (d) {Math: $X=\sqrt{\frac{y}{z}}$}; -\draw[->](a) to (b); -\draw[->](a) to[bend left=30] node[midway,sloped,anchor=north] {flow flows} (c); -\draw[->>>,dotted](b) to[bend right=30] (d); -\draw[ultra thick](c) to (d); -\end{tikzpicture} -\caption{An example diagram typeset with TikZ. It is a good idea to write diagram captions in a way that guides the reader through the diagram. Explicitly name the object where the diagram viewing should ``start''. Preferably, briefly summarize the connection to the parts of the text and other diagrams or figures. (In this case, would the tenative yellow Sun be described closer in some section of the thesis? Or, would there be a figure to detail the dotted pattern of the line?)} -\label{fig:schema} -\end{figure} +\subsection{Cutscény} +\subsection{Quick Time Eventy} +\subsection{Dialogy} +\subsection{Minihry} +\subsection{Nehratelné postavy} +\subsection{API načítání nového obsahu za běhu} +\subsection{Nastavení} -\begin{algorithm} -\begin{algorithmic} -\Function{ExecuteWithHighProbability}{$A$} - \State $r \gets$ a random number between $0$ and $1$ - \State $\varepsilon \gets 0.0000000000000000000000000000000000000042$ - \If{$r\geq\varepsilon$} - \State execute $A$ \Comment{We discard the return value} - \Else - \State print: \texttt{Not today, sorry.} - \EndIf -\EndFunction -\end{algorithmic} -\caption{Algorithm that executes an action with high probability. Do not care about formal semantics in the pseudocode --- semicolons, types, correct function call parameters and similar nonsense from `realistic' languages can be safely omitted. Instead make sure that the intuition behind (and perhaps some hints about its correctness or various corner cases) can be seen as easily as possible.} -\label{alg:w} -\end{algorithm} +\section{Grafika} +\subsection{Statické objekty} +\subsection{Dynamické a procedurální objekty} +\subsection{Osvětlení, efekty, Post-Processing} +\subsection{Materiály a shadery} +\subsection{UI} +\subsection{Načítací obrazovka} +\subsection{Textové překlady} -\section{Extra typesetting hints} +\section{Audio} +\subsection{Kategorie a parametry audio assetů} +\subsection{Dynamický hudební doprovod} -Do not overuse text formatting for highlighting various important parts of your sentences. If an idea cannot be communicated without formatting, the sentence probably needs rewriting anyway. Imagine the thesis being read aloud as a podcast --- the storytellers are generally unable to speak in boldface font. - -Most importantly, do \underline{not} overuse bold text, which is designed to literally \textbf{shine from the page} to be the first thing that catches the eye of the reader. More precisely, use bold text only for `navigation' elements that need to be seen and located first, such as headings, list item leads, and figure numbers. - -Use underline only in dire necessity, such as in the previous paragraph where it was inevitable to ensure that the reader remembers to never typeset boldface text manually again. - -Use \emph{emphasis} to highlight the first occurrences of important terms that the reader should notice. The feeling the emphasis produces is, roughly, ``Oh my --- what a nicely slanted word! Surely I expect it be important for the rest of the thesis!'' - -Finally, never draw a vertical line, not even in a table or around figures, ever. Vertical lines outside of the figures are ugly. +\section{Tipy při vývoji v UE} +\paragraph{Skripty pro editor} +% Blueprunty a python +\paragraph{C++ typy a reflexe} +\paragraph{Kompilace a export projektu} diff --git a/img/InteractableScreenCapture.pdf b/img/InteractableScreenCapture.pdf new file mode 100644 index 0000000000000000000000000000000000000000..511c0f52fe90f0f50da3c4da197da75bc3a3cc1a GIT binary patch literal 37712 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 e;Am?OIaP&@h%Dx|}Z+JWYb!x`8O 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 N7N)$as;>TSTmZS(77hRa literal 0 HcmV?d00001 diff --git a/refs.bib b/refs.bib index edffd3c..9f4bf55 100644 --- a/refs.bib +++ b/refs.bib @@ -35,63 +35,3 @@ year = 2023, note = {[cit. 2025-04-01]. Dostupné z: \url{https://alfredbaudisch.com/blog/gamedev/godot-engine/godots-3d-confusing-workflow-inconsistencies-conflicting-behaviours-and-annoyances/}} } - - -@book{knuth1979tex, - title={TEX and METAFONT: New directions in typesetting}, - author={Knuth, Donald Ervin}, - year={1979}, - publisher={American Mathematical Society} -} - -@book{lamport1994latex, - title={LATEX: a document preparation system: user's guide and reference manual}, - author={Lamport, Leslie}, - year={1994}, - publisher={Addison-Wesley} -} - -@book{glasman2010science, - title={Science research writing for non-native speakers of English}, - author={Glasman-Deal, Hilary}, - year={2010}, - publisher={World Scientific} -} - -@book{sparling1989english, - title={English or Czenglish? Jak se vyhnout čechismům v angličtině}, - author={Sparling, Don}, - year={1989}, - publisher={Státní pedagogické nakladatelství} -} - -@book{tufte1990envisioning, - title={Envisioning information}, - author={Tufte, Edward R and Goeler, Nora Hillman and Benson, Richard}, - year={1990}, - publisher={Graphics press Cheshire, CT} -} - -@book{tufte1983visual, - title={Visual display of quantitative information}, - author={Tufte, Edward R}, - year={1983}, - publisher={Graphics press Cheshire, CT} -} - -@book{wilke2019fundamentals, - title={Fundamentals of Data Visualization}, - author={Wilke, Claus O}, - year={2019}, - publisher={O'Reilly Media, Inc.}, - url={https://clauswilke.com/dataviz/}, - isbn={9781492031086} -} - -@techreport{tantau2015tikz, - title={The TikZ and PGF Packages (Manual for version 3.1.8b)}, - author={Tantau, Till}, - year={2020}, - institution={Institut f{\"u}r Theoretische Informatik Universit{\"a}t zu L{\"u}beck}, - url={http://mirrors.ctan.org/graphics/pgf/base/doc/pgfmanual.pdf} -} -- 2.45.2 From 7fde56bcb8ba2121e12f312cedcf2a742a17e6e2 Mon Sep 17 00:00:00 2001 From: Oleg Petruny Date: Tue, 20 May 2025 07:50:33 +0200 Subject: [PATCH 2/8] interactables --- ch2.tex | 28 +++++++++++++++++++++------- img/InteractableSystemDiagram.pdf | Bin 0 -> 32594 bytes 2 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 img/InteractableSystemDiagram.pdf diff --git a/ch2.tex b/ch2.tex index a3c14b8..531ef9f 100644 --- a/ch2.tex +++ b/ch2.tex @@ -13,6 +13,8 @@ Přenos byl~odůvodněn převážně malou velikostí starého projektu a~taky l \end{itemize} Samozřejmě je~toho daleko víc, ale~většina ostatních vylepšení jako~například nový systémy animací postav Motion Matching\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/motion-matching-in-unreal-engine} nebo~nový fyzikální engine Chaos\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/destruction-overview} neměli vliv na~rozhodnutí. +Podrobné informace jak každá implementace funguje a jak vývojaři s ní pracovat jsou popsané v programatorské dokumentaci. + \section{Herní logika a systémy} \subsection{Architektura} Protože Unreal Engine byl na začátku vývíjen primárně pro online multiplayer hru Unreal Tournament a dnes stejně tak vyvíjen spolu s extremně velkou online multiplayer hrou Fortnite, je backend enginu dost abstraktní. @@ -26,26 +28,38 @@ Jen pro představu co obnaší klasické ziskání reference na instanci vlastn \subsection{Scény a ukládání hry} Scény resp. levely (třídy UWorld a ALevelScriptActor)\footnote{UWorld instance je přímo celý svět, který funguje jako balík metadat a kontejner pro veškeré instancované objekty v něm. ALevelScriptActor je objekt instancovaný v UWorld automaticky a obsahuje uživatelskou logiku světa.} taktéž lze získat v podobě singletonu. Základní implementace byla rozšířená pro high level mechanismus volání událostí ve scéně. Ten je za potřebí například, když hráč aktivuje spínací plošinu ve hře a ta následně otevře dveře. Narozdíl od jiných enginů, v UE objekty ve světě nemůžou referencovat jiné nezávislé objekty. Jednoduše referenci nejde přiřadít z důvodu abstrakce popsané v předchozí podsekci, protože každý objekt může mít vlastní herní svět (a nejen to). Proto pokud plošina z našeho príkladu chce otevřít dveře, musí zkusit poslat požádavek správcí herního světa, ten požadavek se nějak zpracuje a jen potom správce buď provede akci otevírání dveří samostatně nebo zavolá odpovidající funkci v instanci objektu. Pokud by byla potřeba zbavit se použití singleton architektury pro podporu paralelní existenci světů, stači předělat member funkci instance světa na statický multicast delegate\footnote{Delegate jsou obaly na C++ funkce, lambdy, funkce s reflexi a funkce z Blueprintu. Delegate může být typu single, pro uložení reference na jednou funkci, nebo multicast pro uložení dynamického seznamu funkcí. Podrobněji jsou popsané v programatorské dokumentaci.}, ke kterému se každý svět při konstrukci bude vázat. -Navíc hra byla rozšířena o implementaci obnovení hry z úložených dat. Při návrhu byla snaha udělat postup nejvíc triviální. Hru ukládá globální herní instance (třída GameInstance)\footnote{GameInstance je první objekt vytvořený enginem hned po úvodním načtení základu enginu a taky poslední objekt na destrukci při vypínání hry.} při vypínání hry a načíta taktéž při spouštění. Využit byl existující v UE systém serializace, který ukládá inventař postavy, jmeno levlu a checkpoint na něm spolu s posledním stavem levlu. Podrobný proces obnovení uložené hry a práce se serializací je popsan v programatorské dokumentaci. +Navíc hra byla rozšířena o implementaci obnovení hry z úložených dat. Při návrhu byla snaha udělat postup nejvíc triviální. Hru ukládá globální herní instance (třída GameInstance)\footnote{GameInstance je první objekt vytvořený enginem hned po úvodním načtení základu enginu a taky poslední objekt na destrukci při vypínání hry.} při vypínání hry a načíta taktéž při spouštění. Využit byl existující v UE systém serializace, který ukládá inventař postavy, jmeno levlu a checkpoint na něm spolu s posledním stavem levlu. \subsection{Interakce} Interakce jsou často řešeny pomoci architektury interface tříd\footnote{Interface třídy jsou konkurenčním přístupem komponentní architektuře. Interface je běžná praktika v OOP, která funguje jako domluva, že objekt bude obsahovat určité member funkce. Komponentny jsou samostatné určité podinstance objektu. Komponentní přístup se osvědčuje jako intuitivnější a více flexibilní zatímco Interface přístup při vývoji her je spíše nepřijemné vynucení ze světu OOP.} především kvůli populárním návodům vyzdvihující tuto metodu jako nejlepší ještě z doby, kdy jen vzníkal Unreal Engine 4. Nevýhodou je potřeba v obsáhlem a repetetivním nastavení každeho objektu, který chceme zapojit do mechaniky interakce. -Byla navržena celá třída pro kompletní spravu každého intekačního objektu a komponent. Objekt dědicí třídu AInteractable při instancování samostatně nastaví a následně přepína potřebné kolize. Navíc spravuje interakční komponenty a reaguje na požadavky k ním. Komponenty se dělí na aktivatory a modifikatory. - -\paragraph{AInteractableActivator} Aktivatory jsou komponenty s úrčitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro sebe jednu podinstanci každého aktivatoru zaregistrovaného v enginu. Libovolný objekt taky může obsahovat libovolný aktivator. Podrobně o aktivatorech je popsano v dokumentaci. Práce obsahuje klasický způsob detekce objektů pomoci raytracingu a castu objektu v případě nárazu paprsku. - -Navíc je k dispozici detekce objektů v zornem poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy s vynecháním většiny render pipliny a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Nasledně maska v grafickém vlakně je zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec na nalezené komponenty se promítné paprsek a zachytí objekt (viz. výsledek \Cref{fig:InteractableScreenCapture}). Původně +Byl navržen kompletní systém pro spravu každého interačního objektu a komponent (viz.\Cref{fig:InteractableSystemDiagram}). Objekt dědicí třídu AInteractable při instancování samostatně nastaví a následně přepína potřebné kolize. Navíc spravuje interakční komponenty a reaguje na požadavky k ním. Komponenty se dělí na aktivatory a modifikatory. \begin{figure} \centering \includegraphics[width=1\linewidth]{img/InteractableScreenCapture.pdf} -\caption{.} +\caption{Debug náhled aktivace interakčních objektů v zornem poli hráče.} \label{fig:InteractableScreenCapture} \end{figure} +\paragraph{AInteractableActivator} Aktivátory jsou komponenty s úrčitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro sebe jednu podinstanci každého aktivatoru zaregistrovaného v enginu. Libovolný objekt taky může obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci raytracingu a castu objektu v případě nárazu paprsku. + +Navíc je k dispozici detekce objektů v zornem poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy s vynecháním většiny render pipliny a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Nasledně maska v grafickém vlakně je zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec do středu nalezených komponent se promítné paprsek a zachytí objekty (viz. \Cref{fig:InteractableScreenCapture}). Původně bylo předpokládáno využití HLSL compute shaderu pro vyhledání komponent v textuře, ale z časových důvodu jsem nedodělal přenos dat mezi CPU a GPU, jelikož dohledat dokumentaci o použití shaderu je dost náročné. Proto aktuální vyhledávání komponent je spouštěno na procesoru a v renderovacím vlakně na moderních zařízeních trvá pět až deset milisekund. + +\begin{figure} +\centering +\includegraphics[width=1\linewidth]{img/InteractableScreenCapture.pdf} +\caption{Debug náhled aktivace interakčních objektů v zornem poli hráče.} +\label{fig:InteractableScreenCapture} +\end{figure} + +\paragraph{AInteractableModificator} Modifikátory jsou komponenty s úrčitou logikou libovolné modifikace objektu, ve kterém jsou instancovány. Tyto komponenty mohou obsahovat pouze objekty označené jako interakční (dědí třídu interákčního objektu). Aktuální implementace nabízí modifikátory pro aktivaci nějaké události pomoci dosahu ,,ruky'' nebo zahlednutím ,,očí'' hráče, pohyb a rotace předmětu v prostoru, ukládání předmětu do inventáře hráče. Z časových důvodu byla zamítnuta implementace modifikace meshe objektu, která by využivala nového rozhraní Chaos. + +Právě u modifikátorů nejvíc přispěl nový system vstupu pro Unreal Engine 5. Modifikatory využívájí možnosti přidávání resp. odstranění kontextů vstupu za běhu. \subsection{Cutscény} + + \subsection{Quick Time Eventy} \subsection{Dialogy} \subsection{Minihry} diff --git a/img/InteractableSystemDiagram.pdf b/img/InteractableSystemDiagram.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3e3cf9c348694628b73665a4d6f7f9e0c2b03412 GIT binary patch literal 32594 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 literal 0 HcmV?d00001 -- 2.45.2 From b1bee5499ce9d3436329a75f8ddff522d42cdafc Mon Sep 17 00:00:00 2001 From: Oleg Petruny Date: Thu, 22 May 2025 22:54:59 +0200 Subject: [PATCH 3/8] minigames --- ch1.tex | 10 ++++----- ch2.tex | 61 ++++++++++++++++++++++++++++++++++++++++++++++--------- intro.tex | 12 +++++------ 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/ch1.tex b/ch1.tex index e0e1244..6ded212 100644 --- a/ch1.tex +++ b/ch1.tex @@ -45,7 +45,7 @@ Při jejich návrhu je~třeba zvážit: jak~mechaniky podporují zvolený žánr Při~výběru platformy je~nutné zvážit technická omezení a~očekávání hráčů na~dané platformě. Například mobilní hry často využívají dotykové ovládání a~krátké herní smyčky, zatímco hry pro~PC a~konzole mohou nabídnout komplexnější mechaniky a~delší herní dobu. -\paragraph{Kopírování} Kopírování cizích a~vlastních nápadů je~nedílnou součástí úspěšného vývoje. Hodně se~vyplatí mít přehled ve~vybraném žánru a~mechanikách. Není nic špatného učit~se na~chybách a~úspěších jiných her. Důležité ale~je si~pamatovat, že~neexistuje deterministický vzorec, jak~vytvořit dokonalou kombinaci příběhů, žánrů a~mechanik tak, aby~hra získala oblibu hráčů. +\paragraph{Kopírování} Kopírování cizích a~vlastních nápadů je~nedílnou součástí úspěšného vývoje. Vyplatí~se mít přehled ve~vybraném žánru a~mechanikách. Není nic špatného učit~se na~chybách a~úspěších jiných her. Důležité ale~je si~pamatovat, že~neexistuje deterministický vzorec, jak~vytvořit dokonalou kombinaci příběhů, žánrů a~mechanik tak, aby~hra získala oblibu hráčů. \newpage \paragraph{Minihry} Skvělou metodou, jak~rozptýlit hráče od~monotonie herního cyklu jsou~minihry. Přitom je~lze aplikovat kdykoliv. Minihry většinou buď poskytují odměnu, anebo slouží k~relaxaci mezi náročnějšími segmenty hry. Důležité~je, aby~jejich design byl v~souladu s~celkovým stylem hry a~nepůsobil rušivě. Například rytmická minihra ve~fantasy RPG může být zajímavým doplňkem, ale~v~realistické hororové hře by~působila nepatřičně. @@ -81,7 +81,7 @@ Pokud ignorujeme chybějící dokumentaci, je~Unreal Engine stejný engine jako~ Aktuálně UE~se~orientuje na~3D~hry převážně s~grafikou vysoké kvality a~stejně jako Unity podporuje většinu aktuálních platforem. Taky se~skvěle hodí pro~tvorbu filmu, motion design a~realtime simulace. Navíc díky technologiím jako~Nanite a~Lumen začína přebírat trh architektonických rendereru. -\paragraph{CryEngine\protect\footnote{https://www.cryengine.com/}} CryEngine je hodně podobný Unreal Enginu za~výjimkou toho, že~se~specialuzuje jen na~vývoj her. Taky~již~není tolik univerzalní, dokumentace je~ještě míň, komunita je~velmi malá a~přívětivost je snad nejhorší možná. Je~to~dost úzce specializovaný engine, který potřebuje silné odborníky k~jeho ovládání. +\paragraph{CryEngine\protect\footnote{https://www.cryengine.com/}} CryEngine je velmi podobný Unreal Enginu za~výjimkou toho, že~se~specialuzuje jen na~vývoj her. Taky~již~není tolik univerzalní, dokumentace je~ještě míň, komunita je~velmi malá a~přívětivost je snad nejhorší možná. Je~to~dost úzce specializovaný engine, který potřebuje silné odborníky k~jeho ovládání. Všechny známé hry na~CE~jsou zaměřené na~velmi propracovanou grafiku a~částo hry s~mechanikami boje nebo~střelby od~první osoby. Nedokázal jsem dohledat žádné 2D~hry nebo~indie, což~je~pochopitelné. 2D~hry není specializace CryEngine a~tvořit indie na~tomto enginu je~neefektivní až~nepřínosné. Taky~často vývojáři her na~CE se~přiznávají, že~je~nutné přizpůsobovat zdrojový kód enginu podle vlastních potřeb, což~ještě~víc odrazuje začátečníky. @@ -96,7 +96,7 @@ Samozřejmě to~přináší nějaký overhead, hlavně když se~jedná o~práci Největší nevýhodou je~překvapivě nepřehlednost kódu, která~nejčastěji značí špatný programátorský návrh nebo lenost autora. Totiž velký počet vizuálních bloků a~jejich propojení není možné umístit přehledně na~jednu obrazovku. To~potom vyúsťuje v~nepřehledný mix různých logických částí, které~jsou dost obtížné na~orientaci a~údržbu. Napravit~to nejde ani~rozdělením jedné funkce do~více funkcí v~samotném Blueprintu. Nepřehledný mix propojení bloků se~pouze změní na~nepřehledný mix oken s~ruzným kódem. Správný postup v~tomto případě je~ručně konvertovat logiku z~Blueprintu do~C++. -\paragraph{C++} Práce s~C++ v~Unreal Engine je~hodně podobná práci s~velkými frameworky, například Qt. Použití čistého C++ je~zcela povolené, ale~takový kód potom nelze použít v~blueprintech a~tedy i~editoru. Unreal Engine proto definuje speciální makra jako~třeba UCLASS a~UFUNCTION pro~možnost integrování kódu buď~přímo do~blueprintu nebo~aspoň systému reflexe. Makra se~potom zpracovávají ne~macro preprocessor, ale~Unreal Header Tool nebo~Unreal Build Tool, které~slouží jako~generatory kódu. Generatory potom sami generují potřebné funkce a~proměnné pro~systém reflexe a~editor. +\paragraph{C++} Práce s~C++ v~Unreal Engine je~zkutečně podobná práci s~velkými frameworky, například Qt. Použití čistého C++ je~zcela povolené, ale~takový kód potom nelze použít v~blueprintech a~tedy i~editoru. Unreal Engine proto definuje speciální makra jako~třeba UCLASS a~UFUNCTION pro~možnost integrování kódu buď~přímo do~blueprintu nebo~aspoň systému reflexe. Makra se~potom zpracovávají ne~macro preprocessor, ale~Unreal Header Tool nebo~Unreal Build Tool, které~slouží jako~generatory kódu. Generatory potom sami generují potřebné funkce a~proměnné pro~systém reflexe a~editor. V~C++ a~navíc s~otevřeným kódem celého enginu, má~vývojář plnou kontrolu nad~během programu nebo~jeho debugováním. Problém je~ale~použití assetů z~editoru nebo~reference objektů v~herním světě. Jsou~možnosti jak~to~obejít, například statické načtení assetu z~registru pomoci konstantní plné cesty k~assetu nebo~přeiterovat všechny objekty ve~světě. Editor samořejmě není schopen takové reference udržovat v~případě přemístění assetu a~časté iterování přes všechny objekty je~citelná zátěž. Proto ve~většíně případů je~potřeba zpřístupnít celou třídu do~Blueprintu a~v~editoru rovněž vytvořit Blueprint podtřídu, která~bude pouze přiřazovat potřebné reference. @@ -154,10 +154,10 @@ Zároveň takova metoda není schopná poskytnout některé grafické techniky. Postprocessing je~technika dodatečného zpracování vyrenderovaného obrazu. Většinou se~jedná o~triviální manipulaci barev, ale~lze~tady dělat i~spoustu dizajnových a~technickych triků. Např.~různé glitch efekty, efekty tepelného/nočního vidění~atd. získané pomocí procedurální nebo~statické modulace obrazu. V~deffered rendereru navíc lze~používat screen space techniky, které~používají vyrenderovaný snímek k~rychlé tvorbě odrazů (SSR - Screen Space Reflections) nebo~zastínění (SSAO - Screen Space Ambient Occlusion). \subsection*{- 2D} -Je~více způsobů práce s~2D~grafikou, avšak udělat čistě 2D~hru bude problematické. V~oficiální nabídce je~rozhraní Paper~2D\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/paper-2d-overview-in-unreal-engine}, které~vývoj 2D~hry sice zjednodušuje, ale~přesto má pouze základní prvky 2D~enginu. Místy chybí optimalizace, protože soubory v~editoru jsou neracionálně velké a~ve~větších projektech engine začíná být hodně náročný na~hardware. Proto se~pro~2D~hry Unreal~Engine moc nehodí a~je~doporučené rozhlédnout~se po~konkurenčních enginech. +Je~více způsobů práce s~2D~grafikou, avšak udělat čistě 2D~hru bude problematické. V~oficiální nabídce je~rozhraní Paper~2D\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/paper-2d-overview-in-unreal-engine}, které~vývoj 2D~hry sice zjednodušuje, ale~přesto má pouze základní prvky 2D~enginu. Místy chybí optimalizace, protože soubory v~editoru jsou neracionálně velké a~ve~větších projektech engine začíná být náročný na~hardware. Proto se~pro~2D~hry Unreal~Engine moc nehodí a~je~doporučené rozhlédnout~se po~konkurenčních enginech. \newpage -\paragraph{UI} UI~lze programovat v~C++ pomoci třídy Slate nebo~přímo pomoci Blueprintů v~editoru. Programování se~Slate je~hodně nízkoúrovňové tedy stejné jako~slepé programování okenní windows aplikace pomocí Win32~API.Mnohem pohodlnější je práce v~editoru, kde~rovnou lze vidět výsledek. Celkově tvorba UI v~Unreal Engine je~jedna z~jeho nejsilnějších stránek mezi konkurenty, i~když se~o~ní~moc nemluví. +\paragraph{UI} UI~lze programovat v~C++ pomoci třídy Slate nebo~přímo pomoci Blueprintů v~editoru. V~Slate prostředí se~programuje nízkoúrovňové tedy je stejné jako programování okenní windows aplikace pomocí Win32~API bez~přímé vizualizace. Mnohem pohodlnější je práce v~editoru, kde~rovnou lze vidět výsledek. Celkově tvorba UI v~Unreal Engine je~jedna z~jeho nejsilnějších stránek mezi konkurenty, i~když se~o~ní~moc nemluví. Pro~zobrazení UI~nebo jiných 2D~prvků ve~3D bylo vždy možné vytvořit klasickou plochu s~texturou/materiálem nebo~renderovat text do~3D~světa. Samotné renderování textu ve~3D je~implementováno pomoci generace 3D~objektu z~vektorového fontu, což~je často výkonnostně přehnané řešení neboť mesh texty jsou již součástí hotového modelu. Ještě lze použít renderování textu z~předgenerované průhledné rastrové textury fontu. Poslední způsob je~dost rychlý na~implementaci a~výkonnostně nejlepší pokud výstup nemusí být nějak extra kvalitní. Nejlepší možností je~generovat text vektorově v~UI a~následně ho promítnout do~3D~světa, což~je umožněno pomoci již~zmíněných UI~tříd. diff --git a/ch2.tex b/ch2.tex index 531ef9f..c427c03 100644 --- a/ch2.tex +++ b/ch2.tex @@ -6,7 +6,7 @@ Přenos starého projektu na~další major verzi~UE nebyl nijak problematický, Přenos byl~odůvodněn převážně malou velikostí starého projektu a~taky lákavou nabídkou nových technologií zejména Nanite a~Lumen. Navíc pátá verze Unrealu -- přesněji verze~5.5 -- přinesla značná vylepšení jako: \begin{itemize} \item Nový systém zpracování vstupu Enhanced Input\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/enhanced-input-in-unreal-engine}. Místy má~příliš mnoho objektové abstrakce, ale~rozhodně velký krok vpřed. Umožňuje snadné dynamické přepínání různých sad ovládání (multiplayer hry, gameplay/menu), dynamické modifikátory a~spouštěče vstupu (změna senzitivity, víceklik, podržení tlačítka určitou dobu), podpora vstupu více než~jedné periferie naraz a~podpora přeřazení vstupu (např.~změna tlačítka odpovídající za~skok herní postavy). Předtím tohle a~spoustu dalšího se~muselo naprogramovat ručně. -\item Podporu vektorové grafiky v~UI. Veškeré staré UI~elementy byly~tvořeny pomocí základních vektorových obdélníkových tvarů práve proto, aby~se~vyhnulo použití rastrové grafiky, která~je~hodně závislá na~rozlišení. Aktuálně všechny UI~elementy jsou tvořeny ještě starou metodou, pro~udržení konzistentního vzhledu. Dnes bychom určitě využili této~možnosti. +\item Podporu vektorové grafiky v~UI. Veškeré staré UI~elementy byly~tvořeny pomocí základních vektorových obdélníkových tvarů práve proto, aby~se~vyhnulo použití rastrové grafiky, která~je~velmi závislá na~rozlišení. Aktuálně všechny UI~elementy jsou tvořeny ještě starou metodou, pro~udržení konzistentního vzhledu. Dnes bychom určitě využili této~možnosti. \item Přepracované vykreslování textů -- rychlejší vykreslování a~efektivnější využití paměti. \item MetaSound\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/metasounds-the-next-generation-sound-sources-in-unreal-engine} pro~přehrávaní nebo procedurální generování zvuků, který~nahrazuje starou třídu~Cue. De-facto se~jedná o~Digital Signal Processing (DSP) grafový engine a~editor. Bohůžel jsem nestihl tento nastroj využit v~práci. \item World partition systém\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/world-partition-in-unreal-engine} který~dokáže automaticky rozdělit jeden velký svět na~streamovací kousky a~propojit sdílení dat mezi nimi i~při multiplayer hře přes internet. Taky není využit v~této práci. @@ -15,7 +15,8 @@ Samozřejmě je~toho daleko víc, ale~většina ostatních vylepšení jako~nap Podrobné informace jak každá implementace funguje a jak vývojaři s ní pracovat jsou popsané v programatorské dokumentaci. -\section{Herní logika a systémy} +\section{Herní logika, systémy, mechaniky} +\label{sec:systemsAndMechanics} \subsection{Architektura} Protože Unreal Engine byl na začátku vývíjen primárně pro online multiplayer hru Unreal Tournament a dnes stejně tak vyvíjen spolu s extremně velkou online multiplayer hrou Fortnite, je backend enginu dost abstraktní. @@ -23,7 +24,7 @@ Například tak každý svět musí obsahovat vlastní game mode (třída AGameM Protože tento projekt je zaměřen pro hru jednoho hráče, práce je často programováná navzdory ustaleným UE C++ guidelines pro pohodlí a rychlost vývoje. Proto často třídy managerů systémů, game modů, instance hráče a další jsou na architektuře singletonu. Šetří od tří do desetí různých volání getteru, castu reflexe, vyhledávání v hash tabulkach a iteraci polí v každém místě použití. Přesto nemá žádný vliv na stabilitu programu a dokonce šetří výkonem zařízení. -Jen pro představu co obnaší klasické ziskání reference na instanci vlastní třídy hráče na pozadí. GetGameMode() \textrightarrow GetPlayerController(index) \textrightarrow GetPlayerPawn() \textrightarrow Cast(). A blueprinty disponují zkrácenou verzí GetPlayerPawn(index) \textrightarrow Cast(). V případě C++ je potřeba navíc ověřovat zda nějaké z volání nevratílo nullptr. +Jen pro představu co obnaší klasické ziskání reference na instanci vlastní třídy hráče na pozadí. GetGameMode()\textrightarrow GetPlayerController(index)\textrightarrow GetPlayerPawn()\textrightarrow Cast(). A blueprinty disponují zkrácenou verzí GetPlayerPawn(index)\textrightarrow Cast(). V případě C++ je potřeba navíc ověřovat zda nějaké z volání nevratílo nullptr. \subsection{Scény a ukládání hry} Scény resp. levely (třídy UWorld a ALevelScriptActor)\footnote{UWorld instance je přímo celý svět, který funguje jako balík metadat a kontejner pro veškeré instancované objekty v něm. ALevelScriptActor je objekt instancovaný v UWorld automaticky a obsahuje uživatelskou logiku světa.} taktéž lze získat v podobě singletonu. Základní implementace byla rozšířená pro high level mechanismus volání událostí ve scéně. Ten je za potřebí například, když hráč aktivuje spínací plošinu ve hře a ta následně otevře dveře. Narozdíl od jiných enginů, v UE objekty ve světě nemůžou referencovat jiné nezávislé objekty. Jednoduše referenci nejde přiřadít z důvodu abstrakce popsané v předchozí podsekci, protože každý objekt může mít vlastní herní svět (a nejen to). Proto pokud plošina z našeho príkladu chce otevřít dveře, musí zkusit poslat požádavek správcí herního světa, ten požadavek se nějak zpracuje a jen potom správce buď provede akci otevírání dveří samostatně nebo zavolá odpovidající funkci v instanci objektu. Pokud by byla potřeba zbavit se použití singleton architektury pro podporu paralelní existenci světů, stači předělat member funkci instance světa na statický multicast delegate\footnote{Delegate jsou obaly na C++ funkce, lambdy, funkce s reflexi a funkce z Blueprintu. Delegate může být typu single, pro uložení reference na jednou funkci, nebo multicast pro uložení dynamického seznamu funkcí. Podrobněji jsou popsané v programatorské dokumentaci.}, ke kterému se každý svět při konstrukci bude vázat. @@ -37,14 +38,15 @@ Byl navržen kompletní systém pro spravu každého interačního objektu a kom \begin{figure} \centering -\includegraphics[width=1\linewidth]{img/InteractableScreenCapture.pdf} +\includegraphics[width=1\linewidth]{img/InteractableSystemDiagram.pdf} \caption{Debug náhled aktivace interakčních objektů v zornem poli hráče.} -\label{fig:InteractableScreenCapture} +\label{fig:InteractableSystemDiagram} \end{figure} +\newpage \paragraph{AInteractableActivator} Aktivátory jsou komponenty s úrčitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro sebe jednu podinstanci každého aktivatoru zaregistrovaného v enginu. Libovolný objekt taky může obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci raytracingu a castu objektu v případě nárazu paprsku. -Navíc je k dispozici detekce objektů v zornem poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy s vynecháním většiny render pipliny a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Nasledně maska v grafickém vlakně je zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec do středu nalezených komponent se promítné paprsek a zachytí objekty (viz. \Cref{fig:InteractableScreenCapture}). Původně bylo předpokládáno využití HLSL compute shaderu pro vyhledání komponent v textuře, ale z časových důvodu jsem nedodělal přenos dat mezi CPU a GPU, jelikož dohledat dokumentaci o použití shaderu je dost náročné. Proto aktuální vyhledávání komponent je spouštěno na procesoru a v renderovacím vlakně na moderních zařízeních trvá pět až deset milisekund. +Navíc je k dispozici detekce objektů v zornem poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy s vynecháním většiny render pipliny a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Nasledně maska v grafickém vlakně je zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec do středu nalezených komponent se promítné paprsek a zachytí objekty (viz. \Cref{fig:InteractableScreenCapture}). Původně bylo předpokládáno využití HLSL compute shaderu pro vyhledání komponent v textuře, ale z časových důvodu jsem nedodělal přenos dat mezi CPU a GPU, jelikož dohledat dokumentaci o použití shaderu je dost náročné. Proto aktuální vyhledávání komponent je spouštěno na procesoru a v renderovacím vlakně na moderních zařízeních trvá pět až deset milisekund. V praxi se takový úkol řeší náhodným promítáním velkého množství paprsků nebo velkým hitboxem napodobující tvar frustrumu kamery. Taková řešení jsou sice rychlá na implementaci, ale začínají být nekvalitní až nepoužítelné pro trochu větší vzdalenosti než ,,pár metrů''. Navíc zahrnují časté počítání nárazů na velké množství objektů, čímž výkonostně nejsou o nic lepší naimplementováné metody v této práci. \begin{figure} \centering @@ -55,19 +57,52 @@ Navíc je k dispozici detekce objektů v zornem poli hráče. Ta funguje na zpom \paragraph{AInteractableModificator} Modifikátory jsou komponenty s úrčitou logikou libovolné modifikace objektu, ve kterém jsou instancovány. Tyto komponenty mohou obsahovat pouze objekty označené jako interakční (dědí třídu interákčního objektu). Aktuální implementace nabízí modifikátory pro aktivaci nějaké události pomoci dosahu ,,ruky'' nebo zahlednutím ,,očí'' hráče, pohyb a rotace předmětu v prostoru, ukládání předmětu do inventáře hráče. Z časových důvodu byla zamítnuta implementace modifikace meshe objektu, která by využivala nového rozhraní Chaos. -Právě u modifikátorů nejvíc přispěl nový system vstupu pro Unreal Engine 5. Modifikatory využívájí možnosti přidávání resp. odstranění kontextů vstupu za běhu. +Právě u modifikátorů nejvíc přispěl nový system zpracování vstupu pro Unreal Engine 5. Modifikatory využívájí možnosti přidávání resp. odstranění kontextů vstupu za běhu. -\subsection{Cutscény} +\subsection{Cutscény a Quick Time Eventy} +Unreal je slavný, jak dobře lze v něm animovat scénu. Rozhraní a editor systému Sequencer je pohodlné a bohaté na funkcionality. Bohůžel veškeré skvěle věci končí mimo editor, jelikož chybí pohodlná funkcionalita režije různých souborů animací (stejná poznámka se vztahuje i k UI animacím). Proto byl dodán systém UCutsceneManager implementující frontu animací spolu s uživatelským rozhraním přeskočení animace. +Pro přiblížení problému, vyjmenuji časté potřeby, které vzníkají při běžném použití animací. +\begin{itemize} +\item Zároveň spuštěné animace bojují o vlastnictví objektů, +\item animace nelze přetáčet, +\item u animace nelze zjistit zpětně jestli byla přehrana do konce, pouze přivázat volaní funkce po ukončení, +\item animace umí říct jestli je někdé přehráváná, ale neřekné kde. +\end{itemize} + +Převážně pro použití v animacích je naimplementováná fronta nečekaných událostí, což jsou interaktivní UI elementy na obrazovce hráče. V herním průmyslu jsou využívány zejména v cutscénach, pro ,,hlubší ponoření'' hráče do děje. Klasickými příklady jsou animace šplhání herní postavy po nějaké překážce nebo kinematografický souboj mezi postavy. Při takových údalostech lze přibližit hráče dynamické a napínavé situaci pomoci klikání na stejně dynamické UI elementy na obrazovce v souladu s pohybem herní postavy. V V praxi se objevují i pokročílejši varianty napodobení děje pohybem myší a joystickem nebo gyroscopem ovládače. -\subsection{Quick Time Eventy} \subsection{Dialogy} +V enginu je zavádějící implementace pokročílého dialogového systému. Má stejný problém jako animace. Dialogy jsou neintuitivní pro tvorbu a použití, vyžadují časté repetetivní kopírování stejných parametrů do souborů s linky dialogů a dohromady celý tento systém má málo dokumentace. Navíc ,,dialogy'' v tomto systému jsou pouze samostatné věty, které musí přehravat nějaký zvuk a mohou se vybírát v zavislostí na omezeném kontextu typu "kdo na koho mluví". O nepoužitelností takového rozhrání napovídají návody na tvorbu vlastních systému nebo vývojařský obchod plný pluginů implementujících tento systém lépe. + +Protože kvalitná řešení jsou placená a ty zdarma jsou často nevyhovující kvality, byla navržená vlastní implementace. Jedna se o frontu, která umí přehravat celé tabůlky dialogových vět. Celý dialog nebo množinu vět společného kontextu lze pohodlně zapsat do tabulkového formátu přímo v editoru. Jedná věta obsahuje id, text, dobu přehrání nebo zvukovou stopu. Ve výsledku lze do fronty zařadit několik tabůlek, které se mohou přehrávat sekvenčně od n-té věty do poslední, jednu náhodnou větu nebo přesně jednu větu podle id. + \subsection{Minihry} +Gameplay druhého levlu je celý složen z miniher. V moment vydání -- práce obsahuje pouze pět hlavních pro gamedesign miniher v podobě ,,proof of concept''. Z časových důvodů zbylé vedlejší minihry nebyly implementováné. Každá z hlavních miniher je parodie na již existujicí známe hry. + +Minihry jsou samostatné objekty, které manager scény startuje nebo vypíná. Po nastartování minihra sama přepné kontext ovládání na sebe a řídí vlastní stav. Jestli minihra nebyla ukončená dočasně ale dohráná, potom vráti ovládání hráči a spolu s tím výsledek a skóre. + +Dostupné zkušební implementace: +\begin{itemize} +\item Parodie flash hry Age of War. V pozadí uklízení knížek dějepisu jsou umístěny základny hráče a soupeře (umělé inteligence). Hráč ovládá minihru pomici UI, kde nakupuje jednotky typu: pěšák, střelec a tank. Peníze získáva za zničení nepřátelských jednotek. Vyhrává soupeř, který dokázal přejít přes jednotky nepřátele až k základně a zdemolovat ji. V aktuální implementaci chybí vylepšení éry jednotek, zvuky, grafické efekty, modely a animace jednotek. Přesto lze vyzkoušet nakup a jejich ,,souboj'' jednotek. +\item Parodie na mobilní hru Subway Surfers. Aktuálně má minihra nevhodné pozadí, neobsahuje audio a grafické prvky. Podle návrh hráč nahání veverku v lese, která mu ukradla část důležitého pro příběh předmětu. Pomoci pohybů nahoru, dolu, doleva a napravo hráč uhýba objevujicím se lesním překážkam. Překážková dráha má šířku třech běžicích pruhů a překážky mohou vyžadovat přeskočení, sklouznutí nebo úlpý uhyb do strany. +\item Podoba počítačové rytmické hry Osu!. Hráč má za úkol klikat na objevující se na obloze hvězdičky v souladu s rytmem hudby (v pořadí ve kterém se objevili). Aktuálně ,,hvězdičky'' jsou v podobě čtverečků, ale hudba je zcela hotova. Již teď lze vyzkoušet klikání pro prvních 10 sekund doprovodu. +\item Podoba mobilní hry Crossy Road. Hráč pomoci pohybu dopředu, dozadu, doleva a dolu potřebuje posouvat se vpřed po překážkové dráze. Dráha je složená z pěti průhu kolmých ke kameře, a každý průh je složen z dvanáctí políček, na které může stoupnout hráč. Náhodně na krajích pruhu se objevují divoká zvířata, laviny a silný vítr (v originální hře to jsou auta na silnici), které se posouvají k opačnemu kraji přes všechná políčka a mohou ukončit běh hráče. Podle návrhu se bude hra odehrávát v ledovém prostředí, kde hráč bude procházet sněžnou krajinou. Audiovizuální prvky opět nejsouk dispozici, ale již teď teď lze vyzkoušet posun dopředu a uhyb překážkam. +\item Jednoduchá minihra rybolovu (více podob napříč různymi hry). Hráč má na obrazovce svislý obdelníkový indikátor znázorňující vodní hlubinu. Podél indikátoru se náhodně pohybuje obrázek rybičky, kterou hráč musí udržovat v malé pohyblivé zóně. Zóna se pohybuje automaticky dolu nebo nahoru držením tlačítká myší (rychlost pohybu není lineární ale kvadratická). V implementaci chybi zvukové assety a vyvaženost složitosti, ale je k dispozici kompletní UI a mechanika lovu. +\end{itemize} + \subsection{Nehratelné postavy} -\subsection{API načítání nového obsahu za běhu} + + \subsection{Nastavení} + +\section{Návrh tvorby generativního obsahu a jeho načítání za běhu} +\label{sec:contentGenerationAndIntegration} +Nabízí se online tabulká skóre na základní a vygenerováné minihry. + \section{Grafika} +\label{sec:graphics} \subsection{Statické objekty} \subsection{Dynamické a procedurální objekty} \subsection{Osvětlení, efekty, Post-Processing} @@ -77,11 +112,17 @@ Právě u modifikátorů nejvíc přispěl nový system vstupu pro Unreal Engine \subsection{Textové překlady} \section{Audio} +\label{sec:audio} \subsection{Kategorie a parametry audio assetů} \subsection{Dynamický hudební doprovod} \section{Tipy při vývoji v UE} +\label{sec:UETips} + \paragraph{Skripty pro editor} % Blueprunty a python \paragraph{C++ typy a reflexe} \paragraph{Kompilace a export projektu} + +\section{Co se nestihlo nebo změnilo} + diff --git a/intro.tex b/intro.tex index af336c6..ce21058 100644 --- a/intro.tex +++ b/intro.tex @@ -9,11 +9,11 @@ Přestože celé řešení zdánlivě funguje, spoléhá~se na~ochotu samotných Tato práce se~zaměřuje na~tvorbu hry a~návrh systému který~umožní výše popsaný lidský faktor a~nedostatky eliminovat. Přináší tak příběh rozdělený na~pět žánrově odlišných úrovní a~zavádí high-level API pro~Unreal~Engine na~stahování a~načítání obsahu do~hry přímo za~běhu. Specifický příběh snižuje ludonarativní disonanci\footnote{Souvislost resp.~logické propojení herního světa, příběhu a~gameplaye.} při vzníku nového obsahu ve~hře a~přítomnost více žánrů umožňuje otestovat, jestli toto řešení v~každém z~nich dostatečně funguje. Zároveň tato práce přenechává samotné generování obsahu pomoci AI modelů a~testování výsledků až~jako další rozšíření. Čili primárně se~zaměřuje na~kostru samotné hry, aby~spotom byl prostor, kam nový obsah začlenit. \pagebreak -Hlavními tématy, na~které se~práce zaměřuje, jsou: +Hlavní témata na~které se~práce zaměřuje: \begin{itemize} -\item Práce s~Unreal~Engine, jeho reálná omezení, obcházení/vyrovnání se~s~těmito omezení a~tipy. -\item Postupy tvorby různých druhů grafiky pro~3D hry zejména v~UE. -\item Postupy tvorby zvuků a~hudby pro~hry. -\item Ukázkové příklady tvorby herních systémů a~mechanik pro~Unreal~Engine. -\item Tvorba generativního obsahu a~jeho načítaní na~Unreal Engine. +\item Ukázkové příklady tvorby herních systémů a~mechanik pro~Unreal~Engine (\cref{sec:systemsAndMechanics}). +\item Návrh tvorby generativního obsahu a~jeho načítaní v~Unreal Engine (\cref{sec:contentGenerationAndIntegration}). +\item Postupy tvorby různé grafiky v~UE (\cref{sec:graphics}). +\item Postupy tvorby zvuků a~hudby pro~hry (\cref{sec:audio}). +\item Práce s~Unreal~Engine, jeho reálná omezení, obcházení/vyrovnání se~s~těmito omezení a~tipy (\cref{sec:UETips}). \end{itemize} -- 2.45.2 From e2688674ded566380bd5f9c3a5dec0e35844683d Mon Sep 17 00:00:00 2001 From: Oleg Petruny Date: Mon, 26 May 2025 12:23:39 +0200 Subject: [PATCH 4/8] half --- ch2.tex | 135 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 103 insertions(+), 32 deletions(-) diff --git a/ch2.tex b/ch2.tex index c427c03..303a937 100644 --- a/ch2.tex +++ b/ch2.tex @@ -1,7 +1,7 @@ \chapter{Vývoj hry} \label{chap:main} -Přenos starého projektu na~další major verzi~UE nebyl nijak problematický, dokonce v~editoru je~možnost rychlého exportu assetů do~jiného projektu. Rozhodně tomu pomohlo, že~se~C++~kód nepřenášel, ale~napsal znovu. UE~citelně rozšířil seznam dostupných tříd, ale~zaroveň některé jsou~již deprecated nebo~odstraněny úplně. Tak~například byly~odstraněny třídy Matinee (bývalý format animací pohybu objektů) pro~podporu novejší třídy Sequencer nebo~starý PhysX rozhraní (Fyzický engine), které~je nahrazeno systémem Chaos. Přesto se~něco pokazilo při~exportu objektů s~dynamickou fyzikou (závěsy, které~reagují na~simulaci větru, se~museli předělat). +Přenos starého projektu na~další major verzi~UE nebyl nijak problematický, dokonce v~editoru je~možnost rychlého exportu assetů do~jiného projektu. Rozhodně tomu pomohlo, že~se~C++~kód nepřenášel, ale~napsal znovu. UE~citelně rozšířil seznam dostupných tříd, přitom~některé jsou~již deprecated nebo~odstráněny úplně. Tak~například byly~odstraněny třídy Matinee (bývalý format a editor animací objektů) pro~podporu novejší třídy Sequencer nebo~starý PhysX\footnote{PhysX je open-source fyzický engine s hardwarovou akcelerací pro grafiky s CUDA architekturou (GPU společnosti NVIDIA).} rozhraní, které~je nahrazeno systémem Chaos. Přesto se~něco pokazilo při~exportu objektů s~dynamickou fyzikou (závěsy, které~reagují na~simulaci větru, se~museli předělat). Přenos byl~odůvodněn převážně malou velikostí starého projektu a~taky lákavou nabídkou nových technologií zejména Nanite a~Lumen. Navíc pátá verze Unrealu -- přesněji verze~5.5 -- přinesla značná vylepšení jako: \begin{itemize} @@ -13,98 +13,168 @@ Přenos byl~odůvodněn převážně malou velikostí starého projektu a~taky l \end{itemize} Samozřejmě je~toho daleko víc, ale~většina ostatních vylepšení jako~například nový systémy animací postav Motion Matching\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/motion-matching-in-unreal-engine} nebo~nový fyzikální engine Chaos\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/destruction-overview} neměli vliv na~rozhodnutí. -Podrobné informace jak každá implementace funguje a jak vývojaři s ní pracovat jsou popsané v programatorské dokumentaci. +Podrobné informace jak každá implementace funguje a jak vývojáři s ní pracovat jsou popsané v programátorské dokumentaci. \section{Herní logika, systémy, mechaniky} \label{sec:systemsAndMechanics} \subsection{Architektura} -Protože Unreal Engine byl na začátku vývíjen primárně pro online multiplayer hru Unreal Tournament a dnes stejně tak vyvíjen spolu s extremně velkou online multiplayer hrou Fortnite, je backend enginu dost abstraktní. +Protože Unreal Engine byl na začátku vyvíjen primárně pro online multiplayer hru Unreal Tournament a dnes stejně tak vyvíjen spolu s extrémně velkou online multiplayer hrou Fortnite, je backend enginu velmi abstraktní. -Například tak každý svět musí obsahovat vlastní game mode (třída AGameMode), který funguje jako správce dáneho světu -- přestože svět se může spravovát sám. Veškerá funkcionalita používaná v singlplayer hrách mohla by být přímo v kódu levelu nebo v globální instanci celé hry. Důvodem teto abstrakce je právě nativní podpora multiplayer her, která potom vývoj takových her zjednodušuje. +Například každý svět musí obsahovat vlastní game mode (třída AGameMode), který funguje jako správce daného světu -- přestože svět se může spravovat sám. Veškerá funkcionalita používaná v singleplayer hrách mohla by být přímo v kódu levelu nebo v globální instanci celé hry. Důvodem této abstrakce je právě nativní podpora multiplayer her, která potom vývoj takových her zjednodušuje. -Protože tento projekt je zaměřen pro hru jednoho hráče, práce je často programováná navzdory ustaleným UE C++ guidelines pro pohodlí a rychlost vývoje. Proto často třídy managerů systémů, game modů, instance hráče a další jsou na architektuře singletonu. Šetří od tří do desetí různých volání getteru, castu reflexe, vyhledávání v hash tabulkach a iteraci polí v každém místě použití. Přesto nemá žádný vliv na stabilitu programu a dokonce šetří výkonem zařízení. +Protože tento projekt je zaměřen pro hru jednoho hráče, práce je často programováná navzdory ustáleným UE C++ guidelines pro pohodlí a rychlost vývoje. Proto často třídy manažerů systémů, game modů, instance hráče a další jsou na architektuře singletonu. Přináší to nejen zmíněné pohodlí, ale navíc šetří od tří do desetí různých volání getteru, cast reflexe, vyhledávání v hash tabulkách a iteraci polí v každém místě použití. Přesto nemá žádný vliv na stabilitu programu a dokonce šetří výkonem zařízení. -Jen pro představu co obnaší klasické ziskání reference na instanci vlastní třídy hráče na pozadí. GetGameMode()\textrightarrow GetPlayerController(index)\textrightarrow GetPlayerPawn()\textrightarrow Cast(). A blueprinty disponují zkrácenou verzí GetPlayerPawn(index)\textrightarrow Cast(). V případě C++ je potřeba navíc ověřovat zda nějaké z volání nevratílo nullptr. +Jen pro představu co obnáší klasické získání reference na instanci vlastní třídy hráče na pozadí. GetGameMode()\textrightarrow GetPlayerController(index)\textrightarrow GetPlayerPawn()\textrightarrow Cast(). A blueprinty disponují zkrácenou verzí GetPlayerPawn(index)\textrightarrow Cast(). V případě C++ je potřeba navíc ověřovat zda nějaké z volání nevrátilo nullptr. \subsection{Scény a ukládání hry} -Scény resp. levely (třídy UWorld a ALevelScriptActor)\footnote{UWorld instance je přímo celý svět, který funguje jako balík metadat a kontejner pro veškeré instancované objekty v něm. ALevelScriptActor je objekt instancovaný v UWorld automaticky a obsahuje uživatelskou logiku světa.} taktéž lze získat v podobě singletonu. Základní implementace byla rozšířená pro high level mechanismus volání událostí ve scéně. Ten je za potřebí například, když hráč aktivuje spínací plošinu ve hře a ta následně otevře dveře. Narozdíl od jiných enginů, v UE objekty ve světě nemůžou referencovat jiné nezávislé objekty. Jednoduše referenci nejde přiřadít z důvodu abstrakce popsané v předchozí podsekci, protože každý objekt může mít vlastní herní svět (a nejen to). Proto pokud plošina z našeho príkladu chce otevřít dveře, musí zkusit poslat požádavek správcí herního světa, ten požadavek se nějak zpracuje a jen potom správce buď provede akci otevírání dveří samostatně nebo zavolá odpovidající funkci v instanci objektu. Pokud by byla potřeba zbavit se použití singleton architektury pro podporu paralelní existenci světů, stači předělat member funkci instance světa na statický multicast delegate\footnote{Delegate jsou obaly na C++ funkce, lambdy, funkce s reflexi a funkce z Blueprintu. Delegate může být typu single, pro uložení reference na jednou funkci, nebo multicast pro uložení dynamického seznamu funkcí. Podrobněji jsou popsané v programatorské dokumentaci.}, ke kterému se každý svět při konstrukci bude vázat. +Scény resp. levely (třídy UWorld a ALevelScriptActor)\footnote{UWorld instance je přímo celý svět, který funguje jako balík metadat a kontejner pro veškeré instancované objekty v něm. ALevelScriptActor je objekt instancovaný v UWorld automaticky a obsahuje uživatelskou logiku světa.} taktéž lze získat v podobě singletonu. Základní implementace byla rozšířená pro high level mechanismus volání událostí ve scéně. Ten je zapotřebí například, když hráč aktivuje spínací plošinu ve hře a ta následně otevře dveře. Narozdíl od jiných enginů, v UE objekty ve světě nemůžou referencovat jiné nezávislé objekty. Jednoduše referenci nejde přiřadit z důvodu abstrakce popsané v předchozí podsekci, protože každý objekt může mít vlastní herní svět (a nejen to). Proto pokud plošina z našeho príkladu chce otevřít dveře, musí zkusit poslat požádavek správcí herního světa, ten požadavek se nějak zpracuje a jen potom správce buď provede akci otevírání dveří samostatně nebo zavolá odpovídající funkci v instanci objektu. Pokud by byla potřeba zbavit se použití singleton architektury pro podporu paralelní existenci světů, stači předělat member funkci instance světa na statický multicast delegate\footnote{Delegate jsou obaly na C++ funkce, lambdy, funkce s reflexi a funkce z Blueprintu. Delegate může být typu single, pro uložení reference na jednou funkci, nebo multicast pro uložení dynamického seznamu funkcí. Podrobněji jsou popsané v programátorské dokumentaci.}, ke kterému se každý svět při konstrukci bude vázat. -Navíc hra byla rozšířena o implementaci obnovení hry z úložených dat. Při návrhu byla snaha udělat postup nejvíc triviální. Hru ukládá globální herní instance (třída GameInstance)\footnote{GameInstance je první objekt vytvořený enginem hned po úvodním načtení základu enginu a taky poslední objekt na destrukci při vypínání hry.} při vypínání hry a načíta taktéž při spouštění. Využit byl existující v UE systém serializace, který ukládá inventař postavy, jmeno levlu a checkpoint na něm spolu s posledním stavem levlu. +Navíc hra byla rozšířena o implementaci obnovení hry z úložených dat. Při návrhu byla snaha udělat postup nejvíc triviální. Hru ukládá globální herní instance (třída GameInstance)\footnote{GameInstance je první objekt vytvořený enginem hned po úvodním načtení základu enginu a taky poslední objekt na destrukci při vypínání hry.} při vypínání hry a načítá taktéž při spuštění. Využít byl existující v UE systém serializace, který ukládá inventář postavy, jmeno levelu a checkpoint na něm spolu s posledním stavem levelu. \subsection{Interakce} -Interakce jsou často řešeny pomoci architektury interface tříd\footnote{Interface třídy jsou konkurenčním přístupem komponentní architektuře. Interface je běžná praktika v OOP, která funguje jako domluva, že objekt bude obsahovat určité member funkce. Komponentny jsou samostatné určité podinstance objektu. Komponentní přístup se osvědčuje jako intuitivnější a více flexibilní zatímco Interface přístup při vývoji her je spíše nepřijemné vynucení ze světu OOP.} především kvůli populárním návodům vyzdvihující tuto metodu jako nejlepší ještě z doby, kdy jen vzníkal Unreal Engine 4. Nevýhodou je potřeba v obsáhlem a repetetivním nastavení každeho objektu, který chceme zapojit do mechaniky interakce. +Interakce jsou často řešeny pomoci architektury interface tříd\footnote{Interface třídy jsou konkurenčním přístupem komponentní architektuře. Interface je běžná praktika v OOP, která funguje jako domluva, že objekt bude obsahovat určité member funkce. Komponenty jsou samostatné určité podinstance objektu. Komponentní přístup se osvědčuje jako intuitivnější a více flexibilní zatímco Interface přístup při vývoji her je spíše nepříjemné vynucení ze světu OOP.} především kvůli populárním návodům vyzdvihující tuto metodu jako nejlepší ještě z doby, kdy Unreal Engine 4 pouze vznikal. Nevýhodou je potřeba v obsáhlém a repetitivním nastavení každého objektu, který chceme zapojit do mechaniky interakce. -Byl navržen kompletní systém pro spravu každého interačního objektu a komponent (viz.\Cref{fig:InteractableSystemDiagram}). Objekt dědicí třídu AInteractable při instancování samostatně nastaví a následně přepína potřebné kolize. Navíc spravuje interakční komponenty a reaguje na požadavky k ním. Komponenty se dělí na aktivatory a modifikatory. +Byl navržen komplexní systém pro správu každého interakčního objektu a komponent (viz.\Cref{fig:InteractableSystemDiagram}). Objekt dědicí třídu AInteractable při instancování samostatně nastaví a následně přepíná potřebné kolize. Navíc spravuje interakční komponenty a reaguje na požadavky k ním. Komponenty se dělí na aktivátory a modifikátory. \begin{figure} \centering \includegraphics[width=1\linewidth]{img/InteractableSystemDiagram.pdf} -\caption{Debug náhled aktivace interakčních objektů v zornem poli hráče.} +\caption{Diagram zpracování systému interakčních objektů.} \label{fig:InteractableSystemDiagram} \end{figure} \newpage -\paragraph{AInteractableActivator} Aktivátory jsou komponenty s úrčitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro sebe jednu podinstanci každého aktivatoru zaregistrovaného v enginu. Libovolný objekt taky může obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci raytracingu a castu objektu v případě nárazu paprsku. +\paragraph{AInteractableActivator} Aktivátory jsou komponenty s určitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro sebe jednu podinstanci každého aktivátoru registrovaného v enginu. Libovolný objekt taky může obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci raytracingu a dereference objektu v případě nárazu paprsku. -Navíc je k dispozici detekce objektů v zornem poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy s vynecháním většiny render pipliny a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Nasledně maska v grafickém vlakně je zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec do středu nalezených komponent se promítné paprsek a zachytí objekty (viz. \Cref{fig:InteractableScreenCapture}). Původně bylo předpokládáno využití HLSL compute shaderu pro vyhledání komponent v textuře, ale z časových důvodu jsem nedodělal přenos dat mezi CPU a GPU, jelikož dohledat dokumentaci o použití shaderu je dost náročné. Proto aktuální vyhledávání komponent je spouštěno na procesoru a v renderovacím vlakně na moderních zařízeních trvá pět až deset milisekund. V praxi se takový úkol řeší náhodným promítáním velkého množství paprsků nebo velkým hitboxem napodobující tvar frustrumu kamery. Taková řešení jsou sice rychlá na implementaci, ale začínají být nekvalitní až nepoužítelné pro trochu větší vzdalenosti než ,,pár metrů''. Navíc zahrnují časté počítání nárazů na velké množství objektů, čímž výkonostně nejsou o nic lepší naimplementováné metody v této práci. +Navíc je k dispozici detekce objektů v zorném poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy\footnote{Stencil Buffer sousedící s Z-Bufferem a má pouze jeden osmi bitový celočíselný kanál. Slouží především jako pomocník při tvorbě specifických renderovacích technik a efektů. Nejběžnější použití je renderování obrysů objektů, označení objektů pro následné zpracování v nějaké render pipeline a tvorba portálů.} s vynecháním většiny render pipline a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Následně je maska v grafickém vlákně zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec do středu nalezených komponent se promítne paprsek a zachytí objekty (viz. \Cref{fig:InteractableScreenCapture}). Původně bylo předpokládáno využití HLSL compute shaderu\footnote{High-Level Shader Language je proprietární shader jazyk používaný v DirectX.} pro vyhledání komponent v textuře, ale z časových důvodu jsem nedodělal přenos dat mezi CPU a GPU, jelikož dohledat dokumentaci o použití shaderu je dost náročné. Proto aktuální vyhledávání komponent je spouštěno na procesoru a v renderovacím vlákně na moderních zařízeních trvá pět až deset milisekund. V praxi se takový úkol řeší náhodným promítáním velkého množství paprsků nebo velkým hitboxem napodobující tvar frustrumu kamery. Taková řešení jsou sice rychlá na implementaci, ale začínají být nekvalitní až nepoužitelné pro trochu větší vzdálenosti než ,,pár metrů''. Navíc zahrnují časté počítání nárazů na velké množství objektů, čímž výkonnostně nejsou o nic lepší implementované metody v této práci. \begin{figure} \centering \includegraphics[width=1\linewidth]{img/InteractableScreenCapture.pdf} -\caption{Debug náhled aktivace interakčních objektů v zornem poli hráče.} +\caption{Debug náhled aktivace interakčních objektů v zorném poli hráče.} \label{fig:InteractableScreenCapture} \end{figure} -\paragraph{AInteractableModificator} Modifikátory jsou komponenty s úrčitou logikou libovolné modifikace objektu, ve kterém jsou instancovány. Tyto komponenty mohou obsahovat pouze objekty označené jako interakční (dědí třídu interákčního objektu). Aktuální implementace nabízí modifikátory pro aktivaci nějaké události pomoci dosahu ,,ruky'' nebo zahlednutím ,,očí'' hráče, pohyb a rotace předmětu v prostoru, ukládání předmětu do inventáře hráče. Z časových důvodu byla zamítnuta implementace modifikace meshe objektu, která by využivala nového rozhraní Chaos. +\paragraph{AInteractableModificator} Modifikátory jsou komponenty s určitou logikou libovolné modifikace objektu, ve kterém jsou instancovány. Tyto komponenty mohou obsahovat pouze objekty označené jako interakční (dědí třídu interakčního objektu). Aktuální implementace nabízí modifikátory pro aktivaci nějaké události pomoci dosahu ,,ruky'' nebo zahlednutím ,,očí'' hráče, pohyb a rotace předmětu v prostoru, ukládání předmětu do inventáře hráče. Z časových důvodu byla zamítnuta implementace modifikace meshe objektu, která by využívala nového rozhraní Chaos. -Právě u modifikátorů nejvíc přispěl nový system zpracování vstupu pro Unreal Engine 5. Modifikatory využívájí možnosti přidávání resp. odstranění kontextů vstupu za běhu. +Právě u modifikátorů nejvíc přispěl nový systém zpracování vstupu pro Unreal Engine 5. Modifikatory využívájí možnosti přidávání resp. odstranění kontextů vstupu za běhu. -\subsection{Cutscény a Quick Time Eventy} -Unreal je slavný, jak dobře lze v něm animovat scénu. Rozhraní a editor systému Sequencer je pohodlné a bohaté na funkcionality. Bohůžel veškeré skvěle věci končí mimo editor, jelikož chybí pohodlná funkcionalita režije různých souborů animací (stejná poznámka se vztahuje i k UI animacím). Proto byl dodán systém UCutsceneManager implementující frontu animací spolu s uživatelským rozhraním přeskočení animace. +\subsection{Cutscény a Quick Time Events} +Unreal je slavný, jak dobře lze v něm animovat scénu. Rozhraní a editor systému Sequencer je pohodlné a bohaté na funkcionality. Bohůžel veškeré skvěle věci končí mimo editor, jelikož chybí pohodlná funkcionalita režie různých souborů animací (stejná poznámka se vztahuje i k UI animacím). Proto byl dodán systém UCutsceneManager implementující frontu animací spolu s uživatelským rozhraním přeskočení animace. -Pro přiblížení problému, vyjmenuji časté potřeby, které vzníkají při běžném použití animací. +Pro přiblížení problému, vyjmenuji časté potřeby, které vznikají při běžném použití animací. \begin{itemize} \item Zároveň spuštěné animace bojují o vlastnictví objektů, \item animace nelze přetáčet, -\item u animace nelze zjistit zpětně jestli byla přehrana do konce, pouze přivázat volaní funkce po ukončení, -\item animace umí říct jestli je někdé přehráváná, ale neřekné kde. +\item u animace nelze zjistit zpětně jestli byla přehrana do konce, pouze přivázat volání funkce po ukončení, +\item animace umí říct jestli je někde přehráváná, ale neřekne kde. \end{itemize} -Převážně pro použití v animacích je naimplementováná fronta nečekaných událostí, což jsou interaktivní UI elementy na obrazovce hráče. V herním průmyslu jsou využívány zejména v cutscénach, pro ,,hlubší ponoření'' hráče do děje. Klasickými příklady jsou animace šplhání herní postavy po nějaké překážce nebo kinematografický souboj mezi postavy. Při takových údalostech lze přibližit hráče dynamické a napínavé situaci pomoci klikání na stejně dynamické UI elementy na obrazovce v souladu s pohybem herní postavy. V V praxi se objevují i pokročílejši varianty napodobení děje pohybem myší a joystickem nebo gyroscopem ovládače. +Převážně pro použití v animacích je implementována fronta nečekaných událostí (QTE - Quick Time Events), což jsou interaktivní UI elementy na obrazovce hráče. V herním průmyslu jsou využívány zejména v cutscénách, pro ,,hlubší ponoření'' hráče do děje. Klasickými příklady jsou animace šplhání herní postavy po nějaké překážce nebo kinematografický souboj mezi postavy. Při takových událostech lze přiblížit hráče k dynamické a napínavé situaci pomoci klikání na stejně dynamické UI elementy na obrazovce v souladu s pohybem herní postavy. V praxi se objevují i pokročilejší varianty napodobení děje pohybem myší a joystickem nebo gyroskopem ovládače. V implementaci jsou k dispozici eventy jednotného a víc-krátného klikání a držení tlačítka v časovém intervalu. \subsection{Dialogy} -V enginu je zavádějící implementace pokročílého dialogového systému. Má stejný problém jako animace. Dialogy jsou neintuitivní pro tvorbu a použití, vyžadují časté repetetivní kopírování stejných parametrů do souborů s linky dialogů a dohromady celý tento systém má málo dokumentace. Navíc ,,dialogy'' v tomto systému jsou pouze samostatné věty, které musí přehravat nějaký zvuk a mohou se vybírát v zavislostí na omezeném kontextu typu "kdo na koho mluví". O nepoužitelností takového rozhrání napovídají návody na tvorbu vlastních systému nebo vývojařský obchod plný pluginů implementujících tento systém lépe. +V enginu je zavádějící implementace pokročilého dialogového systému. Dialogy jsou neintuitivní pro tvorbu a použití, vyžadují časté repetitivní kopírování stejných parametrů mezi souborů dialogů a k tomu celý systém má nedostačující dokumentace. Navíc ,,dialogy'' v tomto systému jsou pouze samostatné věty, které musí přehrávat nějaký zvuk a mohou se vybírat v závislosti na omezeném kontextu typu "kdo na koho mluví". O nepoužitelností takového rozhrání napovídají návody na tvorbu vlastních dialogů nebo vývojářský obchod plný pluginů implementujících tento systém lépe. -Protože kvalitná řešení jsou placená a ty zdarma jsou často nevyhovující kvality, byla navržená vlastní implementace. Jedna se o frontu, která umí přehravat celé tabůlky dialogových vět. Celý dialog nebo množinu vět společného kontextu lze pohodlně zapsat do tabulkového formátu přímo v editoru. Jedná věta obsahuje id, text, dobu přehrání nebo zvukovou stopu. Ve výsledku lze do fronty zařadit několik tabůlek, které se mohou přehrávat sekvenčně od n-té věty do poslední, jednu náhodnou větu nebo přesně jednu větu podle id. +Protože kvalitná řešení jsou placená a ty zdarma jsou často nevyhovující kvality, byla navržená vlastní implementace. Jedna se o frontu, která umí přehrávat celé tabulky dialogových vět. Dialog resp. množinu vět společného kontextu lze pohodlně zapsat do tabulkového formátu přímo v editoru. Jedná věta obsahuje id, text, dobu přehrání nebo zvukovou stopu. Ve výsledku lze do fronty zařadit několik tabulek, které se mohou přehrávat sekvenčně od n-té věty do poslední, jednu náhodnou větu nebo přesně jednu větu podle id. \subsection{Minihry} -Gameplay druhého levlu je celý složen z miniher. V moment vydání -- práce obsahuje pouze pět hlavních pro gamedesign miniher v podobě ,,proof of concept''. Z časových důvodů zbylé vedlejší minihry nebyly implementováné. Každá z hlavních miniher je parodie na již existujicí známe hry. +Gameplay druhého levlu je celý složen z miniher. V moment vydání -- práce obsahuje pouze pět hlavních pro game design miniher v podobě ,,proof of concept''. Z časových důvodů zbylé vedlejší minihry nebyly implementovány. Každá z hlavních miniher je parodie na již existující známe hry. -Minihry jsou samostatné objekty, které manager scény startuje nebo vypíná. Po nastartování minihra sama přepné kontext ovládání na sebe a řídí vlastní stav. Jestli minihra nebyla ukončená dočasně ale dohráná, potom vráti ovládání hráči a spolu s tím výsledek a skóre. +Minihry jsou samostatné objekty, které manažer scény restartuje nebo vypíná. Po nastartování minihra sama přepne kontext ovládání na sebe a řídí vlastní stav. Jestli minihra nebyla ukončena dočasně ale dohraná, potom vráti ovládání hráči a spolu s tím výsledek a skóre. Dostupné zkušební implementace: \begin{itemize} -\item Parodie flash hry Age of War. V pozadí uklízení knížek dějepisu jsou umístěny základny hráče a soupeře (umělé inteligence). Hráč ovládá minihru pomici UI, kde nakupuje jednotky typu: pěšák, střelec a tank. Peníze získáva za zničení nepřátelských jednotek. Vyhrává soupeř, který dokázal přejít přes jednotky nepřátele až k základně a zdemolovat ji. V aktuální implementaci chybí vylepšení éry jednotek, zvuky, grafické efekty, modely a animace jednotek. Přesto lze vyzkoušet nakup a jejich ,,souboj'' jednotek. -\item Parodie na mobilní hru Subway Surfers. Aktuálně má minihra nevhodné pozadí, neobsahuje audio a grafické prvky. Podle návrh hráč nahání veverku v lese, která mu ukradla část důležitého pro příběh předmětu. Pomoci pohybů nahoru, dolu, doleva a napravo hráč uhýba objevujicím se lesním překážkam. Překážková dráha má šířku třech běžicích pruhů a překážky mohou vyžadovat přeskočení, sklouznutí nebo úlpý uhyb do strany. -\item Podoba počítačové rytmické hry Osu!. Hráč má za úkol klikat na objevující se na obloze hvězdičky v souladu s rytmem hudby (v pořadí ve kterém se objevili). Aktuálně ,,hvězdičky'' jsou v podobě čtverečků, ale hudba je zcela hotova. Již teď lze vyzkoušet klikání pro prvních 10 sekund doprovodu. -\item Podoba mobilní hry Crossy Road. Hráč pomoci pohybu dopředu, dozadu, doleva a dolu potřebuje posouvat se vpřed po překážkové dráze. Dráha je složená z pěti průhu kolmých ke kameře, a každý průh je složen z dvanáctí políček, na které může stoupnout hráč. Náhodně na krajích pruhu se objevují divoká zvířata, laviny a silný vítr (v originální hře to jsou auta na silnici), které se posouvají k opačnemu kraji přes všechná políčka a mohou ukončit běh hráče. Podle návrhu se bude hra odehrávát v ledovém prostředí, kde hráč bude procházet sněžnou krajinou. Audiovizuální prvky opět nejsouk dispozici, ale již teď teď lze vyzkoušet posun dopředu a uhyb překážkam. -\item Jednoduchá minihra rybolovu (více podob napříč různymi hry). Hráč má na obrazovce svislý obdelníkový indikátor znázorňující vodní hlubinu. Podél indikátoru se náhodně pohybuje obrázek rybičky, kterou hráč musí udržovat v malé pohyblivé zóně. Zóna se pohybuje automaticky dolu nebo nahoru držením tlačítká myší (rychlost pohybu není lineární ale kvadratická). V implementaci chybi zvukové assety a vyvaženost složitosti, ale je k dispozici kompletní UI a mechanika lovu. +\item Parodie flash hry Age of War. V pozadí uklízení knížek dějepisu jsou umístěny základny hráče a soupeře (umělé inteligence). Hráč ovládá minihru pomici UI, kde nakupuje jednotky typu: pěšák, střelec a tank. Peníze získává za zničení nepřátelských jednotek. Vyhrává soupeř, který dokázal přejít přes jednotky nepřátele až k základně a zdemolovat ji. V aktuální implementaci chybí vylepšení éry jednotek, zvuky, grafické efekty, modely a animace jednotek. Přesto lze vyzkoušet nakup a jejich ,,souboj'' jednotek. +\item Parodie na mobilní hru Subway Surfers. Aktuálně má minihra nevhodné pozadí, neobsahuje audio a grafické prvky. Podle návrh hráč nahání veverku v lese, která mu ukradla část důležitého pro příběh předmětu. Pomoci pohybů nahoru, dolu, doleva a vpravo hráč uhýbá objevujícím se lesním překážkam. Překážková dráha má šířku třech běžících pruhů a překážky mohou vyžadovat přeskočení, sklouznutí nebo úhyb do strany. +\item Podoba počítačové rytmické hry Osu!. Hráč má za úkol klikat na objevující se na obloze hvězdičky v souladu s rytmem hudby (v pořadí ve kterém se objevily). Aktuálně ,,hvězdičky'' jsou v podobě čtverečků, ale hudba je zcela hotova. Již teď lze vyzkoušet klikání pro prvních 10 sekund doprovodu. +\item Podoba mobilní hry Crossy Road. Hráč pomoci pohybu dopředu, dozadu, doleva a dolu potřebuje posouvat se vpřed po překážkové dráze. Dráha je složená z pěti pruhů kolmých ke kameře, a každý pruh je složen z dvanáctí políček, na které může stoupnout hráč. Náhodně na krajích pruhu se objevují divoká zvířata, laviny a silný vítr (v originální hře to jsou auta na silnici), které se posouvají k opačnému kraji přes všechna políčka a mohou ukončit běh hráče. Podle návrhu se minihra odehrává v ledovém prostředí, kde hráč prochází sněžnou krajinou. Audiovizuální prvky opět nejsou k dispozici, ale již teď teď lze vyzkoušet posun dopředu a úhyb překážkam. +\item Jednoduchá minihra rybolovu (více podob napříč různými hry). Hráč má na obrazovce svislý obdelníkový indikátor znázorňující vodní hlubinu. Podél indikátoru se náhodně pohybuje obrázek rybičky, kterou hráč musí udržovat v malé pohyblivé zóně. Zóna se pohybuje automaticky dolu nebo nahoru držením tlačítka myší (rychlost pohybu není lineární, ale kvadratická). V implementaci chybí zvukové assety a vyváženost složitosti, ale je k dispozici kompletní UI a mechanika lovu. \end{itemize} \subsection{Nehratelné postavy} +Pro tvorbu NPC (Non-Playable Characters) byli použité základní prostředky enginu. Podle dizajnu hry, k dispozici by měli být tři druhy logiky pro NPC, ale z časových důvodu jsou implementovány pouze dvě. Jedno z chování je pouhá chůze k dynamickým bodům. Toto základní chování se potom rozděluje na střežení oblasti nebo boj s hráčem. +Postavy, které střeží oblast využíváji nový pro UE5 systém vjemů nehratelných postav. Postavy tak umí reagovat na zvuky, dotyky nebo zahlédnutí jiných objektů. Toto je využito pro postavy, které hlídají vězení v levelu 4. Ty patrolují předem navržené trasy a spouští určité akce až zahlédnou hráče. + +V implementaci chybí kompletní logika boje s hráčem. Aktuálně postavy pouze běží k hráči jestli ho zahlédnou. Zbytek nedodělané logiky je spíš vizuálního charakteru a spočíval by v jednoduchých animacích použití střelné nebo ruční zbraně ve hře. + +Každé chování využívá Navmesh pro orientaci na úrovní. \subsection{Nastavení} +Hra využívá existující v enginu de/serializace v textovém formátu ,,.ini''. Při exportu hry v podobě samostatného buildu, lze využívat i binární formát konfiguračních souborů. Tento přístup značně zjednodušuje tvorbu konfiguračních proměnných. Tak k tvorbě vlastních parametrů stačí pouhá deklarace potřebných proměnných v C++ třídě. +Triviálně se pracuje s preferencemi hráče, které určují hlasitost různých kategorií zvuků nebo preferencemi ovládání. Tam stačí pouze přečíst resp. zapsat hodnotu. Jednoduché to přestává být v momentě nastavení kvality grafiky a parametrů zobrazení okna aplikace. Zejmená změny zaměřené na okno aplikace mohou zhavarovat a vypnout hru nebo ještě hůř zablokovat vstup celého počítače a donutit uživatele ke kompletnímu restartování zařízení. Takové situace mohou nastat celkem běžně, a nemusí znamenát problem v aplikaci. Například předčasný zásah OS k neodpovídajícímu procesu (hra se delší dobu načítá po aplikaci nového nastavení) nebo nevhodná reprezentace rozlišení, které monitor nebo GPU uživatele nepodporuje. + +V libovolném případě hra musí být schopna obstát nečekáné závady, a proto byl navržen mechanismus obnovení předchozích funkčních nastavení. Po zvolení nových parametrů a jejich aplikaci, hra nejprv uloži stávájící nastavení a až poté aplikuje ty nová. Po aplikovaní a až se proces okna vrátí zpět do renderujícího stavu, v menu se objeví okénko po dobu pěti sekund očekávající schválení od uživatele. Pokud uživatel stihne potvrdit úspěšnou změnu parametrů, jsou uložené jako funkční. Pokud uživatel z libovolného důvodu změnu neschválí, potom jsou obnoveny předchozí parametry. + +Navíc právě nastavení zobrazení okna (a výber zhlazovácí metody) byly ručně implementovány, jelikož nejsou volně k dispozici v high-level implementaci enginu. Jedná se o možnost výběru rozlišení a jeho třídění podle poměru stran, změná režimu vykreslování okna (neokenní, okenní bez rámce, okenní s rámcem), změná obnovovací frekvence. Při výběru a nastavení rozlišení se pracuje napřímo s DirectX\footnote{DirectX je množina API pro práci s grafikou a převážně pro počítačové hry.} rozhraním a zhlazovací metoda se nastavuje přes příkazové rozhraní enginu. \section{Návrh tvorby generativního obsahu a jeho načítání za běhu} \label{sec:contentGenerationAndIntegration} -Nabízí se online tabulká skóre na základní a vygenerováné minihry. + +\subsection{Možností generativních modelů v tomto projektu} +Herní úrovně jsou navrženy tak, aby pokrývala rozsáhlé herní žánry a situace, které dosud nebyly nebo nejsou často využívané s generovanou tvorbou. +\begin{enumerate} +\item Level je zaměřen na tvorbu hororového obsahu. Je to skvělá příležitost vývoje a použití modelu, který využívá naše fyzické, chemické a psychické znalostí o lidském organismu, aby dokázal generovat strašidelný obsahu a následně ho správně režisérský začlenit. +\item Level slouží k testování modelu schopného tvorby libovolných miniher. Nabízí se tady taktéž online tabulka skóre pro základní a vygenerováné minihry. +\item Level existuje pro generování nových logických úseků resp. úkolů a modifikaci stávajících logických překážek. Například nová logika zapojení kabelů v elektrotechnické místnosti nebo přestavba bludiště. +\item Level umožňuje vyzkoušet generování sekvencí chodeb. Chodby by museli obsahovat patroly a kryty tak, aby hráč dokázal nenápadně prolézt skrz patroly. +\item poslední Level bude testovat schopnost navržení herních nepřátel, jejích logiku a jejich rozmístění na úrovní. +\end{enumerate} + +\subsection{Návrh architektury} +Architektura generování obsahu je založená na použití zřetězení různých modelů a kontrolních mechanismu. Kompletní řetězce modelů jsou nasazeny na soukromých serverech, které v různem intervalu nezávisle produkují nové assety. Po generaci assety jsou přemístěné na veřejné úložiště, odkud hra při zapínání stahuje nějaké množství ,,náhodných'' assetů (přesná definice výběru je vystvětlená později v podsekci \cref{par:contentSharing}). + +\paragraph{Fine-tuning} V podobě open-source je k dispozici většina potřebných druhů modelů. Po analýze vyplívá, že bude potřeba vytvořit pouze jeden model, který bude schopen pracovat s kódem v Unreal Engine. Je to potřeba pro programování assetu nebo aspoň jeho umístění ve světě hry. Nejjednodušší a zároveň efektivní cesta je založená na fine-tuningu resp. dotrénování modelu, který se již využívá pro programování. + +Dotrénování může mít více podob. Nejspíš by se jednalo o fine-tuning programovácího modelu v C++ s dodatečnou Unreal Engine syntaxí nebo fine-tuning tvorby diagramů v Blueprintech. Výsledné blueprinty potom lze konvertovat do python příkazů, které vytvoří vygenerovaný diagram v editoru. Nelze jednoznačně říct co je lepší, obě metody vyžadují ověřit. Pro C++ modely je víc dat a k tomu toto prostředí má více kreativní svobody, přestože může zhavarovat aplikaci. Blueprinty jsou zdaleka bezpečnější, ale na oplátku bude těžší sbírat trénovací data. + +\paragraph{Postup generování} +Vše by začínálo v textovém modelu, který vytvoří typ a popis generováného objektu. Pomocí třídícího mechanismu se vybere potřebná množina ostatních modelů, kontrolních mechanismu a konvertovácích nástrojů. Asset bude postupně procházet takovým řetězcem až nabyde finální podoby. Na konci bude vždy kontrolní mechanismus, který zvaliduje soubor a ověří funkčnost assetu pomoci unit-testů ve hře. Pokud asset neprojde validací, celý postup, soubor a vstupní popis se zalogují pro budoucí ruční fine-tuning. + +Příklad tvorby interakčního objektu: +\begin{enumerate} +\item LLM vytváří popis assetu, v tomto případě nějaký interakční objekt, který umí přehrávat určitý zvuk a je umístěn nějakým způsobem na levelu 3. +\item Kontrolní mechanismus vybírá následující potřebné modely a nástroje. +\item Popis prochází modelem generující obrázky. +\item Obrázky prochází modelem generující 3D model (Nejdřív se generuje 360° video potřebného objektu, z kterého se následně generuje mesh). +\item Další část popisu je použita modelem generující zvukové stopy. +\item Poslední čast popisu se vloží do našeho dotrenováného programovácího modelu. Tento model se zároveň stará o umístění assetu v herních světech, protože jako jedíný je natrénovan na kontextu teto hry. +\item Výsledné kousky linkovací mechanismus spojí do jedné třídy resp. vloží do jednoho Blueprintu. Předtím kousky konvertuje do assetů v enginu a případně aktualizuje cesty referencí. +\item Konečný asset se zkouší na kompilaci a prochází unit-testy. Pokud tento krok bude úspěšný, asset je exportovan v podobě DLC nebo patch obsahu. Jinak popis, kousky a výsledek se logují pro budoucí investigaci. +\end{enumerate} + +\subsection{Poskytování obsahu} +\paragraph{Nasazování generátoru} Nezávislé řetězce modelů zjednodušují řízení sítě workerů. Pomoci docker containeru\footnote{Docker je software umožňující lokální virtualizaci prostředí nazývané containery.} nebo nasazovacího skriptu by se snadno vytvořil další nový worker (naše generující jednotka), který může ihned začít s generováním. Vygenerovaný obsah poté může být ihned přemístěn na veřejné úložiště, a proto worker nepotřebuje nijak velké úložiště. Protože workery nemusí odpovídat na požadavky uživatele v reálnem čase a plánovaná doba jedné hry je 15-30 minut nemusí generátory být rychlé a tedy ani náročné na výpočetní prostředky. Nezáleží nám ani na dlouhodobé životností workeru, jelikož modely se často sekvenčně přepínají resp. běží vždy pouze jeden model najednou. Takový přístup přináší flexibilitu v řízení prostředků a umožňuje ukládat mezistavy generátorů. Dokonce nepotřebujeme nijak velkou propustnost sítě, protože jednoduché assety nemohou přesahovat velikost jednotky megabajtů a máme větší časový intervaly mezi vznikem souborů. + +\paragraph{Stahování obsahu}\label{par:contentSharing} Hra automaticky stahuje a aktivuje obsah před začátkem nové hry. Z podrobné analýzy jsem rozhodnul, že nejsou zapotřebí žádné autentifikační nebo šifrovací vrstvy. Vždy by byla možnost reverse-engineeringem\footnote{Metoda analýzy hotového produktu (v našem případě binárního souboru), pro získání neveřejných informací popisujících funkčnost produktu.} získat klíč z binárních souborů hry a napadnout celý systém. Jestli se zamyslet, volný přístup k generovaným assetům nemá žádné zápory. Je jednoduchý na implementaci a udržbu. Je ale očekáváné přípojení pomoci HTTPS\footnote{Hypertext Transfer Protocol Secure je protokol pro šifrovaný přenos dat využívaný pro poskytování webových serverů.} a ověření certifikátu\footnote{Certifikáty v digitální podobě jsou řetězcem veřejných asymetrických klíčů různých vydavatelů. Každý předchozí vydavatel ručí za důvěryhodnost dalšího vydavatele.} s kopii uloženou v aplikaci. Mohlo by totiž dojít k man-in-the-middle útoku\footnote{Man-in-the-middle (MITM) je druh kyberútoku, ve kterém útočník tajně sleduje nebo upravuje komunikaci mezi dvěma uzly.} a hráč by mohl spustit ve hře nebezpečný obsah. + +Hra zahájí stahování pomoci API, který vybere nějaké soubory. V základu assety jsou vybírány náhodně, ale zároveň se používá hodnocení získané od hráčů, které váhově lehce mění hustotu pravděpodobnosti normálního rozdělení. + +\paragraph{Hodnocení obsahu} Potom co hráč zahraje s úrčitým assetem, máme možnost získat odezvu od hráče. Hodnocení využíjeme k váhové manipulaci náhodného výběru a zároveň jako data pro doládění generátorů. Návrh systému hodnocení staženého obsahu je již náročnější problém. Úkolem je umožnit pouze unikátní hodnocení a pouze od hráčů, které si s tímto obsahem opravdu zahráli. Protože nemáme žádnou authentifikaci, volné hodnocení nemusí fungovat. Kdokoliv může spamováním falešných hodnocení přemístit špatně hodnocený obsah do kategorie lepších a naopak. Authentifikáce tomu taky nezabraní, pouze oddálí takovou situaci. + +Některé hry v praxi používájí sofistikováné metody využívájící detekci nelegální kopie hry, nebo vyžití API herních obchodů (dosažení, odznaky, id účtu atd.). Takové metody se zdají být účinné, ale už dávno se lehce obchází pomoci triviální simuláce API odezvy. + +Táto práce nabízí sledování id účtu hráče, který hru zahrál a jeho seznam stažených souboru, pokud hra bude šířená na platformě Steam. UUID (Unique User Identificator) účtu ví pouze vlastník účtu, čili je to citlivá informace. Proto před odesláním takových dat budeme je vždy šifrovat asymetrickým klíčem (veřejným certifikátem webu ze kterého zároveň obsah stahujeme). Až bude hráč chtít ohodnotit ve hře obsah se kterým zahrál, API zkontroluje zda UUID hráče v Steam opravdu vlastní hru, že takový UUID stahovalo tento obsah z našeho serveru a že toto UUID ještě tento obsah nehodnotilo. + +Pokud by hra byla šířená zdarma, bude potřeba zavést omezení na N hodnocení denně pro jedno UUID, protože bude možné bezproblémově vytvářet falešné účty. Jinak, z ekonomických důvodu, nám nebude vadít, že někdo bude nakupovat hru víckrát, aby víckrát nevhodně ohodnotil obsah. + +Pořád bude možné revers-engeneernout API a zapsat stažení libovolného obsahu pro nějaký účet. Taková situace je zcela zanedbatelná, jelikož jedno hodnocení má malý vliv na průměr ve větších číslech. + +\subsection{Problémové typy obsahu} +Již teď je možné předpovědět co nebude kompletně fungovat nebo nebude fungovat v dostatečné kvalitě. +\begin{itemize} +\item Herní obsah tvořený ručně nástrojí v editoru vyžadují samostatné modely. Například animace objektů na levelu tvořené v Sequencer, tvorba UI v editorovém designeru, práce se zvuky v Cue, statická tvorba úrovní a tedy i Landscape nebo foliáž a tvorba fyzických objektů (simulace tkáně, destrukce, pružnosti a dalších jevů). Pravděpodobně i jiné, ale víc technologií v této práci využito nebylo. +\item Materiály tež vyžadují vlastní model, ale jsou tvořeny pomocí grafových prvků napodobující blueprinty a máme k dispozici velké množství dat pro trénování takového modelu. V základu si můžeme vystačit bez generování materiálu a pouze vytvořit variace barev. +\item Grafické provedení generovaných objektů může mít velký dopad na výkon hry. Optimalizaci může provést další model, který bude provádět retopologii\footnote{Retopologie je proces zjednodušení složité topologie 3D objektu bez značně viditelných změn.} a pro tvorbu kolizí lze použít k-DOP\footnote{k-DOP (k-Discrete Oriented Polytope) je jeden z postupu obalování složitých 3D objektu do jednodušších tvarů.} algoritmus dostupný v UE. +\end{itemize} \section{Grafika} \label{sec:graphics} \subsection{Statické objekty} \subsection{Dynamické a procedurální objekty} +%kytky +%trava \subsection{Osvětlení, efekty, Post-Processing} \subsection{Materiály a shadery} \subsection{UI} @@ -115,6 +185,7 @@ Nabízí se online tabulká skóre na základní a vygenerováné minihry. \label{sec:audio} \subsection{Kategorie a parametry audio assetů} \subsection{Dynamický hudební doprovod} +\subsection{Dabing dialogů} \section{Tipy při vývoji v UE} \label{sec:UETips} -- 2.45.2 From c521f93c6d20c0ee78d57b369ecb566fb53b4c4d Mon Sep 17 00:00:00 2001 From: Oleg Petruny Date: Mon, 26 May 2025 12:34:32 +0200 Subject: [PATCH 5/8] typo --- ch2.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ch2.tex b/ch2.tex index 303a937..58ece45 100644 --- a/ch2.tex +++ b/ch2.tex @@ -46,7 +46,7 @@ Byl navržen komplexní systém pro správu každého interakčního objektu a k \newpage \paragraph{AInteractableActivator} Aktivátory jsou komponenty s určitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro sebe jednu podinstanci každého aktivátoru registrovaného v enginu. Libovolný objekt taky může obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci raytracingu a dereference objektu v případě nárazu paprsku. -Navíc je k dispozici detekce objektů v zorném poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy\footnote{Stencil Buffer sousedící s Z-Bufferem a má pouze jeden osmi bitový celočíselný kanál. Slouží především jako pomocník při tvorbě specifických renderovacích technik a efektů. Nejběžnější použití je renderování obrysů objektů, označení objektů pro následné zpracování v nějaké render pipeline a tvorba portálů.} s vynecháním většiny render pipline a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Následně je maska v grafickém vlákně zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec do středu nalezených komponent se promítne paprsek a zachytí objekty (viz. \Cref{fig:InteractableScreenCapture}). Původně bylo předpokládáno využití HLSL compute shaderu\footnote{High-Level Shader Language je proprietární shader jazyk používaný v DirectX.} pro vyhledání komponent v textuře, ale z časových důvodu jsem nedodělal přenos dat mezi CPU a GPU, jelikož dohledat dokumentaci o použití shaderu je dost náročné. Proto aktuální vyhledávání komponent je spouštěno na procesoru a v renderovacím vlákně na moderních zařízeních trvá pět až deset milisekund. V praxi se takový úkol řeší náhodným promítáním velkého množství paprsků nebo velkým hitboxem napodobující tvar frustrumu kamery. Taková řešení jsou sice rychlá na implementaci, ale začínají být nekvalitní až nepoužitelné pro trochu větší vzdálenosti než ,,pár metrů''. Navíc zahrnují časté počítání nárazů na velké množství objektů, čímž výkonnostně nejsou o nic lepší implementované metody v této práci. +Navíc je k dispozici detekce objektů v zorném poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy\footnote{Stencil Buffer sousedí s Z-Bufferem a má pouze jeden osmi bitový celočíselný kanál. Slouží především jako pomocník při tvorbě specifických renderovacích technik a efektů. Nejběžnější použití je renderování obrysů objektů, označení objektů pro následné zpracování v nějaké render pipeline a tvorba portálů.} s vynecháním většiny render pipline a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Následně je maska v grafickém vlákně zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec do středu nalezených komponent se promítne paprsek a zachytí objekty (viz. \Cref{fig:InteractableScreenCapture}). Původně bylo předpokládáno využití HLSL compute shaderu\footnote{High-Level Shader Language je proprietární shader jazyk používaný v DirectX.} pro vyhledání komponent v textuře, ale z časových důvodu jsem nedodělal přenos dat mezi CPU a GPU, jelikož dohledat dokumentaci o použití shaderu je dost náročné. Proto aktuální vyhledávání komponent je spouštěno na procesoru a v renderovacím vlákně na moderních zařízeních trvá pět až deset milisekund. V praxi se takový úkol řeší náhodným promítáním velkého množství paprsků nebo velkým hitboxem napodobující tvar frustrumu kamery. Taková řešení jsou sice rychlá na implementaci, ale začínají být nekvalitní až nepoužitelné pro trochu větší vzdálenosti než ,,pár metrů''. Navíc zahrnují časté počítání nárazů na velké množství objektů, čímž výkonnostně nejsou o nic lepší implementované metody v této práci. \begin{figure} \centering -- 2.45.2 From 88903f7096a591bae24d9d6f6516857ee0839aae Mon Sep 17 00:00:00 2001 From: Oleg Petruny Date: Mon, 26 May 2025 19:23:39 +0200 Subject: [PATCH 6/8] up to minigames review --- ch2.tex | 81 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/ch2.tex b/ch2.tex index 58ece45..ed03f45 100644 --- a/ch2.tex +++ b/ch2.tex @@ -1,40 +1,42 @@ \chapter{Vývoj hry} \label{chap:main} -Přenos starého projektu na~další major verzi~UE nebyl nijak problematický, dokonce v~editoru je~možnost rychlého exportu assetů do~jiného projektu. Rozhodně tomu pomohlo, že~se~C++~kód nepřenášel, ale~napsal znovu. UE~citelně rozšířil seznam dostupných tříd, přitom~některé jsou~již deprecated nebo~odstráněny úplně. Tak~například byly~odstraněny třídy Matinee (bývalý format a editor animací objektů) pro~podporu novejší třídy Sequencer nebo~starý PhysX\footnote{PhysX je open-source fyzický engine s hardwarovou akcelerací pro grafiky s CUDA architekturou (GPU společnosti NVIDIA).} rozhraní, které~je nahrazeno systémem Chaos. Přesto se~něco pokazilo při~exportu objektů s~dynamickou fyzikou (závěsy, které~reagují na~simulaci větru, se~museli předělat). +Přenos starého projektu na~další major verzi~UE nebyl nijak problematický, dokonce v~editoru je~možnost rychlého exportu assetů do~jiného projektu. Rozhodně tomu pomohlo, že~se~C++~kód nepřenášel, ale~byl napsán znovu. UE~citelně rozšířil seznam dostupných tříd, přitom~některé jsou~již deprecated nebo~odstráněny úplně. Tak~mimo~jiné byly~odstraněny třídy Matinee (bývalý formát a editor animací objektů) pro~podporu novejší třídy Sequencer nebo~starý PhysX\footnote{PhysX je open-source fyzický engine s hardwarovou akcelerací pro grafiky s CUDA architekturou (GPU společnosti NVIDIA).} rozhraní, které~je nahrazeno systémem Chaos. Přesto se~něco pokazilo při~exportu objektů s~dynamickou fyzikou (závěsy, které~reagují na~simulaci větru, se~musely předělat). -Přenos byl~odůvodněn převážně malou velikostí starého projektu a~taky lákavou nabídkou nových technologií zejména Nanite a~Lumen. Navíc pátá verze Unrealu -- přesněji verze~5.5 -- přinesla značná vylepšení jako: +Přenos byl~odůvodněn převážně malou velikostí starého projektu a~taky lákavou nabídkou nových technologií, zejména Nanite a~Lumen. Navíc pátá verze Unrealu -- přesněji verze~5.5 -- přinesla značná vylepšení jako: \begin{itemize} -\item Nový systém zpracování vstupu Enhanced Input\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/enhanced-input-in-unreal-engine}. Místy má~příliš mnoho objektové abstrakce, ale~rozhodně velký krok vpřed. Umožňuje snadné dynamické přepínání různých sad ovládání (multiplayer hry, gameplay/menu), dynamické modifikátory a~spouštěče vstupu (změna senzitivity, víceklik, podržení tlačítka určitou dobu), podpora vstupu více než~jedné periferie naraz a~podpora přeřazení vstupu (např.~změna tlačítka odpovídající za~skok herní postavy). Předtím tohle a~spoustu dalšího se~muselo naprogramovat ručně. -\item Podporu vektorové grafiky v~UI. Veškeré staré UI~elementy byly~tvořeny pomocí základních vektorových obdélníkových tvarů práve proto, aby~se~vyhnulo použití rastrové grafiky, která~je~velmi závislá na~rozlišení. Aktuálně všechny UI~elementy jsou tvořeny ještě starou metodou, pro~udržení konzistentního vzhledu. Dnes bychom určitě využili této~možnosti. +\item Nový systém zpracování vstupu Enhanced Input\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/enhanced-input-in-unreal-engine}. Místy má~příliš mnoho objektové abstrakce, ale~rozhodně velký krok vpřed. Umožňuje snadné dynamické přepínání různých sad ovládání (multiplayer hry, gameplay/menu), dynamické modifikátory a~spouštěče vstupu (změna senzitivity, víceklik, podržení tlačítka určitou dobu), podpora vstupu více než~jedné periferie naráz a~podpora přeřazení vstupu (např.~změna tlačítka odpovídající za~skok herní postavy). Předtím tohle a~spoustu dalšího se~muselo naprogramovat ručně. +\item Podporu vektorové grafiky v~UI. Veškeré staré UI~elementy byly~tvořeny pomocí základních vektorových obdélníkových tvarů právě proto, aby~se~vyhnulo použití rastrové grafiky, která~je~velmi závislá na~rozlišení. Aktuálně všechny UI~elementy jsou tvořeny ještě starou metodou, pro~udržení konzistentního vzhledu. V~budoucnu bychom určitě využili této~možnosti. \item Přepracované vykreslování textů -- rychlejší vykreslování a~efektivnější využití paměti. -\item MetaSound\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/metasounds-the-next-generation-sound-sources-in-unreal-engine} pro~přehrávaní nebo procedurální generování zvuků, který~nahrazuje starou třídu~Cue. De-facto se~jedná o~Digital Signal Processing (DSP) grafový engine a~editor. Bohůžel jsem nestihl tento nastroj využit v~práci. -\item World partition systém\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/world-partition-in-unreal-engine} který~dokáže automaticky rozdělit jeden velký svět na~streamovací kousky a~propojit sdílení dat mezi nimi i~při multiplayer hře přes internet. Taky není využit v~této práci. +\item MetaSound\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/metasounds-the-next-generation-sound-sources-in-unreal-engine} pro~přehrávaní nebo procedurální generování zvuků, který~nahrazuje starou třídu~Cue. De-facto se~jedná o~Digital Signal Processing (DSP) grafový engine a~editor. Bohůžel jsem nestihl tento nástroj využit v~práci. +\item World partition systém\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/world-partition-in-unreal-engine}, který~dokáže automaticky rozdělit jeden velký svět na~streamovací kousky a~propojit sdílení dat mezi nimi i~při multiplayer hře přes internet. Taky není využit v~této práci. \end{itemize} -Samozřejmě je~toho daleko víc, ale~většina ostatních vylepšení jako~například nový systémy animací postav Motion Matching\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/motion-matching-in-unreal-engine} nebo~nový fyzikální engine Chaos\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/destruction-overview} neměli vliv na~rozhodnutí. +Samozřejmě je~toho daleko víc, ale~většina ostatních vylepšení jako~rovněž nový systémy animací postav Motion Matching\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/motion-matching-in-unreal-engine} nebo~nový fyzikální engine Chaos\footnote{https://dev.epicgames.com/documentation/en-us/unreal-engine/destruction-overview} neměly vliv na~rozhodnutí. -Podrobné informace jak každá implementace funguje a jak vývojáři s ní pracovat jsou popsané v programátorské dokumentaci. +Podrobné informace jak~každá implementace funguje a~jak s~ní~pracovat, je~popsáno v~přiložené programátorské dokumentaci. \section{Herní logika, systémy, mechaniky} \label{sec:systemsAndMechanics} \subsection{Architektura} -Protože Unreal Engine byl na začátku vyvíjen primárně pro online multiplayer hru Unreal Tournament a dnes stejně tak vyvíjen spolu s extrémně velkou online multiplayer hrou Fortnite, je backend enginu velmi abstraktní. +Protože Unreal Engine byl~na~začátku vyvíjen primárně pro~online multiplayer hru Unreal Tournament a~dnes~je stejně~tak vyvíjen spolu s~extrémně velkou online multiplayer hrou Fortnite, je~backend enginu velmi abstraktní. -Například každý svět musí obsahovat vlastní game mode (třída AGameMode), který funguje jako správce daného světu -- přestože svět se může spravovat sám. Veškerá funkcionalita používaná v singleplayer hrách mohla by být přímo v kódu levelu nebo v globální instanci celé hry. Důvodem této abstrakce je právě nativní podpora multiplayer her, která potom vývoj takových her zjednodušuje. +Například každý svět musí obsahovat vlastní game mode (třída AGameMode), který~funguje jako~správce daného světa -- přestože svět se~může spravovat sám. Veškerá funkcionalita používaná v~singleplayer hrách mohla být přímo v~kódu levelu nebo~v~globální instanci celé hry. Důvodem této abstrakce je~právě nativní podpora multiplayer her, která~potom vývoj takových her zjednodušuje. -Protože tento projekt je zaměřen pro hru jednoho hráče, práce je často programováná navzdory ustáleným UE C++ guidelines pro pohodlí a rychlost vývoje. Proto často třídy manažerů systémů, game modů, instance hráče a další jsou na architektuře singletonu. Přináší to nejen zmíněné pohodlí, ale navíc šetří od tří do desetí různých volání getteru, cast reflexe, vyhledávání v hash tabulkách a iteraci polí v každém místě použití. Přesto nemá žádný vliv na stabilitu programu a dokonce šetří výkonem zařízení. +Protože tento projekt je~zaměřen pro~hru jednoho hráče, práce je~často programována navzdory ustáleným UE~C++ zásadám (guidelines) pro~pohodlí a~rychlost vývoje. Proto~často třídy manažerů systémů, game~modů, instance hráče a~další jsou na~architektuře singletonu. Přináší~to nejen zmíněné pohodlí, ale~navíc šetří od~tří do~deseti volání getterů, reflexí (castů), vyhledávání v~hash tabulkách a~iteraci polí v~každém místě použití. Přesto nemá žádný vliv na~stabilitu programu a~dokonce šetří výkonem zařízení. -Jen pro představu co obnáší klasické získání reference na instanci vlastní třídy hráče na pozadí. GetGameMode()\textrightarrow GetPlayerController(index)\textrightarrow GetPlayerPawn()\textrightarrow Cast(). A blueprinty disponují zkrácenou verzí GetPlayerPawn(index)\textrightarrow Cast(). V případě C++ je potřeba navíc ověřovat zda nějaké z volání nevrátilo nullptr. +Jen pro představu, co~obnáší klasické získání reference na~instanci vlastní třídy hráče na~pozadí. GetGameMode()\textrightarrow GetPlayerController(index)\textrightarrow GetPlayerPawn()\textrightarrow Cast(). A~blueprinty disponují zkrácenou verzí GetPlayerPawn(index)\textrightarrow Cast(). V~případě~C++ je~potřeba navíc ověřovat zda~nějaké z~volání nevrátilo nullptr. \subsection{Scény a ukládání hry} -Scény resp. levely (třídy UWorld a ALevelScriptActor)\footnote{UWorld instance je přímo celý svět, který funguje jako balík metadat a kontejner pro veškeré instancované objekty v něm. ALevelScriptActor je objekt instancovaný v UWorld automaticky a obsahuje uživatelskou logiku světa.} taktéž lze získat v podobě singletonu. Základní implementace byla rozšířená pro high level mechanismus volání událostí ve scéně. Ten je zapotřebí například, když hráč aktivuje spínací plošinu ve hře a ta následně otevře dveře. Narozdíl od jiných enginů, v UE objekty ve světě nemůžou referencovat jiné nezávislé objekty. Jednoduše referenci nejde přiřadit z důvodu abstrakce popsané v předchozí podsekci, protože každý objekt může mít vlastní herní svět (a nejen to). Proto pokud plošina z našeho príkladu chce otevřít dveře, musí zkusit poslat požádavek správcí herního světa, ten požadavek se nějak zpracuje a jen potom správce buď provede akci otevírání dveří samostatně nebo zavolá odpovídající funkci v instanci objektu. Pokud by byla potřeba zbavit se použití singleton architektury pro podporu paralelní existenci světů, stači předělat member funkci instance světa na statický multicast delegate\footnote{Delegate jsou obaly na C++ funkce, lambdy, funkce s reflexi a funkce z Blueprintu. Delegate může být typu single, pro uložení reference na jednou funkci, nebo multicast pro uložení dynamického seznamu funkcí. Podrobněji jsou popsané v programátorské dokumentaci.}, ke kterému se každý svět při konstrukci bude vázat. +Scény, resp. levely (třídy UWorld a ALevelScriptActor)\footnote{UWorld instance je~přímo celý svět, který~funguje jako~balík metadat a~kontejner pro~veškeré instancované objekty v~něm. ALevelScriptActor je~objekt instancovaný v~UWorld automaticky a~obsahuje uživatelskou logiku světa.}, lze~taktéž získat v~podobě singletonu. Základní implementace byla~rozšířená pro~high level mechanismus volání událostí ve~scéně. Ten~je~zapotřebí například, když~hráč aktivuje spínací plošinu ve~hře a~ta~následně otevře dveře. +\newpage +Na rozdíl od~jiných enginů, v~UE objekty ve~světě nemůžou referencovat jiné~nezávislé objekty. Jednoduše referenci nelze přiřadit z~důvodu abstrakce popsané v~předchozí podsekci, protože~každý objekt může~mít vlastní herní svět (a~nejen~to). Proto~pokud~plošina z~našeho príkladu chce otevřít dveře, musí~zkusit poslat požadavek správci herního světa, ten~požadavek se~nějak zpracuje a~teprvé~poté správce buď~provede akci otevírání dveří samostatně nebo~zavolá odpovídající funkci v~instanci objektu. Pokud~by~bylo potřeba opustit singleton architekturu pro~podporu paralelní existenci světů, stačí předělat členskou (member) funkci instance světa na~statický multicast delegate\footnote{Delegate jsou obaly na~C++ funkce, lambdy, funkce s~reflexi a~funkce z~Blueprintu. Delegate může~být typu single, pro~uložení reference na~jednou funkci, nebo~multicast pro~uložení dynamického seznamu funkcí. Podrobněji jsou~popsané v~programátorské dokumentaci.}, ke~kterému se~každý svět při~konstrukci bude vázat. -Navíc hra byla rozšířena o implementaci obnovení hry z úložených dat. Při návrhu byla snaha udělat postup nejvíc triviální. Hru ukládá globální herní instance (třída GameInstance)\footnote{GameInstance je první objekt vytvořený enginem hned po úvodním načtení základu enginu a taky poslední objekt na destrukci při vypínání hry.} při vypínání hry a načítá taktéž při spuštění. Využít byl existující v UE systém serializace, který ukládá inventář postavy, jmeno levelu a checkpoint na něm spolu s posledním stavem levelu. +Navíc hra byla rozšířena o~implementaci obnovení hry z~úložených dat. Při~návrhu byla~snaha udělat postup co~nejjednodušší. Hra se ukládá pomoci globální herní instance (třída GameInstance)\footnote{GameInstance je~první objekt vytvořený enginem hned po~úvodním načtení základu enginu a~taky poslední objekt na~destrukci při~vypínání hry.} při~vypínání hry a~načítá taktéž při~spuštění. Byl~využit existující systém serializace v~UE, který~ukládá inventář postavy, jméno levelu a~checkpoint na~něm spolu s~posledním stavem levelu. \subsection{Interakce} -Interakce jsou často řešeny pomoci architektury interface tříd\footnote{Interface třídy jsou konkurenčním přístupem komponentní architektuře. Interface je běžná praktika v OOP, která funguje jako domluva, že objekt bude obsahovat určité member funkce. Komponenty jsou samostatné určité podinstance objektu. Komponentní přístup se osvědčuje jako intuitivnější a více flexibilní zatímco Interface přístup při vývoji her je spíše nepříjemné vynucení ze světu OOP.} především kvůli populárním návodům vyzdvihující tuto metodu jako nejlepší ještě z doby, kdy Unreal Engine 4 pouze vznikal. Nevýhodou je potřeba v obsáhlém a repetitivním nastavení každého objektu, který chceme zapojit do mechaniky interakce. +Interakce jsou~často řešeny pomocí architektury interface tříd\footnote{Interface třídy jsou~konkurenčním přístupem komponentní architektuře. Interface je~běžná praktika v~OOP (Objektově Orientovaném Jazyce), která~funguje jako~domluva, že~objekt bude obsahovat určité member funkce. Komponenty jsou~samostatné určité podinstance objektu. Komponentní přístup se~osvědčuje jako~intuitivnější a~více flexibilní zatímco Interface přístup při~vývoji her je~spíše nepříjemné vynucení ze~světu~OOP.} především kvůli populárním návodům vyzdvihující tuto metodu jako~nejlepší ještě~z~doby, kdy~Unreal Engine~4 pouze vznikal. Nevýhodou je~potřeba v~obsáhlém a~repetitivním nastavení každého objektu, který~chceme zapojit do~mechaniky interakce. -Byl navržen komplexní systém pro správu každého interakčního objektu a komponent (viz.\Cref{fig:InteractableSystemDiagram}). Objekt dědicí třídu AInteractable při instancování samostatně nastaví a následně přepíná potřebné kolize. Navíc spravuje interakční komponenty a reaguje na požadavky k ním. Komponenty se dělí na aktivátory a modifikátory. +Byl navržen komplexní systém pro~správu každého interakčního objektu a~komponent (viz.\Cref{fig:InteractableSystemDiagram}). Objekt dědící třídu AInteractable při~instancování samostatně nastaví a~následně přepíná potřebné kolize. Zároveň spravuje interakční komponenty a~reaguje na~požadavky, které~jsou na~ně směřovány. Komponenty se~dělí na~aktivátory a~modifikátory. \begin{figure} \centering @@ -44,9 +46,11 @@ Byl navržen komplexní systém pro správu každého interakčního objektu a k \end{figure} \newpage -\paragraph{AInteractableActivator} Aktivátory jsou komponenty s určitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro sebe jednu podinstanci každého aktivátoru registrovaného v enginu. Libovolný objekt taky může obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci raytracingu a dereference objektu v případě nárazu paprsku. +\paragraph{AInteractableActivator} Aktivátory jsou~komponenty s~určitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro~sebe jednu podinstanci každého aktivátoru registrovaného v~enginu. Libovolný objekt může rovněž obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci ray~tracingu a~následného dereferencování dereference objektu v~případě nárazu paprsku. -Navíc je k dispozici detekce objektů v zorném poli hráče. Ta funguje na zpomaleném snímání určité stencil vrstvy\footnote{Stencil Buffer sousedí s Z-Bufferem a má pouze jeden osmi bitový celočíselný kanál. Slouží především jako pomocník při tvorbě specifických renderovacích technik a efektů. Nejběžnější použití je renderování obrysů objektů, označení objektů pro následné zpracování v nějaké render pipeline a tvorba portálů.} s vynecháním většiny render pipline a v malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v podobě masky. Následně je maska v grafickém vlákně zpracovaná pomocí algoritmu vyhledávání komponent z počítačového vidění, kterou poskytuje kníhovna OpenCV. Nakonec do středu nalezených komponent se promítne paprsek a zachytí objekty (viz. \Cref{fig:InteractableScreenCapture}). Původně bylo předpokládáno využití HLSL compute shaderu\footnote{High-Level Shader Language je proprietární shader jazyk používaný v DirectX.} pro vyhledání komponent v textuře, ale z časových důvodu jsem nedodělal přenos dat mezi CPU a GPU, jelikož dohledat dokumentaci o použití shaderu je dost náročné. Proto aktuální vyhledávání komponent je spouštěno na procesoru a v renderovacím vlákně na moderních zařízeních trvá pět až deset milisekund. V praxi se takový úkol řeší náhodným promítáním velkého množství paprsků nebo velkým hitboxem napodobující tvar frustrumu kamery. Taková řešení jsou sice rychlá na implementaci, ale začínají být nekvalitní až nepoužitelné pro trochu větší vzdálenosti než ,,pár metrů''. Navíc zahrnují časté počítání nárazů na velké množství objektů, čímž výkonnostně nejsou o nic lepší implementované metody v této práci. +Navíc je~k~dispozici detekce objektů v~zorném poli hráče. Ta~funguje na~zpomaleném snímání určité stencil vrstvy\footnote{Stencil Buffer sousedí s~Z-Bufferem a~má~pouze jeden osmi~bitový celočíselný kanál. Slouží především jako~pomocník při~tvorbě specifických renderovacích technik a~efektů. Nejběžnější použití je~renderování obrysů objektů, označení objektů pro~následné zpracování v~nějaké render pipeline a~tvorba portálů.} s~vynecháním většiny render pipeline a~v~malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v~podobě masky. Následně je~maska v~grafickém vlákně zpracovaná pomocí algoritmu vyhledávání komponent z~počítačového vidění, kterou~poskytuje knihovna OpenCV. Nakonec do~středu nalezených komponent se~promítne paprsek a~zachytí objekty (viz.~\Cref{fig:InteractableScreenCapture}). + +Původně bylo předpokládáno využití HLSL compute shaderu\footnote{High-Level Shader Language je~proprietární shader jazyk používaný v~DirectX.} pro~vyhledání komponent v~textuře, ale~z~časových důvodů jsem nedodělal přenos dat mezi CPU a~GPU, jelikož dohledat dokumentaci o~použití shaderu je~poměrně náročné. Proto~aktuální vyhledávání komponent je~spouštěno na~procesoru a~v~renderovacím vlákně na~moderních zařízeních trvá pět až~deset milisekund. V~praxi se~tento problém často řeší náhodným promítáním velkého množství paprsků nebo~velkého hitboxu napodobujícího tvar pohledového frustrumu kamery. Tato řešení jsou sice~rychlá na~implementaci, avšak~při~větších vzdálenostech (nad pár metrů) vykazují výrazné snížení přesností nebo zcela neefektivními. Navíc zahrnují časté počítání nárazů na~velké množství objektů, čímž~výkonnostně nejsou o~nic~lepší metody implementované v~této práci. \begin{figure} \centering @@ -55,27 +59,30 @@ Navíc je k dispozici detekce objektů v zorném poli hráče. Ta funguje na zpo \label{fig:InteractableScreenCapture} \end{figure} -\paragraph{AInteractableModificator} Modifikátory jsou komponenty s určitou logikou libovolné modifikace objektu, ve kterém jsou instancovány. Tyto komponenty mohou obsahovat pouze objekty označené jako interakční (dědí třídu interakčního objektu). Aktuální implementace nabízí modifikátory pro aktivaci nějaké události pomoci dosahu ,,ruky'' nebo zahlednutím ,,očí'' hráče, pohyb a rotace předmětu v prostoru, ukládání předmětu do inventáře hráče. Z časových důvodu byla zamítnuta implementace modifikace meshe objektu, která by využívala nového rozhraní Chaos. +\paragraph{AInteractableModificator} Modifikátory jsou~komponenty s~určitou logikou libovolné modifikace objektu, ve~kterém jsou~instancovány. Tyto~komponenty mohou obsahovat pouze objekty označené jako~interakční (dědí třídu interakčního objektu). Aktuální implementace nabízí modifikátory pro~aktivaci nějaké události pomoci dosahu~,,ruky'' nebo zahlédnutím~,,očima'' hráče, pohyb a~rotace předmětu v~prostoru, ukládání předmětu do~inventáře hráče. -Právě u modifikátorů nejvíc přispěl nový systém zpracování vstupu pro Unreal Engine 5. Modifikatory využívájí možnosti přidávání resp. odstranění kontextů vstupu za běhu. +Z~časových důvodů byla zamítnuta implementace modifikace geometrie objektu, která~by~využívala nového rozhraní Chaos. + +Největší přínos nového systému zpracování vstupu v~Unreal Engine~5 se~projevil právě u~modifikátorů. Ty~využívají možnost dynamického přidávání nebo~odstraňování vstupních kontextů za~běhu. \subsection{Cutscény a Quick Time Events} -Unreal je slavný, jak dobře lze v něm animovat scénu. Rozhraní a editor systému Sequencer je pohodlné a bohaté na funkcionality. Bohůžel veškeré skvěle věci končí mimo editor, jelikož chybí pohodlná funkcionalita režie různých souborů animací (stejná poznámka se vztahuje i k UI animacím). Proto byl dodán systém UCutsceneManager implementující frontu animací spolu s uživatelským rozhraním přeskočení animace. +Unreal Engine je~známý~tím, jak~dobře umožňuje animovát scény. Rozhraní a~editor systému Sequencer je~přívětivé a~nabízejí široké možnosti. Bohužel~však většina těchto výhod končí mimo samotný editor, jelikož~chybí pohodlné prostředky pro~režii různých souborů animací (totéž platí i~pro~UI animace). Proto byl~dodán systém UCutsceneManager implementující frontu animací spolu s~uživatelským rozhraním pro~jejich přeskakování. -Pro přiblížení problému, vyjmenuji časté potřeby, které vznikají při běžném použití animací. +Pro~přiblížení problému, vyjmenuji časté potřeby, které~vznikají při~běžném použití animací. \begin{itemize} -\item Zároveň spuštěné animace bojují o vlastnictví objektů, +\item Zároveň spuštěné animace bojují o~vlastnictví objektů, \item animace nelze přetáčet, -\item u animace nelze zjistit zpětně jestli byla přehrana do konce, pouze přivázat volání funkce po ukončení, -\item animace umí říct jestli je někde přehráváná, ale neřekne kde. +\item u~animace nelze zjistit zpětně zda~byla přehrána až~do~konce, pouze přivázat volání funkce po~ukončení, +\item systém dokáže určit, zda~se~animace někde přehrává, ale~není schopen ukázat, kde~přesně. \end{itemize} -Převážně pro použití v animacích je implementována fronta nečekaných událostí (QTE - Quick Time Events), což jsou interaktivní UI elementy na obrazovce hráče. V herním průmyslu jsou využívány zejména v cutscénách, pro ,,hlubší ponoření'' hráče do děje. Klasickými příklady jsou animace šplhání herní postavy po nějaké překážce nebo kinematografický souboj mezi postavy. Při takových událostech lze přiblížit hráče k dynamické a napínavé situaci pomoci klikání na stejně dynamické UI elementy na obrazovce v souladu s pohybem herní postavy. V praxi se objevují i pokročilejší varianty napodobení děje pohybem myší a joystickem nebo gyroskopem ovládače. V implementaci jsou k dispozici eventy jednotného a víc-krátného klikání a držení tlačítka v časovém intervalu. +Převážně pro~použití v~animacích je~implementována fronta rychlých interaktivních událostí (QTE - Quick Time Events), což~jsou~interaktivní UI~elementy na~obrazovce hráče. V~herním průmyslu jsou~využívány zejména v~cutscénách, pro~,,hlubší ponoření'' hráče do~děje. Klasickými příklady jsou~animace šplhání herní postavy po~nějaké překážce nebo~kinematografický souboj mezi postavami. Při~takových událostech lze~přiblížit hráče k~dynamické a~napínavé situaci pomocí klikání na~stejně dynamické UI~elementy na~obrazovce v~souladu s~pohybem herní postavy. V~praxi se~objevují i~pokročilejší varianty napodobení děje pohybem myší a~joystickem nebo~gyroskopem ovládače. V~implementaci jsou~k~dispozici eventy jednotného a~vícenásobného klikání a~držení tlačítka v~časovém intervalu. \subsection{Dialogy} -V enginu je zavádějící implementace pokročilého dialogového systému. Dialogy jsou neintuitivní pro tvorbu a použití, vyžadují časté repetitivní kopírování stejných parametrů mezi souborů dialogů a k tomu celý systém má nedostačující dokumentace. Navíc ,,dialogy'' v tomto systému jsou pouze samostatné věty, které musí přehrávat nějaký zvuk a mohou se vybírat v závislosti na omezeném kontextu typu "kdo na koho mluví". O nepoužitelností takového rozhrání napovídají návody na tvorbu vlastních dialogů nebo vývojářský obchod plný pluginů implementujících tento systém lépe. +V~enginu je~zavádějící implementace pokročilého dialogového systému. Dialogy jsou~neintuitivní pro~tvorbu a~použití, vyžadují časté repetitivní kopírování stejných parametrů mezi~soubory dialogů a~k~tomu celý systém má~nedostatečnou dokumentaci. Navíc~,,dialogy'' v~tomto systému jsou~pouze samostatné věty, které~musí přehrávat nějaký zvuk a~mohou se~vybírat v~závislosti na~omezeném kontextu typu "kdo~na~koho mluví". O~nepoužitelností takového rozhraní svědčí návody na~tvorbu vlastních dialogů nebo~vývojářský obchod plný pluginů implementujících tento systém lépe. -Protože kvalitná řešení jsou placená a ty zdarma jsou často nevyhovující kvality, byla navržená vlastní implementace. Jedna se o frontu, která umí přehrávat celé tabulky dialogových vět. Dialog resp. množinu vět společného kontextu lze pohodlně zapsat do tabulkového formátu přímo v editoru. Jedná věta obsahuje id, text, dobu přehrání nebo zvukovou stopu. Ve výsledku lze do fronty zařadit několik tabulek, které se mohou přehrávat sekvenčně od n-té věty do poslední, jednu náhodnou větu nebo přesně jednu větu podle id. +\newpage +Protože kvalitní řešení jsou~placená a~ty~zdarma jsou~často nevyhovující kvality, byla~navržená vlastní implementace. Jedná~se o~frontu, která~umí přehrávat celé tabulky dialogových vět. Dialog, resp.~množinu vět společného kontextu, lze~pohodlně zapsat do~tabulkového formátu přímo v~editoru. Jednotlivá věta obsahuje id, text, dobu přehrání nebo~zvukovou stopu. Ve~výsledku lze~do~fronty zařadit několik tabulek, které~se~mohou přehrávat sekvenčně od~n-té věty po~poslední, jednu náhodnou větu nebo~přesně jednu větu podle id. \subsection{Minihry} Gameplay druhého levlu je celý složen z miniher. V moment vydání -- práce obsahuje pouze pět hlavních pro game design miniher v podobě ,,proof of concept''. Z časových důvodů zbylé vedlejší minihry nebyly implementovány. Každá z hlavních miniher je parodie na již existující známe hry. @@ -85,14 +92,14 @@ Minihry jsou samostatné objekty, které manažer scény restartuje nebo vypín Dostupné zkušební implementace: \begin{itemize} \item Parodie flash hry Age of War. V pozadí uklízení knížek dějepisu jsou umístěny základny hráče a soupeře (umělé inteligence). Hráč ovládá minihru pomici UI, kde nakupuje jednotky typu: pěšák, střelec a tank. Peníze získává za zničení nepřátelských jednotek. Vyhrává soupeř, který dokázal přejít přes jednotky nepřátele až k základně a zdemolovat ji. V aktuální implementaci chybí vylepšení éry jednotek, zvuky, grafické efekty, modely a animace jednotek. Přesto lze vyzkoušet nakup a jejich ,,souboj'' jednotek. -\item Parodie na mobilní hru Subway Surfers. Aktuálně má minihra nevhodné pozadí, neobsahuje audio a grafické prvky. Podle návrh hráč nahání veverku v lese, která mu ukradla část důležitého pro příběh předmětu. Pomoci pohybů nahoru, dolu, doleva a vpravo hráč uhýbá objevujícím se lesním překážkam. Překážková dráha má šířku třech běžících pruhů a překážky mohou vyžadovat přeskočení, sklouznutí nebo úhyb do strany. +\item Parodie na mobilní hru Subway Surfers. Aktuálně má minihra nevhodné pozadí, neobsahuje audio a grafické prvky. Podle návrh hráč nahání veverku v lese, která mu ukradla část důležitého pro příběh předmětu. pomocí pohybů nahoru, dolu, doleva a vpravo hráč uhýbá objevujícím se lesním překážkam. Překážková dráha má šířku třech běžících pruhů a překážky mohou vyžadovat přeskočení, sklouznutí nebo úhyb do strany. \item Podoba počítačové rytmické hry Osu!. Hráč má za úkol klikat na objevující se na obloze hvězdičky v souladu s rytmem hudby (v pořadí ve kterém se objevily). Aktuálně ,,hvězdičky'' jsou v podobě čtverečků, ale hudba je zcela hotova. Již teď lze vyzkoušet klikání pro prvních 10 sekund doprovodu. -\item Podoba mobilní hry Crossy Road. Hráč pomoci pohybu dopředu, dozadu, doleva a dolu potřebuje posouvat se vpřed po překážkové dráze. Dráha je složená z pěti pruhů kolmých ke kameře, a každý pruh je složen z dvanáctí políček, na které může stoupnout hráč. Náhodně na krajích pruhu se objevují divoká zvířata, laviny a silný vítr (v originální hře to jsou auta na silnici), které se posouvají k opačnému kraji přes všechna políčka a mohou ukončit běh hráče. Podle návrhu se minihra odehrává v ledovém prostředí, kde hráč prochází sněžnou krajinou. Audiovizuální prvky opět nejsou k dispozici, ale již teď teď lze vyzkoušet posun dopředu a úhyb překážkam. +\item Podoba mobilní hry Crossy Road. Hráč pomocí pohybu dopředu, dozadu, doleva a dolu potřebuje posouvat se vpřed po překážkové dráze. Dráha je složená z pěti pruhů kolmých ke kameře, a každý pruh je složen z dvanáctí políček, na které může stoupnout hráč. Náhodně na krajích pruhu se objevují divoká zvířata, laviny a silný vítr (v originální hře to jsou auta na silnici), které se posouvají k opačnému kraji přes všechna políčka a mohou ukončit běh hráče. Podle návrhu se minihra odehrává v ledovém prostředí, kde hráč prochází sněžnou krajinou. Audiovizuální prvky opět nejsou k dispozici, ale již teď teď lze vyzkoušet posun dopředu a úhyb překážkam. \item Jednoduchá minihra rybolovu (více podob napříč různými hry). Hráč má na obrazovce svislý obdelníkový indikátor znázorňující vodní hlubinu. Podél indikátoru se náhodně pohybuje obrázek rybičky, kterou hráč musí udržovat v malé pohyblivé zóně. Zóna se pohybuje automaticky dolu nebo nahoru držením tlačítka myší (rychlost pohybu není lineární, ale kvadratická). V implementaci chybí zvukové assety a vyváženost složitosti, ale je k dispozici kompletní UI a mechanika lovu. \end{itemize} \subsection{Nehratelné postavy} -Pro tvorbu NPC (Non-Playable Characters) byli použité základní prostředky enginu. Podle dizajnu hry, k dispozici by měli být tři druhy logiky pro NPC, ale z časových důvodu jsou implementovány pouze dvě. Jedno z chování je pouhá chůze k dynamickým bodům. Toto základní chování se potom rozděluje na střežení oblasti nebo boj s hráčem. +Pro tvorbu NPC (Non-Playable Characters) byli použité základní prostředky enginu. Podle dizajnu hry, k dispozici by měli být tři druhy logiky pro NPC, ale z časových důvodů jsou implementovány pouze dvě. Jedno z chování je pouhá chůze k dynamickým bodům. Toto základní chování se potom rozděluje na střežení oblasti nebo boj s hráčem. Postavy, které střeží oblast využíváji nový pro UE5 systém vjemů nehratelných postav. Postavy tak umí reagovat na zvuky, dotyky nebo zahlédnutí jiných objektů. Toto je využito pro postavy, které hlídají vězení v levelu 4. Ty patrolují předem navržené trasy a spouští určité akce až zahlédnou hráče. @@ -105,7 +112,7 @@ Hra využívá existující v enginu de/serializace v textovém formátu ,,.ini' Triviálně se pracuje s preferencemi hráče, které určují hlasitost různých kategorií zvuků nebo preferencemi ovládání. Tam stačí pouze přečíst resp. zapsat hodnotu. Jednoduché to přestává být v momentě nastavení kvality grafiky a parametrů zobrazení okna aplikace. Zejmená změny zaměřené na okno aplikace mohou zhavarovat a vypnout hru nebo ještě hůř zablokovat vstup celého počítače a donutit uživatele ke kompletnímu restartování zařízení. Takové situace mohou nastat celkem běžně, a nemusí znamenát problem v aplikaci. Například předčasný zásah OS k neodpovídajícímu procesu (hra se delší dobu načítá po aplikaci nového nastavení) nebo nevhodná reprezentace rozlišení, které monitor nebo GPU uživatele nepodporuje. -V libovolném případě hra musí být schopna obstát nečekáné závady, a proto byl navržen mechanismus obnovení předchozích funkčních nastavení. Po zvolení nových parametrů a jejich aplikaci, hra nejprv uloži stávájící nastavení a až poté aplikuje ty nová. Po aplikovaní a až se proces okna vrátí zpět do renderujícího stavu, v menu se objeví okénko po dobu pěti sekund očekávající schválení od uživatele. Pokud uživatel stihne potvrdit úspěšnou změnu parametrů, jsou uložené jako funkční. Pokud uživatel z libovolného důvodu změnu neschválí, potom jsou obnoveny předchozí parametry. +V libovolném případě hra musí být schopna obstát nečekáné závady, a proto byl navržen mechanismus obnovení předchozích funkčních nastavení. Po zvolení nových parametrů a jejich aplikaci, hra nejprv uloži stávájící nastavení a až poté aplikuje ty nová. Po aplikovaní a až se proces okna vrátí zpět do renderujícího stavu, v menu se objeví okénko po dobu pěti sekund očekávající schválení od uživatele. Pokud uživatel stihne potvrdit úspěšnou změnu parametrů, jsou uložené jako funkční. Pokud uživatel z libovolného důvodů změnu neschválí, potom jsou obnoveny předchozí parametry. Navíc právě nastavení zobrazení okna (a výber zhlazovácí metody) byly ručně implementovány, jelikož nejsou volně k dispozici v high-level implementaci enginu. Jedná se o možnost výběru rozlišení a jeho třídění podle poměru stran, změná režimu vykreslování okna (neokenní, okenní bez rámce, okenní s rámcem), změná obnovovací frekvence. Při výběru a nastavení rozlišení se pracuje napřímo s DirectX\footnote{DirectX je množina API pro práci s grafikou a převážně pro počítačové hry.} rozhraním a zhlazovací metoda se nastavuje přes příkazové rozhraní enginu. @@ -130,7 +137,7 @@ Architektura generování obsahu je založená na použití zřetězení různý Dotrénování může mít více podob. Nejspíš by se jednalo o fine-tuning programovácího modelu v C++ s dodatečnou Unreal Engine syntaxí nebo fine-tuning tvorby diagramů v Blueprintech. Výsledné blueprinty potom lze konvertovat do python příkazů, které vytvoří vygenerovaný diagram v editoru. Nelze jednoznačně říct co je lepší, obě metody vyžadují ověřit. Pro C++ modely je víc dat a k tomu toto prostředí má více kreativní svobody, přestože může zhavarovat aplikaci. Blueprinty jsou zdaleka bezpečnější, ale na oplátku bude těžší sbírat trénovací data. \paragraph{Postup generování} -Vše by začínálo v textovém modelu, který vytvoří typ a popis generováného objektu. Pomocí třídícího mechanismu se vybere potřebná množina ostatních modelů, kontrolních mechanismu a konvertovácích nástrojů. Asset bude postupně procházet takovým řetězcem až nabyde finální podoby. Na konci bude vždy kontrolní mechanismus, který zvaliduje soubor a ověří funkčnost assetu pomoci unit-testů ve hře. Pokud asset neprojde validací, celý postup, soubor a vstupní popis se zalogují pro budoucí ruční fine-tuning. +Vše by začínálo v textovém modelu, který vytvoří typ a popis generováného objektu. Pomocí třídícího mechanismu se vybere potřebná množina ostatních modelů, kontrolních mechanismu a konvertovácích nástrojů. Asset bude postupně procházet takovým řetězcem až nabyde finální podoby. Na konci bude vždy kontrolní mechanismus, který zvaliduje soubor a ověří funkčnost assetu pomocí unit-testů ve hře. Pokud asset neprojde validací, celý postup, soubor a vstupní popis se zalogují pro budoucí ruční fine-tuning. Příklad tvorby interakčního objektu: \begin{enumerate} @@ -145,15 +152,15 @@ Příklad tvorby interakčního objektu: \end{enumerate} \subsection{Poskytování obsahu} -\paragraph{Nasazování generátoru} Nezávislé řetězce modelů zjednodušují řízení sítě workerů. Pomoci docker containeru\footnote{Docker je software umožňující lokální virtualizaci prostředí nazývané containery.} nebo nasazovacího skriptu by se snadno vytvořil další nový worker (naše generující jednotka), který může ihned začít s generováním. Vygenerovaný obsah poté může být ihned přemístěn na veřejné úložiště, a proto worker nepotřebuje nijak velké úložiště. Protože workery nemusí odpovídat na požadavky uživatele v reálnem čase a plánovaná doba jedné hry je 15-30 minut nemusí generátory být rychlé a tedy ani náročné na výpočetní prostředky. Nezáleží nám ani na dlouhodobé životností workeru, jelikož modely se často sekvenčně přepínají resp. běží vždy pouze jeden model najednou. Takový přístup přináší flexibilitu v řízení prostředků a umožňuje ukládat mezistavy generátorů. Dokonce nepotřebujeme nijak velkou propustnost sítě, protože jednoduché assety nemohou přesahovat velikost jednotky megabajtů a máme větší časový intervaly mezi vznikem souborů. +\paragraph{Nasazování generátoru} Nezávislé řetězce modelů zjednodušují řízení sítě workerů. pomocí docker containeru\footnote{Docker je software umožňující lokální virtualizaci prostředí nazývané containery.} nebo nasazovacího skriptu by se snadno vytvořil další nový worker (naše generující jednotka), který může ihned začít s generováním. Vygenerovaný obsah poté může být ihned přemístěn na veřejné úložiště, a proto worker nepotřebuje nijak velké úložiště. Protože workery nemusí odpovídat na požadavky uživatele v reálnem čase a plánovaná doba jedné hry je 15-30 minut nemusí generátory být rychlé a tedy ani náročné na výpočetní prostředky. Nezáleží nám ani na dlouhodobé životností workeru, jelikož modely se často sekvenčně přepínají resp. běží vždy pouze jeden model najednou. Takový přístup přináší flexibilitu v řízení prostředků a umožňuje ukládat mezistavy generátorů. Dokonce nepotřebujeme nijak velkou propustnost sítě, protože jednoduché assety nemohou přesahovat velikost jednotky megabajtů a máme větší časový intervaly mezi vznikem souborů. -\paragraph{Stahování obsahu}\label{par:contentSharing} Hra automaticky stahuje a aktivuje obsah před začátkem nové hry. Z podrobné analýzy jsem rozhodnul, že nejsou zapotřebí žádné autentifikační nebo šifrovací vrstvy. Vždy by byla možnost reverse-engineeringem\footnote{Metoda analýzy hotového produktu (v našem případě binárního souboru), pro získání neveřejných informací popisujících funkčnost produktu.} získat klíč z binárních souborů hry a napadnout celý systém. Jestli se zamyslet, volný přístup k generovaným assetům nemá žádné zápory. Je jednoduchý na implementaci a udržbu. Je ale očekáváné přípojení pomoci HTTPS\footnote{Hypertext Transfer Protocol Secure je protokol pro šifrovaný přenos dat využívaný pro poskytování webových serverů.} a ověření certifikátu\footnote{Certifikáty v digitální podobě jsou řetězcem veřejných asymetrických klíčů různých vydavatelů. Každý předchozí vydavatel ručí za důvěryhodnost dalšího vydavatele.} s kopii uloženou v aplikaci. Mohlo by totiž dojít k man-in-the-middle útoku\footnote{Man-in-the-middle (MITM) je druh kyberútoku, ve kterém útočník tajně sleduje nebo upravuje komunikaci mezi dvěma uzly.} a hráč by mohl spustit ve hře nebezpečný obsah. +\paragraph{Stahování obsahu}\label{par:contentSharing} Hra automaticky stahuje a aktivuje obsah před začátkem nové hry. Z podrobné analýzy jsem rozhodnul, že nejsou zapotřebí žádné autentifikační nebo šifrovací vrstvy. Vždy by byla možnost reverse-engineeringem\footnote{Metoda analýzy hotového produktu (v našem případě binárního souboru), pro získání neveřejných informací popisujících funkčnost produktu.} získat klíč z binárních souborů hry a napadnout celý systém. Jestli se zamyslet, volný přístup k generovaným assetům nemá žádné zápory. Je jednoduchý na implementaci a udržbu. Je ale očekáváné přípojení pomocí HTTPS\footnote{Hypertext Transfer Protocol Secure je protokol pro šifrovaný přenos dat využívaný pro poskytování webových serverů.} a ověření certifikátu\footnote{Certifikáty v digitální podobě jsou řetězcem veřejných asymetrických klíčů různých vydavatelů. Každý předchozí vydavatel ručí za důvěryhodnost dalšího vydavatele.} s kopii uloženou v aplikaci. Mohlo by totiž dojít k man-in-the-middle útoku\footnote{Man-in-the-middle (MITM) je druh kyberútoku, ve kterém útočník tajně sleduje nebo upravuje komunikaci mezi dvěma uzly.} a hráč by mohl spustit ve hře nebezpečný obsah. -Hra zahájí stahování pomoci API, který vybere nějaké soubory. V základu assety jsou vybírány náhodně, ale zároveň se používá hodnocení získané od hráčů, které váhově lehce mění hustotu pravděpodobnosti normálního rozdělení. +Hra zahájí stahování pomocí API, který vybere nějaké soubory. V základu assety jsou vybírány náhodně, ale zároveň se používá hodnocení získané od hráčů, které váhově lehce mění hustotu pravděpodobnosti normálního rozdělení. \paragraph{Hodnocení obsahu} Potom co hráč zahraje s úrčitým assetem, máme možnost získat odezvu od hráče. Hodnocení využíjeme k váhové manipulaci náhodného výběru a zároveň jako data pro doládění generátorů. Návrh systému hodnocení staženého obsahu je již náročnější problém. Úkolem je umožnit pouze unikátní hodnocení a pouze od hráčů, které si s tímto obsahem opravdu zahráli. Protože nemáme žádnou authentifikaci, volné hodnocení nemusí fungovat. Kdokoliv může spamováním falešných hodnocení přemístit špatně hodnocený obsah do kategorie lepších a naopak. Authentifikáce tomu taky nezabraní, pouze oddálí takovou situaci. -Některé hry v praxi používájí sofistikováné metody využívájící detekci nelegální kopie hry, nebo vyžití API herních obchodů (dosažení, odznaky, id účtu atd.). Takové metody se zdají být účinné, ale už dávno se lehce obchází pomoci triviální simuláce API odezvy. +Některé hry v praxi používájí sofistikováné metody využívájící detekci nelegální kopie hry, nebo vyžití API herních obchodů (dosažení, odznaky, id účtu atd.). Takové metody se zdají být účinné, ale už dávno se lehce obchází pomocí triviální simuláce API odezvy. Táto práce nabízí sledování id účtu hráče, který hru zahrál a jeho seznam stažených souboru, pokud hra bude šířená na platformě Steam. UUID (Unique User Identificator) účtu ví pouze vlastník účtu, čili je to citlivá informace. Proto před odesláním takových dat budeme je vždy šifrovat asymetrickým klíčem (veřejným certifikátem webu ze kterého zároveň obsah stahujeme). Až bude hráč chtít ohodnotit ve hře obsah se kterým zahrál, API zkontroluje zda UUID hráče v Steam opravdu vlastní hru, že takový UUID stahovalo tento obsah z našeho serveru a že toto UUID ještě tento obsah nehodnotilo. -- 2.45.2 From 0188a8bca77626e6ff5b2a8f894b10ae6a7c1d09 Mon Sep 17 00:00:00 2001 From: Oleg Petruny Date: Tue, 27 May 2025 12:12:31 +0200 Subject: [PATCH 7/8] up to settings review --- ch2.tex | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ch2.tex b/ch2.tex index ed03f45..9e2fced 100644 --- a/ch2.tex +++ b/ch2.tex @@ -85,27 +85,27 @@ V~enginu je~zavádějící implementace pokročilého dialogového systému. Dia Protože kvalitní řešení jsou~placená a~ty~zdarma jsou~často nevyhovující kvality, byla~navržená vlastní implementace. Jedná~se o~frontu, která~umí přehrávat celé tabulky dialogových vět. Dialog, resp.~množinu vět společného kontextu, lze~pohodlně zapsat do~tabulkového formátu přímo v~editoru. Jednotlivá věta obsahuje id, text, dobu přehrání nebo~zvukovou stopu. Ve~výsledku lze~do~fronty zařadit několik tabulek, které~se~mohou přehrávat sekvenčně od~n-té věty po~poslední, jednu náhodnou větu nebo~přesně jednu větu podle id. \subsection{Minihry} -Gameplay druhého levlu je celý složen z miniher. V moment vydání -- práce obsahuje pouze pět hlavních pro game design miniher v podobě ,,proof of concept''. Z časových důvodů zbylé vedlejší minihry nebyly implementovány. Každá z hlavních miniher je parodie na již existující známe hry. +Herní průchod druhého levlu je~celý složen z~miniher. V~době vydání práce obsahuje pouze pět~hlavních miniher v~podobě ,,proof of~concept''. Z~časových důvodů nebyly vedlejší minihry implementovány. Každá z~miniher je~parodie na~již existující známou hru. -Minihry jsou samostatné objekty, které manažer scény restartuje nebo vypíná. Po nastartování minihra sama přepne kontext ovládání na sebe a řídí vlastní stav. Jestli minihra nebyla ukončena dočasně ale dohraná, potom vráti ovládání hráči a spolu s tím výsledek a skóre. +Minihry jsou samostatné objekty, které~může správce scény restartovat nebo~vypnout. Po~spuštění si~minihra sama přepne kontext ovládání na~sebe a~řídí svůj vlastní stav. Jestli~minihra nebyla ukončena dočasně, ale~dohraná, vráti ovládání zpět hráči spolu s~výsledkem a~skóre. Dostupné zkušební implementace: \begin{itemize} -\item Parodie flash hry Age of War. V pozadí uklízení knížek dějepisu jsou umístěny základny hráče a soupeře (umělé inteligence). Hráč ovládá minihru pomici UI, kde nakupuje jednotky typu: pěšák, střelec a tank. Peníze získává za zničení nepřátelských jednotek. Vyhrává soupeř, který dokázal přejít přes jednotky nepřátele až k základně a zdemolovat ji. V aktuální implementaci chybí vylepšení éry jednotek, zvuky, grafické efekty, modely a animace jednotek. Přesto lze vyzkoušet nakup a jejich ,,souboj'' jednotek. -\item Parodie na mobilní hru Subway Surfers. Aktuálně má minihra nevhodné pozadí, neobsahuje audio a grafické prvky. Podle návrh hráč nahání veverku v lese, která mu ukradla část důležitého pro příběh předmětu. pomocí pohybů nahoru, dolu, doleva a vpravo hráč uhýbá objevujícím se lesním překážkam. Překážková dráha má šířku třech běžících pruhů a překážky mohou vyžadovat přeskočení, sklouznutí nebo úhyb do strany. -\item Podoba počítačové rytmické hry Osu!. Hráč má za úkol klikat na objevující se na obloze hvězdičky v souladu s rytmem hudby (v pořadí ve kterém se objevily). Aktuálně ,,hvězdičky'' jsou v podobě čtverečků, ale hudba je zcela hotova. Již teď lze vyzkoušet klikání pro prvních 10 sekund doprovodu. -\item Podoba mobilní hry Crossy Road. Hráč pomocí pohybu dopředu, dozadu, doleva a dolu potřebuje posouvat se vpřed po překážkové dráze. Dráha je složená z pěti pruhů kolmých ke kameře, a každý pruh je složen z dvanáctí políček, na které může stoupnout hráč. Náhodně na krajích pruhu se objevují divoká zvířata, laviny a silný vítr (v originální hře to jsou auta na silnici), které se posouvají k opačnému kraji přes všechna políčka a mohou ukončit běh hráče. Podle návrhu se minihra odehrává v ledovém prostředí, kde hráč prochází sněžnou krajinou. Audiovizuální prvky opět nejsou k dispozici, ale již teď teď lze vyzkoušet posun dopředu a úhyb překážkam. -\item Jednoduchá minihra rybolovu (více podob napříč různými hry). Hráč má na obrazovce svislý obdelníkový indikátor znázorňující vodní hlubinu. Podél indikátoru se náhodně pohybuje obrázek rybičky, kterou hráč musí udržovat v malé pohyblivé zóně. Zóna se pohybuje automaticky dolu nebo nahoru držením tlačítka myší (rychlost pohybu není lineární, ale kvadratická). V implementaci chybí zvukové assety a vyváženost složitosti, ale je k dispozici kompletní UI a mechanika lovu. +\item Parodie flash hry Age of~War. V~pozadí scény s~úklidem učebnic dějepisu jsou~umístěny základny hráče a~soupeře (umělé inteligence). Hráč ovládá minihru pomicí~UI, kde~nakupuje jednotky typu: pěšák, střelec a~tank. Peníze získává za~zničení nepřátelských jednotek. Vyhrává ten, kdo~dokáže proniknout přes obranu nepřítele až~k~jeho základně a~zničit~ji. V~aktuální implementaci chybí vylepšení jednotek, zvuky, grafické efekty, modely a~animace jednotek. Přesto~lze vyzkoušet nákup a~,,souboj'' jednotek. +\item Parodie na~mobilní hru Subway Surfers. Aktuálně má~minihra nevhodné pozadí, neobsahuje audio a~grafické prvky. Podle návrhu hráč nahání veverku v~lese, která~mu ukradla část důležitého pro~příběh předmětu. Pomocí pohybů nahoru, dolů, vlevo a~vpravo se~hráč vyhýbá objevujícím se~překážkam v~lese. Překážková dráha má~šířku tří~běžeckých pruhů a~překážky mohou vyžadovat přeskočení, sklouznutí nebo~úhyb do~strany. +\item Podoba počítačové rytmické hry Osu!. Hráč má~za~úkol klikat na~objevující~se na~obloze hvězdičky v~souladu s~rytmem hudby (v~pořadí ve~kterém se~objevily). Aktuálně ,,hvězdičky'' jsou~v~podobě čtverečků, ale~hudba je~zcela hotova. Již~teď lze vyzkoušet klikání během prvních 10~sekund hudby. +\item Podoba mobilní hry Crossy Road. Hráč pomocí pohybu dopředu, dozadu, doleva a~dolů potřebuje posouvat se~vpřed po~překážkové dráze. Dráha je~složená z~pěti~pruhů kolmých ke~kameře, a~každý pruh je~složen z~dvanácti políček, na~které~může stoupnout hráč. Náhodně na~krajích pruhu se~objevují divoká zvířata, laviny a~silný vítr (v~originální hře to~jsou auta na~silnici), které~se~posouvají k~opačnému kraji přes všechna políčka a~mohou ukončit běh hráče. Podle návrhu se~minihra odehrává v~ledovém prostředí, kde~hráč prochází sněžnou krajinou. Audiovizuální prvky opět nejsou k~dispozici, ale~již~teď lze vyzkoušet posun dopředu a~úhyb překážkám. +\item Jednoduchá minihra rybolovu (více podob napříč různými hry). Hráč má~na~obrazovce svislý obdélníkový indikátor znázorňující vodní hlubinu. Podél indikátoru se~náhodně pohybuje obrázek rybičky, kterou~hráč musí udržovat v~malé pohyblivé zóně. Zóna se~pohybuje automaticky dolů nebo~nahoru držením tlačítka myší (rychlost pohybu není lineární, ale~kvadratická). V~implementaci chybí zvukové assety a~vyváženost složitosti, ale~je k~dispozici kompletní~UI a~mechanika lovu. \end{itemize} \subsection{Nehratelné postavy} -Pro tvorbu NPC (Non-Playable Characters) byli použité základní prostředky enginu. Podle dizajnu hry, k dispozici by měli být tři druhy logiky pro NPC, ale z časových důvodů jsou implementovány pouze dvě. Jedno z chování je pouhá chůze k dynamickým bodům. Toto základní chování se potom rozděluje na střežení oblasti nebo boj s hráčem. +Pro~tvorbu NPC (Non-Playable Characters) byly použité základní prostředky enginu. Podle návrhu hry, k~dispozici měly~by~být tři~druhy logiky pro~NPC, ale~z~časových důvodů jsou~implementovány pouze~dvě. Jedno~z~chování je~pouhá chůze k~dynamickým bodům. Toto~základní chování se~potom rozděluje na~střežení oblasti nebo~boj s~hráčem. -Postavy, které střeží oblast využíváji nový pro UE5 systém vjemů nehratelných postav. Postavy tak umí reagovat na zvuky, dotyky nebo zahlédnutí jiných objektů. Toto je využito pro postavy, které hlídají vězení v levelu 4. Ty patrolují předem navržené trasy a spouští určité akce až zahlédnou hráče. +Postavy, které~střeží oblast využívají v~UE5 nový systém vjemů nehratelných postav. Postavy umí reagovat na~zvuky, dotyky nebo~zahlédnutí jiných objektů. Toto~je~využito pro~postavy, které~hlídají vězení v~levelu 4. Ty~patrolují předem navržené trasy a~spouští určité akce až~zahlédnou hráče. -V implementaci chybí kompletní logika boje s hráčem. Aktuálně postavy pouze běží k hráči jestli ho zahlédnou. Zbytek nedodělané logiky je spíš vizuálního charakteru a spočíval by v jednoduchých animacích použití střelné nebo ruční zbraně ve hře. +V~implementaci chybí kompletní logika boje s~hráčem. Aktuálně postavy pouze běží k~hráči pokud ho~zahlédnou. Zbytek nedodělané logiky je~spíš vizuálního charakteru a~spočíval~by v~jednoduchých animacích použití střelné nebo~ruční zbraně ve~hře. -Každé chování využívá Navmesh pro orientaci na úrovní. +Každé chování využívá Navmesh pro~orientaci na~úrovni. \subsection{Nastavení} Hra využívá existující v enginu de/serializace v textovém formátu ,,.ini''. Při exportu hry v podobě samostatného buildu, lze využívat i binární formát konfiguračních souborů. Tento přístup značně zjednodušuje tvorbu konfiguračních proměnných. Tak k tvorbě vlastních parametrů stačí pouhá deklarace potřebných proměnných v C++ třídě. -- 2.45.2 From 4f2aa7297822a32fe3bc0be31cfc3300d9651e6b Mon Sep 17 00:00:00 2001 From: Oleg Petruny Date: Wed, 28 May 2025 12:52:41 +0200 Subject: [PATCH 8/8] full review of half of chapter --- ch2.tex | 78 ++++++++++++++++-------------- img/InteractableSystemDiagram.pdf | Bin 32594 -> 32489 bytes 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/ch2.tex b/ch2.tex index 9e2fced..f519a8c 100644 --- a/ch2.tex +++ b/ch2.tex @@ -29,7 +29,7 @@ Jen pro představu, co~obnáší klasické získání reference na~instanci vlas \subsection{Scény a ukládání hry} Scény, resp. levely (třídy UWorld a ALevelScriptActor)\footnote{UWorld instance je~přímo celý svět, který~funguje jako~balík metadat a~kontejner pro~veškeré instancované objekty v~něm. ALevelScriptActor je~objekt instancovaný v~UWorld automaticky a~obsahuje uživatelskou logiku světa.}, lze~taktéž získat v~podobě singletonu. Základní implementace byla~rozšířená pro~high level mechanismus volání událostí ve~scéně. Ten~je~zapotřebí například, když~hráč aktivuje spínací plošinu ve~hře a~ta~následně otevře dveře. \newpage -Na rozdíl od~jiných enginů, v~UE objekty ve~světě nemůžou referencovat jiné~nezávislé objekty. Jednoduše referenci nelze přiřadit z~důvodu abstrakce popsané v~předchozí podsekci, protože~každý objekt může~mít vlastní herní svět (a~nejen~to). Proto~pokud~plošina z~našeho príkladu chce otevřít dveře, musí~zkusit poslat požadavek správci herního světa, ten~požadavek se~nějak zpracuje a~teprvé~poté správce buď~provede akci otevírání dveří samostatně nebo~zavolá odpovídající funkci v~instanci objektu. Pokud~by~bylo potřeba opustit singleton architekturu pro~podporu paralelní existenci světů, stačí předělat členskou (member) funkci instance světa na~statický multicast delegate\footnote{Delegate jsou obaly na~C++ funkce, lambdy, funkce s~reflexi a~funkce z~Blueprintu. Delegate může~být typu single, pro~uložení reference na~jednou funkci, nebo~multicast pro~uložení dynamického seznamu funkcí. Podrobněji jsou~popsané v~programátorské dokumentaci.}, ke~kterému se~každý svět při~konstrukci bude vázat. +Na rozdíl od~jiných enginů, v~UE objekty ve~světě nemůžou referencovat jiné~nezávislé objekty. Jednoduše referenci nelze přiřadit z~důvodu abstrakce popsané v~předchozí podsekci, protože~každý objekt může~mít vlastní herní svět (a~nejen~to). Proto~pokud~plošina z~našeho příkladu chce otevřít dveře, musí~zkusit poslat požadavek správci herního světa, ten~požadavek se~nějak zpracuje a~teprvé~poté správce buď~provede akci otevírání dveří samostatně nebo~zavolá odpovídající funkci v~instanci objektu. Pokud~by~bylo potřeba opustit singleton architekturu pro~podporu paralelní existenci světů, stačí předělat členskou (member) funkci instance světa na~statický multicast delegate\footnote{Delegate jsou obaly na~C++ funkce, lambdy, funkce s~reflexi a~funkce z~Blueprintu. Delegate může~být typu single, pro~uložení reference na~jednou funkci, nebo~multicast pro~uložení dynamického seznamu funkcí. Podrobněji jsou~popsané v~programátorské dokumentaci.}, ke~kterému se~každý svět při~konstrukci bude vázat. Navíc hra byla rozšířena o~implementaci obnovení hry z~úložených dat. Při~návrhu byla~snaha udělat postup co~nejjednodušší. Hra se ukládá pomoci globální herní instance (třída GameInstance)\footnote{GameInstance je~první objekt vytvořený enginem hned po~úvodním načtení základu enginu a~taky poslední objekt na~destrukci při~vypínání hry.} při~vypínání hry a~načítá taktéž při~spuštění. Byl~využit existující systém serializace v~UE, který~ukládá inventář postavy, jméno levelu a~checkpoint na~něm spolu s~posledním stavem levelu. @@ -46,11 +46,11 @@ Byl navržen komplexní systém pro~správu každého interakčního objektu a~k \end{figure} \newpage -\paragraph{AInteractableActivator} Aktivátory jsou~komponenty s~určitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro~sebe jednu podinstanci každého aktivátoru registrovaného v~enginu. Libovolný objekt může rovněž obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci ray~tracingu a~následného dereferencování dereference objektu v~případě nárazu paprsku. +\paragraph{AInteractableActivator} Aktivátory jsou~komponenty s~určitými mechanismy detekce objektů. Instance hráče automaticky vytváří pro~sebe jednu podinstanci každého aktivátoru registrovaného v~enginu. Libovolný objekt může rovněž obsahovat libovolný aktivator. Práce obsahuje klasický způsob detekce objektů pomoci ray~tracingu a~následného dereferencování objektu v~případě nárazu paprsku. Navíc je~k~dispozici detekce objektů v~zorném poli hráče. Ta~funguje na~zpomaleném snímání určité stencil vrstvy\footnote{Stencil Buffer sousedí s~Z-Bufferem a~má~pouze jeden osmi~bitový celočíselný kanál. Slouží především jako~pomocník při~tvorbě specifických renderovacích technik a~efektů. Nejběžnější použití je~renderování obrysů objektů, označení objektů pro~následné zpracování v~nějaké render pipeline a~tvorba portálů.} s~vynecháním většiny render pipeline a~v~malém rozlišení. Zachycený snímek obsahuje pouze viditelné hráčem interakční objekty v~podobě masky. Následně je~maska v~grafickém vlákně zpracovaná pomocí algoritmu vyhledávání komponent z~počítačového vidění, kterou~poskytuje knihovna OpenCV. Nakonec do~středu nalezených komponent se~promítne paprsek a~zachytí objekty (viz.~\Cref{fig:InteractableScreenCapture}). -Původně bylo předpokládáno využití HLSL compute shaderu\footnote{High-Level Shader Language je~proprietární shader jazyk používaný v~DirectX.} pro~vyhledání komponent v~textuře, ale~z~časových důvodů jsem nedodělal přenos dat mezi CPU a~GPU, jelikož dohledat dokumentaci o~použití shaderu je~poměrně náročné. Proto~aktuální vyhledávání komponent je~spouštěno na~procesoru a~v~renderovacím vlákně na~moderních zařízeních trvá pět až~deset milisekund. V~praxi se~tento problém často řeší náhodným promítáním velkého množství paprsků nebo~velkého hitboxu napodobujícího tvar pohledového frustrumu kamery. Tato řešení jsou sice~rychlá na~implementaci, avšak~při~větších vzdálenostech (nad pár metrů) vykazují výrazné snížení přesností nebo zcela neefektivními. Navíc zahrnují časté počítání nárazů na~velké množství objektů, čímž~výkonnostně nejsou o~nic~lepší metody implementované v~této práci. +Původně bylo předpokládáno využití HLSL compute shaderu\footnote{High-Level Shader Language je~proprietární shader jazyk používaný v~DirectX.} pro~vyhledání komponent v~textuře, ale~z~časových důvodů jsem nedodělal přenos dat mezi CPU a~GPU, jelikož dohledat dokumentaci o~použití shaderu je~poměrně náročné. Proto~aktuální vyhledávání komponent je~spouštěno na~procesoru a~v~renderovacím vlákně na~moderních zařízeních trvá pět až~deset milisekund. V~praxi se~tento problém často řeší náhodným promítáním velkého množství paprsků nebo~velkého hitboxu napodobujícího tvar pohledového frustrumu kamery. Tato řešení jsou sice~rychlá na~implementaci, avšak~při~větších vzdálenostech vykazují výrazné snížení přesností a~větší spotřebu výpočetních prostředků. Navíc zahrnují časté počítání nárazů na~velké množství objektů, čímž~výkonnostně nejsou o~nic~lepší metody implementované v~této práci. \begin{figure} \centering @@ -66,7 +66,7 @@ Z~časových důvodů byla zamítnuta implementace modifikace geometrie objektu, Největší přínos nového systému zpracování vstupu v~Unreal Engine~5 se~projevil právě u~modifikátorů. Ty~využívají možnost dynamického přidávání nebo~odstraňování vstupních kontextů za~běhu. \subsection{Cutscény a Quick Time Events} -Unreal Engine je~známý~tím, jak~dobře umožňuje animovát scény. Rozhraní a~editor systému Sequencer je~přívětivé a~nabízejí široké možnosti. Bohužel~však většina těchto výhod končí mimo samotný editor, jelikož~chybí pohodlné prostředky pro~režii různých souborů animací (totéž platí i~pro~UI animace). Proto byl~dodán systém UCutsceneManager implementující frontu animací spolu s~uživatelským rozhraním pro~jejich přeskakování. +Unreal Engine je~známý~tím, jak~dobře umožňuje animovát scény. Rozhraní a~editor systému Sequencer jsou~přívětivé a~nabízejí široké možnosti. Bohužel~však většina těchto výhod končí mimo samotný editor, jelikož~chybí pohodlné prostředky pro~režii různých souborů animací (totéž platí i~pro~UI animace). Proto byl~dodán systém UCutsceneManager implementující frontu animací spolu s~uživatelským rozhraním pro~jejich přeskakování. Pro~přiblížení problému, vyjmenuji časté potřeby, které~vznikají při~běžném použití animací. \begin{itemize} @@ -108,72 +108,76 @@ V~implementaci chybí kompletní logika boje s~hráčem. Aktuálně postavy pouz Každé chování využívá Navmesh pro~orientaci na~úrovni. \subsection{Nastavení} -Hra využívá existující v enginu de/serializace v textovém formátu ,,.ini''. Při exportu hry v podobě samostatného buildu, lze využívat i binární formát konfiguračních souborů. Tento přístup značně zjednodušuje tvorbu konfiguračních proměnných. Tak k tvorbě vlastních parametrů stačí pouhá deklarace potřebných proměnných v C++ třídě. +Hra využívá existující v~enginu de/serializaci v~textovém formátu~,,.ini''. Při~exportu hry v~podobě samostatného buildu lze~využívat i~binární formát konfiguračních souborů. Tento~přístup značně zjednodušuje tvorbu konfiguračních proměnných. Díky~tomu stačí pro~tvorbu vlastních parametrů pouze~deklarovat potřebné proměnné v~C++~třídě. -Triviálně se pracuje s preferencemi hráče, které určují hlasitost různých kategorií zvuků nebo preferencemi ovládání. Tam stačí pouze přečíst resp. zapsat hodnotu. Jednoduché to přestává být v momentě nastavení kvality grafiky a parametrů zobrazení okna aplikace. Zejmená změny zaměřené na okno aplikace mohou zhavarovat a vypnout hru nebo ještě hůř zablokovat vstup celého počítače a donutit uživatele ke kompletnímu restartování zařízení. Takové situace mohou nastat celkem běžně, a nemusí znamenát problem v aplikaci. Například předčasný zásah OS k neodpovídajícímu procesu (hra se delší dobu načítá po aplikaci nového nastavení) nebo nevhodná reprezentace rozlišení, které monitor nebo GPU uživatele nepodporuje. +Triviálně se~pracuje s~preferencemi hráče, které~určují hlasitost různých kategorií zvuků nebo~preferencemi ovládání. U~těchto preferencí stačí pouze~přečíst resp.~zapsat hodnotu. Jednoduché to~přestává~být v~momentě nastavení kvality grafiky a~parametrů zobrazení okna aplikace. Zejména změny zaměřené na~okno aplikace mohou způsobit pád~aplikace nebo~ještě hůř zablokovat vstup celého počítače a~donutit uživatele ke~kompletnímu restartování zařízení. Takové~situace mohou nastat celkem běžně, a~nemusí znamenat problém v~aplikaci. Například předčasný zásah~OS k~neodpovídajícímu procesu (hra se~delší dobu načítá po~aplikování nového nastavení) nebo~nevhodná reprezentace rozlišení, které~monitor nebo~GPU uživatele nepodporuje. -V libovolném případě hra musí být schopna obstát nečekáné závady, a proto byl navržen mechanismus obnovení předchozích funkčních nastavení. Po zvolení nových parametrů a jejich aplikaci, hra nejprv uloži stávájící nastavení a až poté aplikuje ty nová. Po aplikovaní a až se proces okna vrátí zpět do renderujícího stavu, v menu se objeví okénko po dobu pěti sekund očekávající schválení od uživatele. Pokud uživatel stihne potvrdit úspěšnou změnu parametrů, jsou uložené jako funkční. Pokud uživatel z libovolného důvodů změnu neschválí, potom jsou obnoveny předchozí parametry. +V~libovolném případě hra musí~být~schopna obstát nečekané závady, a~proto byl~navržen mechanismus obnovení předchozích funkčních nastavení. Po~zvolení nových parametrů a~jejich aplikování, hra nejprv~uloží stávající nastavení a~až~poté aplikuje ty~nová. Po~aplikování a~až se~proces okna vrátí zpět do~renderujícího stavu, v~menu se~objeví okénko po~dobu pěti~sekund očekávající schválení od~uživatele. Pokud~uživatel stihne potvrdit úspěšnou změnu parametrů, jsou~uložené jako~funkční. Pokud~uživatel z~libovolného důvodu změnu neschválí, potom jsou~obnoveny předchozí parametry. -Navíc právě nastavení zobrazení okna (a výber zhlazovácí metody) byly ručně implementovány, jelikož nejsou volně k dispozici v high-level implementaci enginu. Jedná se o možnost výběru rozlišení a jeho třídění podle poměru stran, změná režimu vykreslování okna (neokenní, okenní bez rámce, okenní s rámcem), změná obnovovací frekvence. Při výběru a nastavení rozlišení se pracuje napřímo s DirectX\footnote{DirectX je množina API pro práci s grafikou a převážně pro počítačové hry.} rozhraním a zhlazovací metoda se nastavuje přes příkazové rozhraní enginu. +\newpage +Právě nastavení zobrazení okna (a~výběr zhlazovácí metody) byly implementovány navíc, jelikož~nejsou volně k~dispozici v~high-level implementaci enginu. Jedná~se~o~možnost výběru rozlišení a~jeho třídění podle poměru stran, změna režimu vykreslování okna (bezokenní, okenní bez~rámce, okenní s~rámcem) aЁzměná obnovovací frekvence. Při~výběru a~nastavení rozlišení se~pracuje přímo s~DirectX\footnote{DirectX je~množina API pro~práci s~grafikou a~převážně pro~počítačové hry.} rozhraním a~zhlazovací metoda se~nastavuje přes~příkazové rozhraní enginu. \section{Návrh tvorby generativního obsahu a jeho načítání za běhu} \label{sec:contentGenerationAndIntegration} \subsection{Možností generativních modelů v tomto projektu} -Herní úrovně jsou navrženy tak, aby pokrývala rozsáhlé herní žánry a situace, které dosud nebyly nebo nejsou často využívané s generovanou tvorbou. +Herní úrovně jsou navrženy~tak, aby~pokrývaly rozsáhlé herní žánry a~situace, které~dosud~nebyly nebo~nejsou~často využívané s~generovanou tvorbou. \begin{enumerate} -\item Level je zaměřen na tvorbu hororového obsahu. Je to skvělá příležitost vývoje a použití modelu, který využívá naše fyzické, chemické a psychické znalostí o lidském organismu, aby dokázal generovat strašidelný obsahu a následně ho správně režisérský začlenit. -\item Level slouží k testování modelu schopného tvorby libovolných miniher. Nabízí se tady taktéž online tabulka skóre pro základní a vygenerováné minihry. -\item Level existuje pro generování nových logických úseků resp. úkolů a modifikaci stávajících logických překážek. Například nová logika zapojení kabelů v elektrotechnické místnosti nebo přestavba bludiště. -\item Level umožňuje vyzkoušet generování sekvencí chodeb. Chodby by museli obsahovat patroly a kryty tak, aby hráč dokázal nenápadně prolézt skrz patroly. -\item poslední Level bude testovat schopnost navržení herních nepřátel, jejích logiku a jejich rozmístění na úrovní. +\item Level je~zaměřen na~tvorbu hororového obsahu. Je~to~skvělá příležitost vývoje a~použití modelu, který~využívá naše~fyzické, chemické a~psychické znalosti o~lidském organismu, aby~dokázal generovat strašidelný obsah a~následně ho~vhodně začlenit. +\item Level slouží k~testování modelu schopného tvorby libovolných miniher. Nabízí~se~tady~taktéž online tabulka skóre pro~základní a~vygenerované minihry. +\item Level existuje pro~generování nových logických úseků a~úkolů a~modifikaci stávajících logických překážek. Například nová logika zapojení kabelů v~elektrotechnické místnosti nebo~přestavba bludiště. +\item Level umožňuje vyzkoušet generování sekvencí chodeb. Chodby by~musely obsahovat patroly a~kryty~tak, aby~hráč dokázal nenápadně prolézt skrz patroly. +\item Poslední Level bude testovat schopnost navržení herních nepřátel, jejich logiku a~rozmístění na~úrovni. \end{enumerate} \subsection{Návrh architektury} -Architektura generování obsahu je založená na použití zřetězení různých modelů a kontrolních mechanismu. Kompletní řetězce modelů jsou nasazeny na soukromých serverech, které v různem intervalu nezávisle produkují nové assety. Po generaci assety jsou přemístěné na veřejné úložiště, odkud hra při zapínání stahuje nějaké množství ,,náhodných'' assetů (přesná definice výběru je vystvětlená později v podsekci \cref{par:contentSharing}). +Architektura generování obsahu je~založená na~použití zřetězení různých modelů a~kontrolních mechanismů. Kompletní řetězce modelů jsou~nasazeny na~soukromých serverech, které~v~různých intervalech nezávisle produkují nové assety. Po~generování jsou hotové soubory přemístěny na~veřejné úložiště, odkud~hra při~zapínání stahuje několik~,,náhodných'' assetů (přesnou definici výběru obsahuje \cref{par:contentSharing}). -\paragraph{Fine-tuning} V podobě open-source je k dispozici většina potřebných druhů modelů. Po analýze vyplívá, že bude potřeba vytvořit pouze jeden model, který bude schopen pracovat s kódem v Unreal Engine. Je to potřeba pro programování assetu nebo aspoň jeho umístění ve světě hry. Nejjednodušší a zároveň efektivní cesta je založená na fine-tuningu resp. dotrénování modelu, který se již využívá pro programování. +\paragraph{Fine-tuning} V~podobě open-source je~k~dispozici většina potřebných druhů modelů. Po~analýze vyplývá, že~bude~potřeba vytvořit pouze jeden~model, který~bude~schopen pracovat s~kódem v~Unreal Engine. Je~to~potřeba pro~programování assetu nebo~aspoň~jeho~umístění ve~světě hry. Nejjednodušší a~zároveň efektivní cesta je~založená na~fine-tuningu resp.~dotrénování modelu, který~se~již~využívá pro~programování. -Dotrénování může mít více podob. Nejspíš by se jednalo o fine-tuning programovácího modelu v C++ s dodatečnou Unreal Engine syntaxí nebo fine-tuning tvorby diagramů v Blueprintech. Výsledné blueprinty potom lze konvertovat do python příkazů, které vytvoří vygenerovaný diagram v editoru. Nelze jednoznačně říct co je lepší, obě metody vyžadují ověřit. Pro C++ modely je víc dat a k tomu toto prostředí má více kreativní svobody, přestože může zhavarovat aplikaci. Blueprinty jsou zdaleka bezpečnější, ale na oplátku bude těžší sbírat trénovací data. +\newpage +Dotrénování může~mít více~podob. Nejspíš by~se~jednalo o~fine-tuning programovacího modelu v~C++ s~dodatečnou syntaxí Unreal Enginu nebo~fine-tuning tvorby diagramů v~Blueprintech. Výsledné blueprinty potom~lze konvertovat do~Python příkazů, které~vytvoří vygenerovaný diagram v~editoru. Nelze jednoznačně říct, který~přístup je~vhodnější. Pro~C++~model je~k~dispozici více dat a~toto prostředí umožňuje větší tvůrčí svobodu, přestože~může zhavarovat aplikaci. Blueprinty jsou~zdaleka bezpečnější, ale~na~oplátku bude~těžší sbírat trénovací data. \paragraph{Postup generování} -Vše by začínálo v textovém modelu, který vytvoří typ a popis generováného objektu. Pomocí třídícího mechanismu se vybere potřebná množina ostatních modelů, kontrolních mechanismu a konvertovácích nástrojů. Asset bude postupně procházet takovým řetězcem až nabyde finální podoby. Na konci bude vždy kontrolní mechanismus, který zvaliduje soubor a ověří funkčnost assetu pomocí unit-testů ve hře. Pokud asset neprojde validací, celý postup, soubor a vstupní popis se zalogují pro budoucí ruční fine-tuning. +Vše~by~začínalo v~textovém modelu, který~vytvoří typ a~popis generovaného objektu. Pomocí třídícího mechanismu se~vybere příslušná množina dalších modelů, kontrolních mechanismů a~konverzních nástrojů. Asset bude~postupně procházet takovým řetězcem až~nabyde finální podoby. Na~konci bude~vždy kontrolní mechanismus, který~zvaliduje soubor a~ověří funkčnost assetu pomocí unit-testů ve~hře. Pokud~asset neprojde validací, celý postup, soubor a~vstupní popis se~zaznamenají pro~budoucí ruční doladění modelu. Příklad tvorby interakčního objektu: \begin{enumerate} -\item LLM vytváří popis assetu, v tomto případě nějaký interakční objekt, který umí přehrávat určitý zvuk a je umístěn nějakým způsobem na levelu 3. -\item Kontrolní mechanismus vybírá následující potřebné modely a nástroje. +\item LLM vytváří popis assetu, v~tomto případě interakční objekt, který~přehrává specifický zvuk a~je~umístěn na~konkrétním místě v~levelu~3. +\item Kontrolní mechanismus vybírá následující potřebné modely a~nástroje. \item Popis prochází modelem generující obrázky. -\item Obrázky prochází modelem generující 3D model (Nejdřív se generuje 360° video potřebného objektu, z kterého se následně generuje mesh). -\item Další část popisu je použita modelem generující zvukové stopy. -\item Poslední čast popisu se vloží do našeho dotrenováného programovácího modelu. Tento model se zároveň stará o umístění assetu v herních světech, protože jako jedíný je natrénovan na kontextu teto hry. -\item Výsledné kousky linkovací mechanismus spojí do jedné třídy resp. vloží do jednoho Blueprintu. Předtím kousky konvertuje do assetů v enginu a případně aktualizuje cesty referencí. -\item Konečný asset se zkouší na kompilaci a prochází unit-testy. Pokud tento krok bude úspěšný, asset je exportovan v podobě DLC nebo patch obsahu. Jinak popis, kousky a výsledek se logují pro budoucí investigaci. +\item Obrázky prochází modelem generující 3D~model (nejdřív se~generuje 360°~video potřebného objektu, ze~kterého se~následně generuje mesh). +\item Další část popisu je~použita modelem generující zvukové stopy. +\item Poslední část popisu se~vloží do~našeho dotrénovaného programovacího modelu. Tento~model se~zároveň stará o~umístění assetu v~herních světech, protože~jako~jediný je~natrénovaný na~kontext této~hry. +\item Výsledné kousky linkovací mechanismus spojí do~jedné~třídy resp.~vloží do~jednoho Blueprintu. Předtím převede jednotlivé části na~assety v~enginu a~případně aktualizuje cesty referencí. +\item Konečný asset se~zkouší na~kompilaci a~prochází unit-testy. Pokud~tento~krok bude úspěšný, asset je~exportovan v~podobě~DLC nebo~patch~obsahu. Jinak~popis, vygenerováné částí a~výsledek se~logují pro~budoucí investigaci. \end{enumerate} \subsection{Poskytování obsahu} -\paragraph{Nasazování generátoru} Nezávislé řetězce modelů zjednodušují řízení sítě workerů. pomocí docker containeru\footnote{Docker je software umožňující lokální virtualizaci prostředí nazývané containery.} nebo nasazovacího skriptu by se snadno vytvořil další nový worker (naše generující jednotka), který může ihned začít s generováním. Vygenerovaný obsah poté může být ihned přemístěn na veřejné úložiště, a proto worker nepotřebuje nijak velké úložiště. Protože workery nemusí odpovídat na požadavky uživatele v reálnem čase a plánovaná doba jedné hry je 15-30 minut nemusí generátory být rychlé a tedy ani náročné na výpočetní prostředky. Nezáleží nám ani na dlouhodobé životností workeru, jelikož modely se často sekvenčně přepínají resp. běží vždy pouze jeden model najednou. Takový přístup přináší flexibilitu v řízení prostředků a umožňuje ukládat mezistavy generátorů. Dokonce nepotřebujeme nijak velkou propustnost sítě, protože jednoduché assety nemohou přesahovat velikost jednotky megabajtů a máme větší časový intervaly mezi vznikem souborů. +\paragraph{Nasazování generátoru} Nezávislé řetězce modelů zjednodušují řízení sítě workerů. Pomocí Docker kontejneru\footnote{Docker je~software umožňující lokální virtualizaci prostředí nazývané kontejnery. Kontejnery jsou~předvytvořené samostatné prostředí s~potřebnými nástroji.} nebo~nasazovacího skriptu by~se~snadno vytvořil další worker (naše~generující jednotka), který~může ihned~začít s~generováním. Vygenerovaný obsah je~poté přemístěn na~veřejné úložiště, tedy~worker nepotřebuje nijak~velké vlastní úložiště. +\newpage +Protože~workery nemusí~odpovídat na~požadavky uživatele v~reálném čase a~plánovaná doba jedné hry je~15-30~minut, generátory nemusí~být~rychlé a~tedy~ani~náročné na~výpočetní prostředky. Nezáleží~nám ani~na~dlouhodobé životnosti workerů, jelikož~modely se~často sekvenčně přepínají (běží vždy pouze jeden model najednou). Takový~přístup přináší flexibilitu v~řízení prostředků a~umožňuje ukládat mezistavy generátorů. Dokonce nepotřebujeme nijak~velkou propustnost sítě, protože~jednoduché assety nemohou přesahovat velikost jednotek megabajtů a~jistě~máme větší časové intervaly mezi~vznikem souborů. -\paragraph{Stahování obsahu}\label{par:contentSharing} Hra automaticky stahuje a aktivuje obsah před začátkem nové hry. Z podrobné analýzy jsem rozhodnul, že nejsou zapotřebí žádné autentifikační nebo šifrovací vrstvy. Vždy by byla možnost reverse-engineeringem\footnote{Metoda analýzy hotového produktu (v našem případě binárního souboru), pro získání neveřejných informací popisujících funkčnost produktu.} získat klíč z binárních souborů hry a napadnout celý systém. Jestli se zamyslet, volný přístup k generovaným assetům nemá žádné zápory. Je jednoduchý na implementaci a udržbu. Je ale očekáváné přípojení pomocí HTTPS\footnote{Hypertext Transfer Protocol Secure je protokol pro šifrovaný přenos dat využívaný pro poskytování webových serverů.} a ověření certifikátu\footnote{Certifikáty v digitální podobě jsou řetězcem veřejných asymetrických klíčů různých vydavatelů. Každý předchozí vydavatel ručí za důvěryhodnost dalšího vydavatele.} s kopii uloženou v aplikaci. Mohlo by totiž dojít k man-in-the-middle útoku\footnote{Man-in-the-middle (MITM) je druh kyberútoku, ve kterém útočník tajně sleduje nebo upravuje komunikaci mezi dvěma uzly.} a hráč by mohl spustit ve hře nebezpečný obsah. +\paragraph{Stahování obsahu}\label{par:contentSharing} Hra automaticky stahuje a~aktivuje obsah před začátkem nové hry. Z~podrobné analýzy jsem~rozhodnul, že~nejsou~zapotřebí žádné autentifikační nebo~šifrovací vrstvy. Vždy~by~byla~možnost zpětnou analýzou (reverse-engineeringem)\footnote{Reverse-engineering je~metoda analýzy hotového produktu (v~nášem~případě binárního souboru), pro~získání neveřejných informací popisujících funkčnost produktu.} získat klíč z~binárních souborů hry a~napadnout celý systém. Když~se~nad~tím zamyslíme, volný přístup k~generovaným assetům nemá žádné zápory. Je~jednoduchý na~implementaci a~údržbu. Očekává~se ale připojení pomocí HTTPS\footnote{Hypertext Transfer Protocol Secure je~protokol pro~šifrovaný přenos dat využívaný pro~poskytování webových serverů.} a~ověření certifikátu\footnote{Certifikáty v~digitální podobě jsou~řetězcem veřejných asymetrických klíčů různých vydavatelů. Každý~předchozí vydavatel ručí za~důvěryhodnost dalšího vydavatele.} s~kopií uloženou v~aplikaci. Mohlo~by~totiž dojít k~man-in-the-middle útoku\footnote{Man-in-the-middle (MITM) je~druh kyberútoku, ve~kterém útočník tajně sleduje nebo~upravuje komunikaci mezi~dvěma uzly.} a~hráč by~mohl spustit ve~hře nebezpečný obsah. -Hra zahájí stahování pomocí API, který vybere nějaké soubory. V základu assety jsou vybírány náhodně, ale zároveň se používá hodnocení získané od hráčů, které váhově lehce mění hustotu pravděpodobnosti normálního rozdělení. +Hra zahájí stahování pomocí API, který~vybere nějaké soubory. V~základu assety jsou~vybírány náhodně, ale~zároveň se~používá hodnocení získané od~hráčů, které~váhově lehce~mění hustotu pravděpodobnosti normálního rozdělení. -\paragraph{Hodnocení obsahu} Potom co hráč zahraje s úrčitým assetem, máme možnost získat odezvu od hráče. Hodnocení využíjeme k váhové manipulaci náhodného výběru a zároveň jako data pro doládění generátorů. Návrh systému hodnocení staženého obsahu je již náročnější problém. Úkolem je umožnit pouze unikátní hodnocení a pouze od hráčů, které si s tímto obsahem opravdu zahráli. Protože nemáme žádnou authentifikaci, volné hodnocení nemusí fungovat. Kdokoliv může spamováním falešných hodnocení přemístit špatně hodnocený obsah do kategorie lepších a naopak. Authentifikáce tomu taky nezabraní, pouze oddálí takovou situaci. +\paragraph{Hodnocení obsahu} Poté, co~si~hráč zahraje s~určitým assetem, máme~možnost získat zpětnou vazbu od~hráče. Hodnocení využíjeme k~váhové manipulaci náhodného výběru a~zároveň jako~data pro~doladění generátorů. Návrh~systému hodnocení staženého obsahu je~již náročnější problém. Úkolem je~umožnit pouze unikátní hodnocení a~pouze od~hráčů, kteří~si~s~tímto obsahem opravdu zahráli. Protože~nemáme žádnou autentifikaci, volné~hodnocení nemusí fungovat. Kdokoliv může~spamováním falešných hodnocení přemístit špatně hodnocený obsah do~kategorie lepších a~naopak. Autentifikace tomu~také nezabrání, pouze~oddálí takovou situaci. -Některé hry v praxi používájí sofistikováné metody využívájící detekci nelegální kopie hry, nebo vyžití API herních obchodů (dosažení, odznaky, id účtu atd.). Takové metody se zdají být účinné, ale už dávno se lehce obchází pomocí triviální simuláce API odezvy. +Některé hry v~praxi používají sofistikováné metody využívající detekci nelegální kopie hry, nebo~využití API herních obchodů (achievementy, odznaky, ID~účtu~atd.). Takové metody se~zdají~být účinné, ale~už~dávno se~lehce obchází pomocí triviální simulace odpovědí API. -Táto práce nabízí sledování id účtu hráče, který hru zahrál a jeho seznam stažených souboru, pokud hra bude šířená na platformě Steam. UUID (Unique User Identificator) účtu ví pouze vlastník účtu, čili je to citlivá informace. Proto před odesláním takových dat budeme je vždy šifrovat asymetrickým klíčem (veřejným certifikátem webu ze kterého zároveň obsah stahujeme). Až bude hráč chtít ohodnotit ve hře obsah se kterým zahrál, API zkontroluje zda UUID hráče v Steam opravdu vlastní hru, že takový UUID stahovalo tento obsah z našeho serveru a že toto UUID ještě tento obsah nehodnotilo. +Táto práce nabízí sledování ID~účtu hráče, který~hru zahrál a~jeho seznam stažených souborů, pokud~hra bude šířená na~platformě Steam. UUID (Unique User Identifier) účtu ví~pouze vlastník účtu, čili~je~to citlivá informace. Proto~před odesláním takových dat je~budeme vždy šifrovat asymetrickým klíčem (veřejným certifikátem webu ze~kterého~zároveň obsah stahujeme). Až~bude hráč chtít ohodnotit ve~hře obsah se~kterým~zahrál, API~zkontroluje zda~UUID~hráče na~Steamu opravdu vlastní hru, že~toto~UUID stahovalo tento~obsah z~našeho serveru a~že~toto~UUID ještě tento~obsah nehodnotilo. -Pokud by hra byla šířená zdarma, bude potřeba zavést omezení na N hodnocení denně pro jedno UUID, protože bude možné bezproblémově vytvářet falešné účty. Jinak, z ekonomických důvodu, nám nebude vadít, že někdo bude nakupovat hru víckrát, aby víckrát nevhodně ohodnotil obsah. +Pokud~by~hra byla~šířená zdarma, bude~potřeba zavést omezení na~N~hodnocení denně pro~jedno~UUID, protože~bude~možné bezproblémově vytvářet falešné účty. Jinak, z~ekonomických důvodů, nám~nebude vadit, že~někdo bude~nakupovat hru víckrát, aby~víckrát nevhodně ohodnotil obsah. -Pořád bude možné revers-engeneernout API a zapsat stažení libovolného obsahu pro nějaký účet. Taková situace je zcela zanedbatelná, jelikož jedno hodnocení má malý vliv na průměr ve větších číslech. +Pořád bude možné reverse-engineerovat~API a~zapsat stažení libovolného obsahu pro~nějaký účet. Taková situace je~zcela zanedbatelná, jelikož~jedno~hodnocení má~malý vliv na~průměr ve~větších číslech. \subsection{Problémové typy obsahu} -Již teď je možné předpovědět co nebude kompletně fungovat nebo nebude fungovat v dostatečné kvalitě. +Již teď je možné předpovědět co~nebude kompletně fungovat nebo~nebude fungovat v~dostatečné kvalitě. \begin{itemize} -\item Herní obsah tvořený ručně nástrojí v editoru vyžadují samostatné modely. Například animace objektů na levelu tvořené v Sequencer, tvorba UI v editorovém designeru, práce se zvuky v Cue, statická tvorba úrovní a tedy i Landscape nebo foliáž a tvorba fyzických objektů (simulace tkáně, destrukce, pružnosti a dalších jevů). Pravděpodobně i jiné, ale víc technologií v této práci využito nebylo. -\item Materiály tež vyžadují vlastní model, ale jsou tvořeny pomocí grafových prvků napodobující blueprinty a máme k dispozici velké množství dat pro trénování takového modelu. V základu si můžeme vystačit bez generování materiálu a pouze vytvořit variace barev. -\item Grafické provedení generovaných objektů může mít velký dopad na výkon hry. Optimalizaci může provést další model, který bude provádět retopologii\footnote{Retopologie je proces zjednodušení složité topologie 3D objektu bez značně viditelných změn.} a pro tvorbu kolizí lze použít k-DOP\footnote{k-DOP (k-Discrete Oriented Polytope) je jeden z postupu obalování složitých 3D objektu do jednodušších tvarů.} algoritmus dostupný v UE. +\item Herní obsah tvořený ručně nástroji v~editoru vyžadují samostatné modely. Například animace objektů na~levelu tvořené v~Sequencer, tvorba~UI~v editorovém designeru, práce~se~zvuky v~Cue, statická tvorba úrovní a~tedy i~Landscape nebo~foliáž a~tvorba fyzických objektů (simulace tkáně, destrukce, pružnosti a~dalších~jevů). Pravděpodobně i~jiné, ale~víc technologií v~této práci využito nebylo. +\item Materiály též vyžadují vlastní model, ale~jsou tvořeny pomocí grafových prvků podobné blueprintům a~máme k~dispozici velké~množství dat pro~trénování takového modelu. V~základu můžeme vystačit si~bez~generování materiálů a~pouze vytvářet variace barev. +\item Grafické provedení generovaných objektů může~mít velký dopad na~výkon hry. Optimalizaci může provést další model, který~bude provádět retopologii\footnote{Retopologie je proces zjednodušení složité topologie 3D~objektu bez~značně viditelných změn.} a~pro~tvorbu kolizí lze~použít k-DOP\footnote{k-DOP (k-Discrete Oriented Polytope) je~jeden z~postupů obalování složitých 3D~objektu do~jednodušších tvarů.} algoritmus dostupný~v~UE. \end{itemize} \section{Grafika} diff --git a/img/InteractableSystemDiagram.pdf b/img/InteractableSystemDiagram.pdf index 3e3cf9c348694628b73665a4d6f7f9e0c2b03412..c207a409cbc99046c1b5856fecafc87d2af356e2 100644 GIT binary patch delta 3568 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 ME?;i7X`SqU0B-v3zW@LL delta 3753 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