Paper game
Details
Project details
- C++, Windows
- Personal school project. Result after one year of programming
- Includes custom collision system.
- 2 enemy AI's, using A* pathfinding
- Completely built from scratch using C++ (also self-made textures and animations)
- Time scope: 3 months
About Paper game
Paper Game was my very first complete game. This was the result of what I learned in my first school
year, and for that
matter my first year of programming all together. Even though the movement felt a bit clunky, and the
path
finding algorithm I
wrote (based off of some pseudo code) was far from optimized, I was quiet proud of the final result.
Code snippet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
void RadarEnemy::FindPathToPlayer(int nrCols, const Point2f& targetPos, std::vector<LevelNode*>& levelNodes)
{
float distance{ utils::GetDistance(m_Position, targetPos) };
if ((distance < m_MaxKeepDetectingDistance && distance > m_MinTargetDistance) || distance < m_MinTargetDistance - 5.0f)
{
const int maxPathLength{ 500 };
const int horizontalDistance{ 10 };
const int verticalDistance{ 14 }; // = sqrt(200)
bool isOutOfRange{ false };
bool targetFound{};
LevelNode* startingNode{};
LevelNode* endNode{};
LevelNode* currentNode{};
std::vector<LevelNode*>openNodes{};
std::vector<LevelNode*>closedNodes{};
// Find starting and ending nodes.
startingNode = levelNodes[int(m_Position.y / levelNodes[0]->GetSize()) * nrCols + int(m_Position.x / levelNodes[0]->GetSize())];
endNode = levelNodes[int(targetPos.y / levelNodes[0]->GetSize()) * nrCols + int(targetPos.x / levelNodes[0]->GetSize())];
if (!endNode->IsTraversable())
{
endNode = m_PrevEndNode;
}
m_PrevEndNode = endNode;
if (endNode != nullptr)
{
int iterations{};
openNodes.push_back(startingNode);
while (!targetFound && iterations < maxPathLength)
{
++iterations;
int lowestFCost{};
bool isFirstCheck{ true };
// Find the open node with the lowest fCost.
for (LevelNode*& i : openNodes)
{
if (!isFirstCheck)
{
if ((i->GetFCost() < lowestFCost || lowestFCost == 0))
{
lowestFCost = i->GetFCost();
currentNode = i;
}
}
else
{
lowestFCost = i->GetFCost();
currentNode = i;
isFirstCheck = false;
}
}
// Find the current node in openNodes.
for (size_t i{ 0 }; i < openNodes.size(); ++i)
{
if (openNodes[i] == currentNode)
{
LevelNode* pTempNode{ openNodes[i] };
openNodes[i] = openNodes.back();
openNodes.back() = pTempNode;
openNodes.pop_back();
}
}
closedNodes.push_back(currentNode);
if (currentNode == endNode)
{
targetFound = true;
endNode->SetParent(currentNode->GetParent());
}
else
{
int currentColIdx{ currentNode->GetColIdx() };
int currentRowIdx{ currentNode->GetRowIdx() };
std::vector<LevelNode*>neighbours{};
// Stores the grid coordinates of the neighbours, first the colIdx, then the rowIdx, ...
std::vector<int> neigboursIndexes{
currentColIdx + 1, currentRowIdx,
currentColIdx + 1, currentRowIdx - 1,
currentColIdx + 1, currentRowIdx + 1,
currentColIdx, currentRowIdx + 1,
currentColIdx, currentRowIdx - 1,
currentColIdx - 1, currentRowIdx,
currentColIdx - 1, currentRowIdx + 1,
currentColIdx - 1, currentRowIdx - 1, };
// Store the neigbours in a vector.
for (int i{ 0 }; i < 8; ++i)
{
neighbours.push_back(levelNodes[neigboursIndexes[i * 2 + 1] * nrCols + neigboursIndexes[i * 2]]);
}
for (LevelNode*& i : neighbours)
{
bool neigbourIsClosed{ false };
// Search neigbour in closedNodes.
for (LevelNode*& j : closedNodes)
{
if (j == i)
{
neigbourIsClosed = true;
}
}
if (neigbourIsClosed || !i->IsTraversable())
{
// Skip to next neighbour.
continue;
}
else
{
int neighboursColIdx{ i->GetColIdx() };
int neighboursRowIdx{ i->GetRowIdx() };
// Calculate the GCost, if it were to go to this neighbour. This might be a shorter way.
int gCostToParent{};
int gCostToCurrent{};
// Only do this, if the parent is known.
if (i->GetParent() != nullptr)
{
int parentsColIdx{ i->GetParent()->GetColIdx() };
int parentsRowIdx{ i->GetParent()->GetRowIdx() };
if (StraightMovement(parentsColIdx, parentsRowIdx, neighboursColIdx, neighboursRowIdx))
{
gCostToParent = i->GetParent()->GetGCost() + horizontalDistance; // Straight movement is 10m.
}
else if (DiagonalMovement(parentsColIdx, parentsRowIdx, neighboursColIdx, neighboursRowIdx))
{
gCostToParent = i->GetParent()->GetGCost() + verticalDistance; // Diagonal movement is 14m (sqrt(200)).
}
if (StraightMovement(currentColIdx, currentRowIdx, neighboursColIdx, neighboursRowIdx))
{
gCostToCurrent = currentNode->GetGCost() + horizontalDistance; // Straight movement is 10m.
}
else if (DiagonalMovement(currentColIdx, currentRowIdx, neighboursColIdx, neighboursRowIdx))
{
gCostToCurrent = currentNode->GetGCost() + verticalDistance; // Diagonal movement is 14m (sqrt(200)).
}
}
// Search neighbour in openNodes.
bool neigbourIsOpen{ false };
for (LevelNode*& j : openNodes)
{
if (j == i)
{
neigbourIsOpen = true;
}
}
if ((gCostToCurrent < gCostToParent) || !neigbourIsOpen)
{
if (StraightMovement(currentColIdx, currentRowIdx, neighboursColIdx, neighboursRowIdx))
{
i->SetGCost(currentNode->GetGCost() + horizontalDistance); // Straight movement is 10m.
}
else if (DiagonalMovement(currentColIdx, currentRowIdx, neighboursColIdx, neighboursRowIdx))
{
i->SetGCost(currentNode->GetGCost() + verticalDistance); // Diagonal movement is 14m (sqrt(200)).
}
int dx{ abs(neighboursColIdx - endNode->GetColIdx()) };
int dy{ abs(neighboursRowIdx - endNode->GetRowIdx()) };
i->SetHCost(horizontalDistance * (dx + dy) + (verticalDistance - 2 * horizontalDistance) * std::min(dx, dy));
i->SetFCost(i->GetGCost() + i->GetHCost());
i->SetParent(levelNodes[currentRowIdx * nrCols + currentColIdx]);
if (!neigbourIsOpen)
{
openNodes.push_back(i);
}
}
}
}
}
}
if (iterations >= maxPathLength)
{
isOutOfRange = true;
}
if (!isOutOfRange)
{
FillPath(endNode, startingNode);
}
for (LevelNode*& j : openNodes)
{
levelNodes[j->GetRowIdx() * nrCols + j->GetColIdx()] = j;
}
for (LevelNode*& j : closedNodes)
{
levelNodes[j->GetRowIdx() * nrCols + j->GetColIdx()] = j;
}
}
}
}