diff options
Diffstat (limited to 'design.tex')
-rw-r--r-- | design.tex | 383 |
1 files changed, 305 insertions, 78 deletions
@@ -15,7 +15,7 @@ similar to Jetpack Joyride. The cr\^epe engine is designed to ease the transition for developers familiar with Unity, ensuring minimal friction when switching platforms. Our aim is to preserve -many of Unity’s core features while introducing a lightweight and open-source +many of Unity's core features while introducing a lightweight and open-source alternative, licensed under the MIT License. The engine is primarily aimed at indie developers who have prior experience with @@ -114,116 +114,307 @@ the main part of the \gls{ecs}. The design of these eight systems in combination \gls{ecs}, will be briefly discussed in the next parts of this design document. \section{Design} + \subsection{Game Loop} -\subsubsection{Problem statement:} -A game loop is essential for maintaining a continuous flow of game actions, ensuring that updates to game logic, physics, and rendering occur in a synchronized manner. Without a game loop, the game would lack consistent timing and leading to unpredictable behavior. -The game loop is mainly responsible for these 2 purposes: + +\subsubsection{Problem statement} + +A game loop is essential for maintaining a continuous flow of game actions, ensuring +that updates to game logic, physics, and rendering occur in a synchronized manner. +Without a game loop, the game would lack consistent timing and leading to +unpredictable behavior. + +The game loop is mainly responsible for these 2 purposes:\noparbreak \begin{itemize} - \item Updating all systems in the correct order. + \item Updating all systems in the correct order \item Making sure the gameloop timer is up to date \end{itemize} -The game loop can be external where the user has the ability to update the systems themselves or an intergrated game loop which is managed by the gameloop. -Both of these approaches have advantages and disadvantages when it comes to flexibility and reliability. +The game loop can be external where the user has the ability to update the systems +themselves or an intergrated game loop which is managed by the gameloop. Both of +these approaches have advantages and disadvantages when it comes to flexibility and +reliability. -\subsubsection{Design:} -The game loop of this engine is integrated into the engine, this is done for the following reasons: -\begin{itemize} - \item \emph{Simplify development} The user only has to call startGame(). - \item \emph{Uniform system calls} The systems are always updated in the same order limiting overwrites and undefined system behavior. - \item \emph{Reliable timer update} Each cycle the game loop timer is always updated limiting timing issues. -\end{itemize} -As seen in figure \ref{gameloop-flow} the gameloop is divided into different steps. +\subsection{Texture} + +% FIXME: our +The textures in our game engine are represented by the \codeinline{Texture} class. It +is implemented a \gls{facade} around the \gls{sdl} library. + +\subsubsection{Architecture} + +\Cref{fig:class-texture} shows a class diagram of the texture \gls{facade}. It +contains the following classes:\noparbreak +\begin{description} + \item[SDLContext] This is a facade around the \codeinline{SDL2} library which is + used around different parts of the engine, and is therefore implemented as a + singleton. This class is friends with \codeinline{Texture}, + \codeinline{LoopManager}, \codeinline{RenderSystem} and + \codeinline{AnimatorSystem}. + \item[Texture] This is a wrapper around the \codeinline{SDL_Texture} class, and + uses cr\^epe's \codeinline{Asset} class to load an Texture instead. +\end{description} + +\begin{figure} + \centering + % TODO: export as vector format instead + \includegraphics[width=\textwidth]{img/texture.png} + \caption{User texture class diagram} + \label{fig:class-texture} +\end{figure} + +\subsection{AssetManager} + +The AssetManager is a \gls{api} class that the user can use to make a +\codeinline{Asset} available from different scenes. + +\subsubsection{Architecture} + +\Cref{fig:class-assetmanager} shows a class diagram of the AssetManager. It contains +the following classes:\noparbreak +\begin{description} + \item[AssetManager] is a Singleton class, meaning only one instance of this class + exists throughout the application. This ensures a single point of access and + control over asset management, simplifying resource handling and avoiding + duplicated assets in memory. +\end{description} + +\begin{figure} + \centering + % TODO: export as vector format instead + \includegraphics[width=0.5\textwidth]{img/AssesManager.png} + \caption{User AssetManager class diagram} + \label{fig:class-assetmanager} +\end{figure} + +\subsection{Rendering} + +Every game engine has an rendering structure to present all the different enities and +components on the screen. + +\subsubsection{Architecture} + +% TODO: anyone read this? +\Cref{fig:class-rendering} shows a class diagram of the RenderSystem. It contains the +following classes:\noparbreak \begin{itemize} - \item \emph{Update loop timer} The loop timer gets updated and the expected frame time is calculated. - \item \emph{Check events} Queued events get dispatched and callback functions are handled acordingly. - \item \emph{Process input} The input system is called and user input is processed. - \item \emph{Fixed update} A fixed loop for timing sensitive systems such as physics. - \item \emph{Update} A per frame update for all per frame updates. - \item \emph{Render} Calling the render system to render the frame. + \item The system architecture is centered around rendering and component + management, with the following key components:\noparbreak + \begin{description} + \item[\codeinline{System}] An interface class containing the virtual + \codeinline{update()} function. + \item[\codeinline{RenderSystem}] A derived class of \codeinline{System} + responsible for rendering operations. + \end{description} + \item The \codeinline{System::get_instance()} function provides a static, singleton + instance for managing system-wide operations. + \item The \codeinline{RenderSystem} class implements various rendering + functions:\noparbreak + \begin{description} + \item[\codeinline{sort_layers()}] Organizes the rendering layers. + \item[\codeinline{clear_screen()}] Clears the screen prior to rendering. + \item[\codeinline{update_sprites()}] Updates sprite positions and states. + \item[\codeinline{update_camera()}] Manages the camera view. + \item[\codeinline{present_screen()}] Presents the final rendered image to the + screen. + \end{description} + \item The \codeinline{SdlContext} class, another singleton, manages the \gls{sdl} + and has a friendship relationship with \codeinline{ComponentManager} for tighter + integration with component management. + \item Components are organized as follows:\noparbreak + \begin{itemize} + \item The \codeinline{Component} base class allows for generic handling of + components. + \item Derived component classes include:\noparbreak + \begin{description} + \item[\codeinline{Sprite}] Represents visual elements with attributes like + \codeinline{sprite}, \codeinline{color}, \codeinline{flip}, + \codeinline{sortingLayer}, and \codeinline{orderInLayer}. + \item[\codeinline{Transform}] Manages positional attributes, including + \codeinline{position}, \codeinline{rotation}, and \codeinline{scale}. + \end{description} + \end{itemize} + \item Both \codeinline{Sprite} and \codeinline{Transform} components provide a + \codeinline{get_instances_max()} function to retrieve the maximum instance count. \end{itemize} -This is done as illustrated in Figure \ref{gameloop-flow}, the game loop continues to call the fixed update function as long as sufficient time is available. -Delta time, calculated using the time between the start of the last frame and the current frame, is used to measure the duration of each frame. -This value is converted into a time-based unit, enabling other systems or game developers to create behavior independent of frame rate. - -Rendering and animations are handled separately on a per-frame basis. A delay, in conjunction with the delta time calculation, -is applied to maintain consistent visual behavior, even when frame rates vary. -As seen in figure Figure \ref{gameloop-class} to access the deltaTime anywhere in the system a timerClass is created using a singleton desing pattern which ensures only one instance of the class is created; the gameloop updates the timing and delta time of this class to ensure it is accurate. -The gameloops two main functions are the setup() and loop(). The first is called when the game starts and handles all startup procedures this function only runs once. -The loop() function keeps looping as long as the game is running. -The gamedeveloper start the game engine/game using the code example below: + +\begin{figure} + \centering + % TODO: export as vector format instead + \includegraphics[width=\textwidth]{img/Rendering.png} + \caption{System Rendering class diagram} + \label{fig:class-rendering} +\end{figure} + +\subsubsection{System} + +\begin{figure} + \centering + % TODO: export as vector format instead + \includegraphics[width=\textwidth]{img/flowchart_rendering.png} + \caption{System Rendering flowchart } + \label{fig:class-renderingflowchart} +\end{figure} + +\subsubsection{Design} + +The game loop of this engine is integrated into the engine, this is done for the +following reasons:\noparbreak +\begin{description} + \item[Simplify development] The user only has to call \codeinline{startGame()}. + \item[Uniform system calls] The systems are always updated in the same order + limiting overwrites and undefined system behavior. + \item[Reliable timer update] Each cycle the game loop timer is always updated + limiting timing issues. +\end{description} + +As seen in \cref{fig:gameloop-flow} the gameloop is divided into different +steps:\noparbreak +\begin{description} + \item[Update loop timer] The loop timer gets updated and the expected frame time is + calculated. + \item[Check events] Queued events get dispatched and callback functions are handled + acordingly. + \item[Process input] The input system is called and user input is processed. + \item[Fixed update] A fixed loop for timing sensitive systems such as physics. + \item[Update] A per frame update for all per frame updates. + \item[Render] Calling the render system to render the frame. +\end{description} + +This is done as illustrated in \cref{fig:gameloop-flow}, the game loop continues to call +the fixed update function as long as sufficient time is available. Delta time, +calculated using the time between the start of the last frame and the current frame, +is used to measure the duration of each frame. This value is converted into a +time-based unit, enabling other systems or game developers to create behavior +independent of frame rate. + +Rendering and animations are handled separately on a per-frame basis. A delay, in +conjunction with the delta time calculation, is applied to maintain consistent visual +behavior, even when frame rates vary. As seen in \cref{fig:gameloop-class} to access the +deltaTime anywhere in the system a timerClass is created using a singleton desing +pattern which ensures only one instance of the class is created; the gameloop updates +the timing and delta time of this class to ensure it is accurate. The gameloops two +main functions are the setup() and loop(). The first is called when the game starts +and handles all startup procedures this function only runs once. The loop() function +keeps looping as long as the game is running. + +The gamedeveloper start the game engine/game using the code example below:\noparbreak \begin{blockcode} - Gameloop loop; - loop.start(); +Gameloop loop; +loop.start(); \end{blockcode} -This starts calls the setup() and loop() functions and starts the game loop timer; -To get the current frames Delta time \codeinline{LoopTimer::getInstance().getDeltaTime()} can be used which will return the expected frame time. + +This starts calls the setup() and loop() functions and starts the game loop timer; To +get the current frames' delta time, the \codeinline{getDeltaTime()} method on the +\codeinline{LoopTimer} singleton can be used, which will return the expected frame +time. \begin{figure} \centering \includepumldiag{img/gameloop-flow.puml} - \caption{Gameloop Flowchart Diagram} \label{gameloop-flow} + \caption{Gameloop Flowchart Diagram} + \label{fig:gameloop-flow} \end{figure} \begin{figure} \centering \includepumldiag{img/gameloop-class.puml} - \caption{Gameloop Flowchart Diagram} \label{gameloop-class} + \caption{Gameloop Flowchart Diagram} + \label{fig:gameloop-class} \end{figure} + \subsection{Event system} -\subsubsection{Problem Statement:} -The game engine utilizes the Entity-Component-System (ECS) architecture, where components store data, -and systems process that data to apply changes. Each system is responsible for managing a specific domain, -such as physics in the physics system and rendering in the rendering system. -To facilitate communication between systems without introducing direct dependencies, -a method of inter-system communication is required to maintain loose coupling. -Additionally, a mechanism that allows one object's trigger to manipulate adn affect multiple other objects is beneficial for game developers, providing greater flexibility in designing interactions within the game. - - -\subsubsection{Architecture:} -The sollution to connect the various systems and BehaviorScripts together without inducing high coupling is an event system that facilitates communication between systems and BehaviorScripts using various types of events. -The event system includes several pre-defined events, all derived from a parent Event class, capable of handling user input and application-level events, such as window resizing. -Furthermore, a specific event is designated for the collision handler within the physics system, which can be triggered when two objects collide. -The event system also allows developers to create custom events, such as "onPlayerDeath," and assign callback functions that execute when the event is triggered. + +\subsubsection{Problem Statement} + +The game engine utilizes the \gls{ecs} architecture, where components store data, and +systems process that data to apply changes. Each system is responsible for managing a +specific domain, such as physics in the physics system and rendering in the rendering +system. To facilitate communication between systems without introducing direct +dependencies, a method of inter-system communication is required to maintain loose +coupling. Additionally, a mechanism that allows one object's trigger to manipulate +adn affect multiple other objects is beneficial for game developers, providing +greater flexibility in designing interactions within the game. + +\subsubsection{Architecture} + +The sollution to connect the various systems and BehaviorScripts together without +inducing high coupling is an event system that facilitates communication between +systems and BehaviorScripts using various types of events. The event system includes +several pre-defined events, all derived from a parent Event class, capable of +handling user input and application-level events, such as window resizing. +Furthermore, a specific event is designated for the collision handler within the +physics system, which can be triggered when two objects collide. The event system +also allows developers to create custom events, such as "onPlayerDeath," and assign +callback functions that execute when the event is triggered. + \begin{figure} - \centering - \includegraphics[width=\linewidth]{img/event-uml.drawio.png} - \caption{event system class diagram} - \label{fig:event-uml} + \centering + % TODO: export as vector format instead + \includegraphics[width=\linewidth]{img/event-uml.drawio.png} + \caption{Event system class diagram} + \label{fig:event-uml} \end{figure} -the event system as seen in \ref{fig:event-uml} includes several parts such as: +The event system as seen in \cref{fig:event-uml} includes several parts such +as:\noparbreak \begin{description} - \item[eventManager] The manager has the functions to subscribe/trigger/queue/dispatch events. It also stores all callback functions corresponding to specific event. - The manager is a singleton and can therefor only exist once so all events are stored in one place. - \item[IEventWrapper] This is a EventWrapper \emph{interface} which is used to store all the different templated eventshandlers in one map in the event manager. - this wrapper contains the logic to convert the parent class \emph{event} to the correct subclasses. It also contains a variable onSuccessDestroy which can be set to destroy the callback call onces completed. - This can be used to make a one time only event. - \item[Event] This is the parent class where all specific event classes are derived from. Each event contains a - \begin{itemize} - \item \emph{\codeinline{static std::uint32_t getStaticEventType()}} to set type during compiling. - \item \emph{\codeinline{virtual std::uint32_t getEventType() const override }} function to manage the type conversion during runtime. - \end{itemize} - Other functions can be freely added when creating a custom function. - When an event is triggered a specific derived class must be used to indicate which event is triggered. A reference to this event is then transfered to all callback functions subscribed. + \item[eventManager] The manager has the functions to + subscribe/trigger/queue/dispatch events. It also stores all callback functions + corresponding to specific event. The manager is a singleton and can therefor only + exist once so all events are stored in one place. + \item[IEventWrapper] This is a EventWrapper \emph{interface} which is used to store + all the different templated eventshandlers in one map in the event manager. this + wrapper contains the logic to convert the parent class \emph{event} to the + correct subclasses. It also contains a variable onSuccessDestroy which can be set + to destroy the callback call onces completed. This can be used to make a one time + only event. + \item[Event] This is the parent class where all specific event classes are derived + from. Each event contains a--- + \begin{itemize} + % TODO: the design document is not a technical reference, so implementation + % details shouldn't even be in here. Also, are getter functions used to set + % things nowadays? + \item \emph{\codeinline{static std::uint32_t getStaticEventType()}} to set type + during compiling. + \item \emph{\codeinline{virtual std::uint32_t getEventType() const override }} + function to manage the type conversion during runtime. + \end{itemize} + Other functions can be freely added when creating a custom function. When an + event is triggered a specific derived class must be used to indicate which event + is triggered. A reference to this event is then transfered to all callback + functions subscribed. \end{description} -The EventManager is a singleton so all all callbacks are stored in one place and it can be called everywhere in the system or game. -The user can use the EventManager for the following functions: -\begin{itemize} - \item \emph{Subscribe:} This subscribes a function pointer or lambda function to a given event. The function can be subscribed either to all event triggers or a specifc ID. - \item \emph{Trigger:} This triggers a given event and all callbacks correlating to this event are executed immediately. - \item \emph{Queue event:} This queues an event to be executed at a fixed time during the gameloop. - \item \emph{Unsubscibe:} This removes the callback function from the event and it will no longer be executed. -\end{itemize} -Figure \ref{fig:event-seq} shows that when a specific function is triggered or dispatched using the callback(eventHandler) is executed. + +The EventManager is a singleton so all all callbacks are stored in one place and it +can be called everywhere in the system or game. The user can use the EventManager for +the following functions:\noparbreak +\begin{description} + \item[Subscribe] This subscribes a function pointer or lambda function to a given + event. The function can be subscribed either to all event triggers or a specifc + ID. + \item[Trigger] This triggers a given event and all callbacks correlating to this + event are executed immediately. + \item[Queue event] This queues an event to be executed at a fixed time during the + gameloop. + \item[Unsubscibe] This removes the callback function from the event and it will no + longer be executed. +\end{description} + +\Cref{fig:event-seq} shows that when a specific function is triggered or dispatched +using the callback(eventHandler) is executed. + \begin{figure} \centering \includepumldiag{img/event-sequence.puml} \caption{Sequence diagram for event calling} \label{fig:event-seq} \end{figure} + % \subsection{Physics} + \subsection{Rendering} + \subsection{Scripting} The scripting interface was designed around a `target' \gls{api} (described by @@ -463,7 +654,7 @@ structs, which are used to organize options per system or engine component. \appendix -\section{\Glsfmtlongpl{poc}} +\section{Proof-of-concepts} The full (documented) source code of these \glspl{poc} is available on GitHub \autocite{crepe:code-repo}. @@ -538,5 +729,41 @@ 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. +\subsection{Camera} +\label{poc:camera} + +The camera \gls{poc} \autocite[camera example]{crepe:code-repo} consists of the +following:\noparbreak +\begin{itemize} + \item An \codeinline{on_key_pressed} function, which listens for key presses and + adjusts camera position and zoom based on key inputs. + \item A user-defined script class (\codeinline{MyCameraScript}) derived from + \codeinline{Script}, implementing only the \codeinline{update()} function. To + update the camera movements and zoom. + \item A main function that--- + \begin{itemize} + \item Subscribes the \codeinline{on_key_pressed} function to handle + \codeinline{KeyPressedEvent} events. + \item Creates a \codeinline{GameObject} for the camera and adds + \codeinline{Camera} and \codeinline{BehaviorScript} components, with + \codeinline{MyCameraScript} attached to manage the camera's transformation. + \item Instantiates a background \codeinline{GameObject} with + \codeinline{Transform} and \codeinline{Sprite} components, loading an + external texture as its background. + \end{itemize} +\end{itemize} + +Running this \gls{poc} allows for controlled camera movement and zoom in response to +key inputs. The \codeinline{MyCameraScript::update} function ensures that these +transformations are applied each frame, as demonstrated by the output in +\cref{fig:poc-output-camera}. + +\begin{figure} + \centering + \fitimg{\includegraphics[scale=0.7]{img/poc-camera.pdf}} + \caption{Camera \glsfmtshort{poc} output} + \label{fig:poc-output-camera} +\end{figure} + \end{document} |