aboutsummaryrefslogtreecommitdiff
path: root/design.tex
diff options
context:
space:
mode:
Diffstat (limited to 'design.tex')
-rw-r--r--design.tex288
1 files changed, 264 insertions, 24 deletions
diff --git a/design.tex b/design.tex
index 6b936a1..4e46388 100644
--- a/design.tex
+++ b/design.tex
@@ -24,9 +24,94 @@ workflows.
\section{Overview}
-\subsection{Core}
+As described above, the cr\^epe game engine's goal is to offer a Unity-like
+experience tailored for developing 2D games similar to Jetpack Joyride. That is why
+Jetpack Joyride and Unity provided the main inputs for this game engine design.
+Firstly, a quick overview will be given of the Unity game engine, in particular the
+\gls{ecs}. Secondly, this Overview will quickly talk you through some of the most
+important parts of the game engine, and why these parts are needed to create the
+Jetpack Joyride game.
+
+\subsection{ECS}
+
+The Unity game engine is structured using the Entity Component System (\gls{ecs}) (as
+shown in \cref{fig:ecs-block-diagram}). The \gls{ecs} is made out of three main
+subsystems, namely entities, components and systems. Entities are just IDs. An entity
+is also called a GameObject in Unity and it is made out of one (or more) components.
+Components are the classes that hold the data. The components determine what kind of
+entity it is (e.g. an enemy, audio, and so on). Systems take care of the behavior of
+the entities. Systems mainly read and write the enity's components data. The
+\gls{ecs} clearly distinguishes the data (components) from the functionality
+(systems).
-\subsection{Patterns}
+\begin{figure}
+ \centering
+ \includegraphics[width=0.5\textwidth]{img/ECSBlockDiagram.png}
+ \caption{ECS design pattern}
+ Source: \autocite{img:ecs-block-diag}
+ \label{fig:ecs-block-diagram}
+\end{figure}
+
+The \gls{ecs} will also be used at the cr\^epe game engine. Everything (from the
+protagonist and bullets to the walls and enemies) in the cr\^epe game engine will be
+a GameObject (i.e.~entity). The game programmer must program his game by creating all
+kind of GameObjects and placing them in one (or multiple) scenes, just like Unity.
+
+\subsection{Jetpack Joyride}
+
+Firstly, some background information about Jetpack Joyride. Jetpack Joyride is a
+side-scrolling endless runner action video game created by Halfbrick Studios. The
+protagonist is called Barry Steakfries, who the player controls as he steals a
+bullet-powered jet pack from a top-secret laboratory
+\autocite{wikipedia:jetpack-joyride}. A screenshot from the game can be seen in
+\cref{fig:jetpack-joyride} (pleae be aware that the goal of this project is not to
+create an exact replica of Jetpack Joyride, it is only used as a source of
+inspiration).
+
+\begin{figure}
+ \centering
+ \includegraphics[width=0.5\textwidth]{img/JetpackJoyride.jpg}
+ \caption{Jetpack Joyride}
+ Source: \autocite{img:jetpack-joyride}
+ \label{fig:jetpack-joyride}
+\end{figure}
+
+The protagonist wears a jetpack with which he can float in the air. The player must
+avoid obstacles (such as lasers, missiles and zappers) by floating at the right
+height. The player can control the protagonist's jetpack, thereby also controlling
+the protagonist's height. The protagonist experiences gravity and other forces (like
+the force from his jetpack pushing him upwards). These forces should be easily
+programmable by the game programmer. That is why a physics system is needed in the
+cr\^epe game engine. Only very limited/easy physics are needed for Jetpack Joyride,
+that is why this is only supported by the cr\^epe game engine.
+
+The protagonist must avoid obstacles. That is why the cr\^epe game engine should also
+support a collision system. Again, only very limited/easy collision is needed for
+Jetpack Joyride, that is why only very limited/easy collision is supported by the
+cr\^epe game engine.
+
+The game must, of course, also be visible to and playable by the user. A rendering
+system will take care of rendering (displaying) the game and its GameObjects. An
+input system will take care of all the inputs (mouse and keyboard).
+
+Jetpack Joyride also offers audio. A system will take care of the audio in the
+cr\^epe game engine.
+
+Particles are very common in Jetpack Joyride, e.g. underneath the jetpack and behind
+the rockets. Particles will be supported by the particle system.
+
+The start of a scene is described in a scene. However, the game programmer might also
+want to write game logic code which is running during the game (e.g. to switch to a
+new scene or to perform a custom action at a collision). For these purposes, Unity
+uses scripts. These scripts will also be supported by the cr\^epe game engine.
+
+Finally, as an extra, replay functionality will be supported by the cr\^epe game
+engine. A dedicated replay system will be used to support replay.
+
+It turns out that a physics, collision, rendering, input, audio, particle, script,
+and replay system are needed to create the Jetpack Joyride game. These systems form
+the main part of the \gls{ecs}. The design of these eight systems in combination with
+\gls{ecs}, will be briefly discussed in the next parts of this design document.
\section{Design}
\subsection{Game Loop}
@@ -74,7 +159,7 @@ When such an event occurs, the EventManager calls the assigned callback function
\end{figure}
\subsection{Rendering}
-\subsection{Physics}
+% \subsection{Physics}
\subsection{Scripting}
@@ -111,6 +196,7 @@ architecture:\noparbreak
\end{itemize}
\subsubsection{Architecture}
+\label{sec:scripts:architecture}
The restrictions detailed at the start of this section are mitigated as
follows:\noparbreak
@@ -133,10 +219,11 @@ follows:\noparbreak
contains the following classes:\noparbreak
\begin{description}
\item[Script] This is the script \emph{interface}, and is used by the game
- programmer to create derived script classes. All methods in this class are
- declared virtual and have an empty implementation.
+ programmer to create derived script classes. All virtual methods in this class
+ have an empty implementation by default, and are optionally implemented by the
+ game programmer.
- This class' methods are protected by default, and a friend relation to
+ This class' virtual methods are protected by default, and a friend relation to
\codeinline{ScriptSystem} is used to ensure only \codeinline{ScriptSystem} is
able to call these methods.
@@ -145,6 +232,10 @@ contains the following classes:\noparbreak
function returns a reference to the \codeinline{BehaviorScript} instance it was
called on so it can be chained after the call to
\codeinline{GameObject::add_component}.
+
+ \codeinline{Script} also has a reference to its parent
+ \codeinline{BehaviorScript} instance so components can easily be retrieved using
+ the component manager.
\item[BehaviorScript]
This is the script \emph{component}, and is given as the template parameter to
\codeinline{GameObject::add_component}.
@@ -152,7 +243,8 @@ contains the following classes:\noparbreak
This class also uses a friend relation to \codeinline{ScriptSystem} to restrict
access to its private reference member \codeinline{script}.
\item[ScriptSystem] This is the system class that runs the methods implemented in
- the derivative instances of \codeinline{Script}.
+ the derivative instances of \codeinline{Script}. Described further in
+ \cref{sec:scripts:sytem}.
\end{description}
\begin{figure}
@@ -162,11 +254,37 @@ contains the following classes:\noparbreak
\label{fig:class-scripts}
\end{figure}
+\subsubsection{System}
+\label{sec:scripts:sytem}
+
+Because most of the complexity in the scripting interface comes from the containers
+described in \cref{sec:scripts:architecture}, the script system class itself is
+relatively simple. The script system provides a method
+\codeinline{ScriptSystem::update} that calls all active script's update functions.
+
+Because of the limitation that types cannot be passed as parameters in C++, the
+user-defined script class (derived from \codeinline{Script}) can not directly be
+instantiated when adding the component to the component manager. To work around this
+limitation, the method \codeinline{BehaviorScript::set_script} was created. This
+results in the possibility that an instance of \codeinline{BehaviorScript} does not
+reference an instance of \codeinline{Script}. In addition to the non-active script
+components, the script system skips over these `invalid' instances. This is
+illustrated in \cref{fig:activity-scripts}.
+
+\begin{figure}
+ \centering
+ \includepumldiag{img/activity-scripts.puml}
+ \caption{Script system update method}
+ \label{fig:activity-scripts}
+\end{figure}
+
+A \gls{poc} for the script system is shown in \cref{poc:scripts}.
+
\subsection{Audio}
Since writing a custom real-time audio mixing engine is outside the scope of this
project\mref and C++ does not provide a built-in cross-platform audio \gls{api}, the
-audio system inside the cr\^epe engine is implemented as a fa\c{c}ade around an
+audio system inside the cr\^epe engine is implemented as a \gls{facade} around an
existing audio library.
\subsubsection{Libraries}
@@ -177,15 +295,7 @@ searching for libraries (search terms: `dynamic/adaptive audio', `real-time audi
`audio library', `game audio engine'), several libraries were found. These libraries
were checked against the audio engine requirements \autocite{crepe:requirements} and
then tested by writing the same benchmark-style \gls{poc} using the remaining
-qualifying libraries:\noparbreak
-\begin{enumerate}
- \item Load a background track (Ogg Vorbis)
- \item Load three short samples (WAV)
- \item Start the background track
- \item Play each sample sequentially while pausing and resuming the background track
- \item Play all samples simultaniously
- \item Stop all audio and exit
-\end{enumerate}
+qualifying libraries. These \glspl{poc} are detailed in \cref{poc:audio}.
Of these libraries the following were determined to be unsuitable for use in this
project:\noparbreak
@@ -201,12 +311,12 @@ project:\noparbreak
The only library that remained after these tests is SoLoud \autocite{lib:soloud}. It
is Zlib/LibPng licensed and provides a high-level object-oriented C++ \gls{api}.
-\Cref{sec:audio:architecture} describes the fa\c{c}ade written for this library.
+\Cref{sec:audio:architecture} describes the \gls{facade} written for this library.
\subsubsection{Architecture}
\label{sec:audio:architecture}
-\Cref{fig:class-audio-facade} shows a class diagram of the audio fa\c{c}ade. It
+\Cref{fig:class-audio-facade} shows a class diagram of the audio \gls{facade}. It
contains the following classes:
\begin{description}
\item[SoundContext] This is a wrapper around the \codeinline{SoLoud::soloud}
@@ -222,18 +332,148 @@ contains the following classes:
\begin{figure}
\centering
\includepumldiag{img/facade-audio.puml}
- \caption{Audio fa\c{c}ade class diagram}
+ \caption{Audio \glsfmtshort{facade} class diagram}
\label{fig:class-audio-facade}
\end{figure}
-\subsection{Input}
+A \gls{poc} for the final Audio \gls{facade} is also showcased in \cref{poc:audio}.
+
+% \subsection{Save manager}
+%
+% The save manager \gls{api} is designed to give the game programmer an easy to use
+% interface for retrieving and storing game-specific data (\cref{req:savemgr}).
+%
+% Because the engine validation app only stores statistics and highscores, the save
+% manager is not required to support loading different save files
+% (\cref{req:savemgr:multi-file}), nor storing complicated data types
+% (\cref{req:savemgr:types-custom}). The save manager only supports storing simple
+% types (\cref{req:savemgr:types-scalar,req:savemgr:types-string}).
+%
+% In order to reduce complexity for the game programmer further, the following
+% requirements were also set:\noparbreak
+%
+% \begin{itemize}
+% \item Prevent data loss in the case of crashes (\cref{req:savemgr:journalling})
+% \item Handle opening/closing/flushing of the underlying file automatically
+% (\cref{req:savemgr:file-manage})
+% \item Save file variables are uniquely identified (\cref{req:savemgr:var-key})
+% \end{itemize}
+%
+% \subsubsection{Architecture}
+% \label{sec:savemgr:architecture}
+%
+% \begin{figure}
+% \centering
+% \includepumldiag{img/class-savemgr.puml}
+% \caption{Save manager class diagram}
+% \label{fig:class-savemgr}
+% \end{figure}
+%
+% In order to realize \cref{req:savemgr:journalling,req:savemgr:var-key}, a third-party
+% key-value database library is used.
+
+\subsection{Global configuration interface}
+
+Because the game programmer only has access to interfaces within the public \gls{api}
+namespace (\codeinline{crepe::api}), they would not be able to configure aspects of
+engine-internal components. To work around this access restriction, a global
+interface was made that stores arbitrary data, which can be accessed both internally
+and via the public \gls{api}.
+
+\subsubsection{Architecture}
+\label{sec:config:architecture}
+
+The global configuration interface consists of a single singleton class that can be
+accessed globally (see \cref{fig:class-config}). This class holds several anonymous
+structs, which are used to organize options per system or engine component.
+
+\begin{figure}
+ \centering
+ \includepumldiag{img/class-config.puml}
+ \caption{Global configuration interface class diagram}
+ \label{fig:class-config}
+\end{figure}
+
+% \subsection{Input}
+
+% \subsection{Physics}
+
+\appendix
+
+\section{\Glsfmtlongpl{poc}}
+
+The full (documented) source code of these \glspl{poc} is available on GitHub
+\autocite{crepe:code-repo}.
+
+\subsection{Script system}
+\label{poc:scripts}
+
+The script system \gls{poc} \autocite[script example]{crepe:code-repo} consists of
+the following:\noparbreak
+\begin{itemize}
+ \item A user-defined class (\codeinline{MyScript}) derived from
+ \codeinline{Script}, which only implements the \codeinline{update()} function.
+ \item A main function that---
+ \begin{itemize}
+ \item Creates a game object with \codeinline{Transform} and
+ \codeinline{BehaviorScript} components.
+ \item A call to \codeinline{ScriptSystem::update}, which results in
+ \codeinline{MyScript::update} being called.
+ \end{itemize}
+\end{itemize}
+
+Running the \gls{poc} results in the output shown in \cref{fig:poc-output-scripts},
+demonstrating that the system works as intended.
+
+\begin{figure}
+ \centering
+ \fitimg{\includegraphics[scale=0.7]{img/poc-output-scripts.png}}
+ \caption{Script system \glsfmtshort{poc} output}
+ \label{fig:poc-output-scripts}
+\end{figure}
-\subsection{Physics}
+\subsection{Logging utilities}
+\label{poc:log}
+A small \gls{poc} was written to test the engine's logging functions \autocite[log
+example]{crepe:code-repo}. The following calls are used in this example:
-\section{Tools}
+\begin{blockcode}
+dbg_trace(); // the dbg_* macros automatically show
+dbg_logf("test printf parameters: %d", 3); // where the message is coming from
+logf(LogLevel::INFO, "info message");
+logf(LogLevel::WARNING, "warning");
+logf(LogLevel::ERROR, "error");
+\end{blockcode}
+
+The output of this test is shown in \cref{fig:poc-log}.
+
+\begin{figure}
+ \centering
+ \includegraphics[scale=0.7]{img/poc-log.png}
+ \caption{Logging function outputs}
+ \label{fig:poc-log}
+\end{figure}
+
+\subsection{Audio}
+\label{poc:audio}
+
+A test that consists of the following steps was written for each audio library
+mentioned in \cref{sec:audio:libs}:\noparbreak
+\begin{enumerate}
+ \item Load a background track (Ogg Vorbis)
+ \item Load three short samples (WAV)
+ \item Start the background track
+ \item Play each sample sequentially while pausing and resuming the background track
+ \item Play all samples simultaniously
+ \item Stop all audio and exit
+\end{enumerate}
-\section{Conclusion}
+The repository \autocite{crepe:code-repo} contains two finished \glspl{poc} under the
+\codeinline{mwe/audio/} subdirectory for miniaudio and SoLoud. The SoLoud \gls{poc}
+was later converted to a full audio \gls{facade}, which is currently part of the
+cr\^epe engine. The \gls{poc} using the audio \gls{facade} is available from the same
+repository, under the \codeinline{src/example/audio_internal.cpp} file.
\end{document}