mirror of
https://github.com/kuhyx/engineer-thesis-WUT.git
synced 2026-07-04 12:03:05 +02:00
feat: game level section
This commit is contained in:
parent
9c3f286ff8
commit
b1abc98a9d
Binary file not shown.
@ -180,23 +180,23 @@
|
|||||||
{\fontsize{12}{12}\helveticaLight and specialization Computer Systems and Networks \\[50pt]}
|
{\fontsize{12}{12}\helveticaLight and specialization Computer Systems and Networks \\[50pt]}
|
||||||
|
|
||||||
|
|
||||||
{\fontsize{14}{14}\helvetica \tytul \\[50pt]}
|
{\fontsize{14}{14}\helvetica \tytulEng \\[50pt]}
|
||||||
|
|
||||||
{\fontsize{12}{12}\helveticaLight numer pracy według wydzia{\fontsize{12}{12}\adagioLight ł}owej ewidencji prac \{liczba\} \\[12pt]}
|
{\fontsize{12}{12}\helveticaLight thesis number in the Faculty thesis register 103B-ISA-IN/307585/1202985 \\[12pt]}
|
||||||
|
|
||||||
{\fontsize{21}{21}\helvetica Krzysztof Rudnicki \\[12pt]}
|
{\fontsize{21}{21}\helvetica Krzysztof Stefan Rudnicki \\[12pt]}
|
||||||
|
|
||||||
{\fontsize{12}{12}\helveticaLight numer albumu 307585 \\[24pt]}
|
{\fontsize{12}{12}\helveticaLight student record book number 307585 \\[24pt]}
|
||||||
|
|
||||||
{\fontsize{12}{12}\helveticaLight promotor }
|
{\fontsize{12}{12}\helveticaLight thesis supervisor }
|
||||||
|
|
||||||
{\fontsize{12}{12}\helvetica dr hab. inż. Tomasz Martyn \\[12pt]}
|
{\fontsize{12}{12}\helvetica dr hab. inż. Tomasz Martyn \\[12pt]}
|
||||||
|
|
||||||
{\fontsize{12}{12}\helveticaLight konsultacje }
|
{\fontsize{12}{12}\helveticaLight konsultacje }
|
||||||
|
|
||||||
{\fontsize{12}{12}\helvetica dr hab. inż. Tomasz Martyn \\[40pt]}
|
{\fontsize{12}{12}\helvetica Tomasz Martyn \\[40pt]}
|
||||||
|
|
||||||
{\fontsize{12}{12}\helveticaLight Warszawa 2023}
|
{\fontsize{12}{12}\helveticaLight WARSAW 2023}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
|
|
||||||
@ -1482,7 +1482,179 @@ void Game::renderMain() {
|
|||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\section{Game level class}
|
\section{Game level class}
|
||||||
Game level class takes a 2D array of game objects instances (tiles), receives inputs from game class and reacts to them, it handles removing, swaping, adding new tiles on a map and checking for sequences of tiles
|
Game level class takes a 2D array of game objects instances (tiles), receives inputs from game class and reacts to them, it handles removing, swaping, adding new tiles on a map and checking for sequences of tiles. It also loads the level file and translates it to in game object
|
||||||
|
|
||||||
|
\paragraph{Reading file}
|
||||||
|
Game level class goes through each line of level file. \\
|
||||||
|
It loads each row to a vector of unsigned int as the level file contains rows of numbers representing tile type
|
||||||
|
\begin{figure}
|
||||||
|
\begin{lstlisting}
|
||||||
|
4 2 3 2 4 3 3 1 3 3
|
||||||
|
1 1 2 1 1 2 1 3 1 1
|
||||||
|
4 1 4 3 3 4 4 3 2 3
|
||||||
|
4 3 3 4 3 3 2 1 2 1
|
||||||
|
3 2 3 1 4 4 3 2 4 2
|
||||||
|
4 2 1 3 3 2 2 3 1 1
|
||||||
|
4 3 2 1 2 2 3 3 4 4
|
||||||
|
1 4 3 2 1 4 3 2 1 4
|
||||||
|
3 4 3 3 4 4 1 2 3 1
|
||||||
|
2 3 2 2 1 2 3 3 4 2
|
||||||
|
\end{lstlisting}
|
||||||
|
\caption{Exemplary level file}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\paragraph{Level initialization}
|
||||||
|
Initializing level takes the loaded vector of ids and creates game objects based on the window dimensions (so that the tiles fill the window evenly), textures are based on the tile id,
|
||||||
|
we assume that all levels are square so the width and height of the window is always the same.
|
||||||
|
\newpage1
|
||||||
|
\begin{lstlisting}[style=C++Style]
|
||||||
|
void GameLevel::handleTile(std::vector<std::vector<unsigned int>> tileData,
|
||||||
|
unsigned int x_coordinate, unsigned int y_coordinate,
|
||||||
|
float unit_width, float unit_height) {
|
||||||
|
const unsigned int tileNumber = tileData[y_coordinate][x_coordinate];
|
||||||
|
glm::vec3 color = this->assignColor(static_cast<int>(tileNumber));
|
||||||
|
|
||||||
|
// Directly use the constructor arguments for GameObject with emplace_back
|
||||||
|
this->Bricks.emplace_back(tileNumber,
|
||||||
|
glm::vec2(unit_width * static_cast<float>(x_coordinate), unit_height * static_cast<float>(y_coordinate)),
|
||||||
|
glm::vec2(unit_width, unit_height),
|
||||||
|
ResourceManager::GetTexture(idTextureMap[tileNumber]),
|
||||||
|
color
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameLevel::init(std::vector<std::vector<unsigned int>> tileData, unsigned int levelWidth, unsigned int levelHeight)
|
||||||
|
{
|
||||||
|
// calculate dimensions
|
||||||
|
unsigned int height = tileData.size();
|
||||||
|
unsigned int width = tileData[0].size(); // note we can index vector at [0] since this function is only called if height > 0
|
||||||
|
float unit_width = static_cast<float>(levelWidth) / static_cast<float>(width);
|
||||||
|
// initialize level tiles based on tileData
|
||||||
|
for (unsigned int y_coordinate = 0; y_coordinate < height; ++y_coordinate)
|
||||||
|
{
|
||||||
|
for (unsigned int x_coordinate = 0; x_coordinate < width; ++x_coordinate)
|
||||||
|
{
|
||||||
|
this->handleTile(tileData, x_coordinate, y_coordinate, unit_width, unit_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
2
|
||||||
|
\subsection{Highlighting, selecting and swapping tiles}
|
||||||
|
There are four steps in order for the tile to be swapped, logic of fulfilling this conditions is handled in game level class
|
||||||
|
|
||||||
|
\paragraph{Highlighting tile}
|
||||||
|
First the tile must be highlighted, game level makes sure that only one tile is highlighted by keeping the index of highlighted tile and changing it whenever user inputs a change
|
||||||
|
|
||||||
|
\paragraph{Selecting tile}
|
||||||
|
Then the tile must be selected, again game level has a special parameter just to keep track of which tile is selected
|
||||||
|
\begin{lstlisting}[style=C++Style]
|
||||||
|
void GameLevel::selectPiece() {
|
||||||
|
this->Bricks.at(selectedTile).Selected = true;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameLevel::unselectPiece() {
|
||||||
|
this->Bricks.at(selectedTile).Selected = false;
|
||||||
|
this->unswapAll();
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\paragraph{Which tile will be swapped}
|
||||||
|
Then the user must choose which tile will be swapped, game level does not keep special parameter for that, it just automatically deselects all tiles in the selected tile neighboorhood if user choose any other tile
|
||||||
|
\begin{lstlisting}[style=C++Style]
|
||||||
|
void GameLevel::moveRight()
|
||||||
|
{
|
||||||
|
if(!this->Bricks.at(selectedTile).Selected) {
|
||||||
|
this->Bricks.at(selectedTile).Highlighted = false;
|
||||||
|
this->selectedTile++;
|
||||||
|
if(static_cast<size_t>(this->selectedTile) > this->Bricks.size()) {
|
||||||
|
this->selectedTile--;
|
||||||
|
}
|
||||||
|
this->Bricks.at(selectedTile).Highlighted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->swapRight();
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\paragraph{Confirming swap}
|
||||||
|
At the end user must confirm the swap, then the swap is checked if it creates the pattern of at least 3 tiles
|
||||||
|
\begin{lstlisting}[style=C++Style]
|
||||||
|
void GameLevel::confirmSwap()
|
||||||
|
{
|
||||||
|
const bool canSwap = this->checkSwap();
|
||||||
|
if(this->swappableTile != -1 && canSwap) {
|
||||||
|
this->Bricks.at(selectedTile).Selected = false;
|
||||||
|
this->Bricks.at(selectedTile).Highlighted = false;
|
||||||
|
this->unswapAll();
|
||||||
|
const GameObject temp = this->Bricks[this->swappableTile];
|
||||||
|
this->Bricks[this->swappableTile].Position = this->Bricks[this->selectedTile].Position;
|
||||||
|
this->Bricks[this->selectedTile].Position = temp.Position;
|
||||||
|
std::swap(this->Bricks[this->swappableTile], this->Bricks[this->selectedTile]);
|
||||||
|
this->selectedTile = 0;
|
||||||
|
this -> swappableTile = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameLevel::selectPiece() {
|
||||||
|
this->Bricks.at(selectedTile).Selected = true;
|
||||||
|
if(this->Bricks.at(selectedTile).Selected) {
|
||||||
|
this->confirmSwap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\subsection{Matching logic}
|
||||||
|
When 3, 4 or 5 tiles get match in a row or column, matching function removes those tiles, fills the empty space with blocks from above and fills the final empty space with randomly generated tiles
|
||||||
|
|
||||||
|
\paragraph{Finding matches}
|
||||||
|
To simplify finding matches first game objects tiles is transformed into vector of ids, then this vector is converted into vectors of columns and rows and finaly those vectors are checked for matches
|
||||||
|
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}[node distance=2cm]
|
||||||
|
\useasboundingbox (-5,0) rectangle (5, -9); % Set a custom bounding box
|
||||||
|
\node (start) [startstop] {Find matches};
|
||||||
|
\node (pro1) [process, below of=start] {Convert to ids vector};
|
||||||
|
\draw [arrow] (start) -- (pro1);
|
||||||
|
\node (pro2) [process, below of=pro1] {Convert to rows and columns ids vectors};
|
||||||
|
\draw [arrow] (pro1) -- (pro2);
|
||||||
|
\node (pro3) [process, below of=pro2] {Find matches};
|
||||||
|
\draw [arrow] (pro2) -- (pro3);
|
||||||
|
\node (stop) [startstop, below of=pro3] {Return matches};
|
||||||
|
\draw [arrow] (pro3) -- (stop);
|
||||||
|
\end{tikzpicture}
|
||||||
|
\caption{Finding matches logic}
|
||||||
|
\label{fig:findingMatches}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsubsection{Processing matches}
|
||||||
|
\paragraph{Removing blocks}
|
||||||
|
After finding matches, blocks where matches appear are set to be replaced by replacing their tile ids with -1
|
||||||
|
\paragraph{Replacing blocks}
|
||||||
|
After setting tiles to be replaced, algorithm goes through blocks with ids equal to -1, checks if there are any blocks above, if yes it replaces the block from above with block from below, if there is no block above, it generates one randomly
|
||||||
|
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}[node distance=2cm]
|
||||||
|
\useasboundingbox (-5,0) rectangle (5, -11); % Set a custom bounding box
|
||||||
|
\node (start) [startstop] {Processing matches};
|
||||||
|
\node (dec1) [decision, below of=start, yshift=-1cm] {Block id is -1?};
|
||||||
|
\draw [arrow] (start) -- (dec1);
|
||||||
|
\node (pro1) [process, below of=dec1] {Replace blocks};
|
||||||
|
\node (dec2) [decision, below of=pro1, yshift=-2cm] {Blocks above?};
|
||||||
|
\draw [arrow] (pro1) -- (dec2);
|
||||||
|
\draw [arrow] (dec2) -- (pro1);
|
||||||
|
\node (pro2) [process, below of=pro1] {Generate new blocks};
|
||||||
|
\draw [arrow] (pro1) -- (pro2);
|
||||||
|
\node (stop) [startstop, below of=pro1] {End processing};
|
||||||
|
\draw [arrow] (pro2) -- (stop);
|
||||||
|
\draw [arrow] (pro1) -- (stop);
|
||||||
|
\end{tikzpicture}
|
||||||
|
\caption{Finding matches logic}
|
||||||
|
\label{fig:findingMatches}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
\subsection{Dependency Management}
|
\subsection{Dependency Management}
|
||||||
There are several libraries: OpenGL, GLFW, stb\_image and freetype being integrated into engine, they are simply included at the top of any files that need them.
|
There are several libraries: OpenGL, GLFW, stb\_image and freetype being integrated into engine, they are simply included at the top of any files that need them.
|
||||||
|
|||||||
64
matchingLogic.txt
Normal file
64
matchingLogic.txt
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
Given a vector of n x n elements
|
||||||
|
0. simplify vector from game object to vector of unsigned int id:
|
||||||
|
GameObject:
|
||||||
|
id
|
||||||
|
texture
|
||||||
|
position
|
||||||
|
...
|
||||||
|
-> id
|
||||||
|
we get a vector:
|
||||||
|
{
|
||||||
|
0 1 2
|
||||||
|
2 1 0
|
||||||
|
1 2 0
|
||||||
|
}
|
||||||
|
1. split the vector into vertical and horizontal
|
||||||
|
vertical:
|
||||||
|
calculate square root of vector size -> thats the length of single row and column
|
||||||
|
a) split horizontaly
|
||||||
|
get first n elements -> thats first vertical
|
||||||
|
then get second n elements -> thats second vertical
|
||||||
|
...
|
||||||
|
in the end we get 3 vectors:
|
||||||
|
{ 0, 1, 2}
|
||||||
|
{ 2, 1, 0}
|
||||||
|
{ 1, 2, 0}
|
||||||
|
b) split vertically
|
||||||
|
get first element [0]
|
||||||
|
add "n" elements
|
||||||
|
get second column [n] (in our case 3)
|
||||||
|
repeat until number of column + n < n^2
|
||||||
|
increment first element by 1
|
||||||
|
go to loop start if first element <= n (3)
|
||||||
|
in the end we get 3 vectors
|
||||||
|
{ 0, 2, 1 }
|
||||||
|
{ 1, 1, 2 }
|
||||||
|
{ 2, 0, 0}
|
||||||
|
Matching logic:
|
||||||
|
1. Find matches
|
||||||
|
a) Vertical
|
||||||
|
t t t
|
||||||
|
t t t
|
||||||
|
t t t
|
||||||
|
t t
|
||||||
|
t
|
||||||
|
I)
|
||||||
|
Since we splited our vectors into vectors of ids we made our job much easier
|
||||||
|
count = 1
|
||||||
|
take first element
|
||||||
|
take second element
|
||||||
|
check if first == second
|
||||||
|
if yes count += 1
|
||||||
|
if no count = 1
|
||||||
|
take third element
|
||||||
|
check if second == third
|
||||||
|
if yes count += 1
|
||||||
|
if no = 1
|
||||||
|
if count >= 3
|
||||||
|
matchesVector.push_back(0, 1, 2)
|
||||||
|
if count == 1
|
||||||
|
|
||||||
|
b) horizontal
|
||||||
|
t t t
|
||||||
|
t t t t
|
||||||
|
t t t t t
|
||||||
Loading…
Reference in New Issue
Block a user