aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--design.tex179
-rw-r--r--example.tex4
-rw-r--r--glossary.bib4
-rw-r--r--img/event-sequence.puml16
-rw-r--r--img/event-uml.drawio.pngbin0 -> 217362 bytes
-rw-r--r--img/gameloop-class.puml37
-rw-r--r--img/gameloop-flow.puml26
-rw-r--r--research.tex8
8 files changed, 266 insertions, 8 deletions
diff --git a/design.tex b/design.tex
index a7ad91e..6bef7dc 100644
--- a/design.tex
+++ b/design.tex
@@ -115,6 +115,26 @@ the main part of the \gls{ecs}. The design of these eight systems in combination
\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:\noparbreak
+\begin{itemize}
+ \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.
+
\subsection{Texture}
% FIXME: our
@@ -236,8 +256,165 @@ following classes:\noparbreak
\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();
+\end{blockcode}
+
+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{fig:gameloop-flow}
+\end{figure}
+
+\begin{figure}
+ \centering
+ \includepumldiag{img/gameloop-class.puml}
+ \caption{Gameloop Flowchart Diagram}
+ \label{fig:gameloop-class}
+\end{figure}
+
+\subsection{Event system}
+
+\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
+ % 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 \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}
+ % 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:\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
@@ -477,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}.
diff --git a/example.tex b/example.tex
index 9f36f59..3c487bd 100644
--- a/example.tex
+++ b/example.tex
@@ -164,8 +164,8 @@ The bibliography is automatically printed after \codeinline{\end{document}}.
\subsubsection{Glossary}
Glossary entries can be inserted using the \codeinline{\gls} commands. Example:
-``\Gls{sdl2} handles \glspl{hid} as well!''. In following occurrences of acronyms,
-only their short form is printed: `\gls{sdl2}' and `\gls{hid}'. All of these link to
+``\Gls{sdl} handles \glspl{hid} as well!''. In following occurrences of acronyms,
+only their short form is printed: `\gls{sdl}' and `\gls{hid}'. All of these link to
the glossary that is automatically printed after \codeinline{\end{document}}.
\subsubsection{Requirements}
diff --git a/glossary.bib b/glossary.bib
index 0842ff8..a846cb0 100644
--- a/glossary.bib
+++ b/glossary.bib
@@ -8,7 +8,7 @@
long = {Human Interface Device},
}
-@abbreviation{sdl2,
+@abbreviation{sdl,
short = {SDL2},
long = {Simple DirectMedia Layer},
}
@@ -56,3 +56,5 @@
description = {Design pattern used to provide an abstraction from other software},
}
+% TODO: delta time
+
diff --git a/img/event-sequence.puml b/img/event-sequence.puml
new file mode 100644
index 0000000..2555f33
--- /dev/null
+++ b/img/event-sequence.puml
@@ -0,0 +1,16 @@
+@startuml
+skinparam SequenceParticipantPadding 15
+
+
+participant EventHandler
+participant Publisher
+participant EventManager
+
+EventHandler -> EventManager : Subscribe event
+Publisher -> EventManager : trigger event
+
+EventManager -> EventHandler : execute callback function
+Publisher -> EventManager : Queue Event
+EventManager -> EventHandler : dispatching queued events
+EventHandler -> EventManager : unsubscribe event
+@enduml
diff --git a/img/event-uml.drawio.png b/img/event-uml.drawio.png
new file mode 100644
index 0000000..9eab458
--- /dev/null
+++ b/img/event-uml.drawio.png
Binary files differ
diff --git a/img/gameloop-class.puml b/img/gameloop-class.puml
new file mode 100644
index 0000000..e66b7d8
--- /dev/null
+++ b/img/gameloop-class.puml
@@ -0,0 +1,37 @@
+@startuml
+!include theme.ipuml
+class LoopManager {
+ +static LoopManager& getInstance()
+
+ +void loop()
+ +void setup()
+ -void render()
+ -void processInput()
+ -void fixedUpdate()
+ -void update()
+ -bool gameRunning
+ -LoopManager()
+}
+
+class LoopTimer {
+ +static LoopTimer& getInstance()
+ +void start()
+ +void update()
+ +float getLag()
+ +float getFixedDeltaTime()
+ +void advanceFixedUpdate()
+ +void enforceFrameRate()
+ +float getDeltaTime()
+ -float lag
+ -float fixedDeltaTime
+ -float deltaTime
+}
+
+LoopManager --> LoopTimer : uses
+LoopManager : loop()
+LoopManager : |-- processInput()
+LoopManager : |-- fixedUpdate()
+LoopManager : |-- update()
+LoopManager : |-- render()
+' TODO: what is this supposed to be?
+@enduml
diff --git a/img/gameloop-flow.puml b/img/gameloop-flow.puml
new file mode 100644
index 0000000..e75ea42
--- /dev/null
+++ b/img/gameloop-flow.puml
@@ -0,0 +1,26 @@
+@startuml
+!include theme.ipuml
+start
+
+:Initialize LoopManager;
+:Start LoopTimer;
+
+repeat
+ :Update LoopTimer;
+ :Check for Events;
+ :Process Input;
+
+ while (Lag >= Fixed Delta Time?) is (yes)
+ :Perform Fixed Update;
+ :Advance Fixed Update;
+ endwhile
+
+ :Perform Normal Update;
+ :Render Frame;
+
+repeat while (Game Running?)
+
+:Game exit logic;
+
+stop
+@enduml
diff --git a/research.tex b/research.tex
index 6853d5f..241dd8b 100644
--- a/research.tex
+++ b/research.tex
@@ -253,16 +253,16 @@ described.
A game engine must have the ability to handle user input, render graphics, and
process audio. Several large frameworks are available that provide these features and
are already widely used by other game engines. The two most popular and
-best-supported options are \gls{sdl2} and \gls{sfml}.
+best-supported options are \gls{sdl} and \gls{sfml}.
\paragraph{SDL2}
% TODO: ref?sdl2
-According to its official website, \gls{sdl2} is \emph{``a cross-platform development
+According to its official website, \gls{sdl} is \emph{``a cross-platform development
library designed to provide low-level access to audio, keyboard, mouse, joystick, and
graphics hardware via \gls{opengl} and \gls{d3d}. It is used by video playback
software, emulators, and popular games, including Valve's award-winning catalog and
-many Humble Bundle games.''} \gls{sdl2} is written in the C programming language, and
+many Humble Bundle games.''} \gls{sdl} is written in the C programming language, and
therefore, structs and functions are used instead of objects and methods.
\begin{comparison}
@@ -293,7 +293,7 @@ game development.
applications.}
\con{The 2D rendering engine may experience performance issues in large-scale
games.}
- \con{The community is smaller compared to \gls{sdl2}.}
+ \con{The community is smaller compared to \gls{sdl}.}
\con{No native 3D support is provided.}
\con{Not all image formats are supported.}
\end{comparison}