8 Commits

Author SHA1 Message Date
dr20ervin 2672a54b4c Refactor scoring and add Game Over screen 2026-05-20 14:24:05 +03:00
dr20ervin ce3df94b68 Add textures, gameplay pause, and menu state placeholders 2026-05-20 14:08:57 +03:00
dr20ervin 25a7fefa04 Add textures, gameplay pause, and menu state placeholders. 2026-05-20 13:59:01 +03:00
Vajda Ervin-Oliver 1c7762ea3b Update .gitattributes 2026-05-20 13:48:51 +03:00
Vajda Ervin-Oliver a651fa6870 Delete . gitattributes 2026-05-20 13:47:29 +03:00
Vajda Ervin-Oliver 1664939b69 Create . gitattributes 2026-05-20 13:47:00 +03:00
Vajda Ervin-Oliver 434b9c3064 Fix typo in project description 2026-05-20 13:02:14 +03:00
dr20ervin 67f5fcf374 Uploading assets & developing multiplayer implementations 2026-05-20 12:11:04 +03:00
20 changed files with 423 additions and 66 deletions
+4
View File
@@ -2,6 +2,10 @@
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
# Mark specific Raylib files as vendored to exclude from language stats
include/raylib.h linguist-vendored
include/raymath.h linguist-vendored
include/rlgl.h linguist-vendored
###############################################################################
# Set default behavior for command prompt diff.
+25 -5
View File
@@ -2,9 +2,29 @@
A classic arcade game rebuilt from the ground up using modern C++ and the Raylib library.
**Features:**
* Clean, Object-Oriented entity architecture.
* Game state management (Main Menu, Gameplay, Game Over).
* A CPU AI opponent with adjustable difficulty levels (Easy, Normal, Hard).
---
*This is a project developed for UTCN - ETTI Helios Additional_activity.*
## Credits & Attributions
### Code Base Template
* **Original Author:** educ8s (Nick Koumaris)
* **Original Repository:** [github.com/educ8s/Cpp-Pong-Game-Raylib](https://github.com/educ8s/Cpp-Pong-Game-Raylib)
* **Description:** This project utilizes core architectural patterns and Raylib integrations adapted from the original open-source template repository listed above.
### Asset Credits & Attribution
The multimedia resources (graphics, sprites, and audio files) used in this game are not my personal creations. They have been curated and adapted from the open-source community for educational development.
* **Original Project:** Moddable Pong
* **Author / Creator:** Endless OS Foundation & Endless Studios
* **Website:** [endlessos.org](https://endlessos.org)
* **Source Code Repository:** [github.com/endlessm/moddable-pong](https://github.com/endlessm/moddable-pong/)
* **Usage Notice:** These assets are integrated strictly for non-commercial, academic layout validation and development milestones. All rights, ownership, and copyrights belong entirely to the original creators at the Endless OS Foundation.
---
## Features
* **Clean Object-Oriented Architecture:** Developed using rigorous OOP principles, utilizing a base abstract `GameObject` class with virtual override structures for specialized modular entities (`Ball`, `Paddle`, and `CpuPaddle`).
* **Robust Game State Machine:** Features an integrated game loop managing distinct application states including `MainMenu`, `DifficultySelect`, `Multiplayer`, `Settings`, and `Playing`.
* **Dynamic AI Opponent:** A singleplayer CPU opponent equipped with configurable movement velocities tailored across three distinct difficulty tiers: Easy, Normal, and Hard.
* **Enhanced Visual Layout:** Fully upgraded with high-resolution texture mapping via Raylib's `DrawTexturePro`, featuring dedicated game court backdrops, localized wall segments, styled central dashed lines, and custom entity sprites.
* **Academic Context:** This work is part of a student engineering project within the Helios Additional_activity initiative at the Technical University of Cluj-Napoca (UTCN) - Faculty of Electronics, Telecommunications and Information Technology (ETTI).
+16
View File
@@ -0,0 +1,16 @@
# Asset Credits & Attribution
The multimedia resources (graphics, sprites, and audio files) contained within this folder are not my personal creations. They have been curated and adapted from the open-source community for educational development.
## Original Source Information
* **Project:** Moddable Pong
* **Author / Creator:** Endless OS Foundation & Endless Studios
* **Website:** [endlessos.org](https://endlessos.org)
* **Source Code Repository:** [github.com/endlessm/moddable-pong](https://github.com/endlessm/moddable-pong/)
---
## Usage Notice
These assets are integrated strictly for non-commercial, academic layout validation and development milestones. This work is part of a student engineering project within the **Helios Additional_activity** initiative at the **Technical University of Cluj-Napoca (UTCN) - Faculty of Electronics, Telecommunications and Information Technology (ETTI)**.
All rights, ownership, and copyrights belong entirely to the original creators at the Endless OS Foundation.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+37 -10
View File
@@ -7,6 +7,9 @@
// Game States & Configurations
enum class GameState {
MainMenu,
DifficultySelect,
Multiplayer,
Settings,
Playing,
Paused,
GameOver
@@ -28,8 +31,6 @@ class GameObject {
public:
Vector2 position;
Color color;
public:
GameObject(Vector2 pos, Color c) : position(pos), color(c) {}
virtual ~GameObject() = default;
@@ -43,9 +44,22 @@ class Paddle : public GameObject {
public:
float width;
float height;
Texture2D texture;
Paddle(Vector2 pos, Color c, float w, float h)
public:
Paddle(Vector2 pos, Color c, float w, float h, const std::string& texturePath = "")
: GameObject(pos, c), width(w), height(h) {
if (!texturePath.empty()) {
texture = LoadTexture(texturePath.c_str());
} else {
texture = { 0 };
}
}
~Paddle() override {
if (texture.id > 0) {
UnloadTexture(texture);
}
}
void Update() override;
@@ -56,9 +70,22 @@ class Ball : public GameObject {
public:
float radius;
Vector2 velocity;
Texture2D texture;
Ball(Vector2 pos, Color c, float r)
public:
Ball(Vector2 pos, Color c, float r, const std::string& texturePath = "")
: GameObject(pos, c), radius(r), velocity({ 5.0f, 5.0f }) {
if (!texturePath.empty()) {
texture = LoadTexture(texturePath.c_str());
} else {
texture = { 0 };
}
}
~Ball() override {
if (texture.id > 0) {
UnloadTexture(texture);
}
}
void Update() override;
@@ -71,8 +98,8 @@ private:
Difficulty currentDifficulty;
public:
CpuPaddle(Vector2 pos, Color c, float w, float h, Difficulty diff = Difficulty::Normal)
: Paddle(pos, c, w, h) {
CpuPaddle(Vector2 pos, Color c, float w, float h, Difficulty diff = Difficulty::Normal, const std::string& texturePath = "")
: Paddle(pos, c, w, h, texturePath) {
SetDifficulty(diff);
}
@@ -101,11 +128,11 @@ public:
void Update() override {}
void LimitMovement() {
if (position.y <= 0) {
position.y = 0;
if (position.y <= 20.0f) {
position.y = 20.0f;
}
if (position.y + height >= GetScreenHeight()) {
position.y = GetScreenHeight() - height;
if (position.y + height >= GetScreenHeight() - 20.0f) {
position.y = GetScreenHeight() - 20.0f - height;
}
}
};
+1 -1
View File
@@ -16,6 +16,6 @@ public:
: title(menuTitle), options(menuOptions), selectedIndex(0) {
}
void Update(GameState& currentState);
int Update();
void Draw();
};
+8
View File
@@ -145,6 +145,14 @@
<ClInclude Include="include\raymath.h" />
<ClInclude Include="include\rlgl.h" />
</ItemGroup>
<ItemGroup>
<Image Include="assets\textures\ball\basic_ball_5.png" />
<Image Include="assets\textures\hud\line.png" />
<Image Include="assets\textures\paddles\basic_paddle.png" />
<Image Include="assets\textures\paddles\basic_paddle_2.png" />
<Image Include="assets\textures\spaces\basic_space.png" />
<Image Include="assets\textures\spaces\walls.png" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
+26
View File
@@ -16,6 +16,12 @@
<Filter Include="Header Files\RayLib">
<UniqueIdentifier>{2f179593-5e9f-4095-be57-3f12f03a9705}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files\textures">
<UniqueIdentifier>{2d4ce555-d6fc-4e9c-a30e-eadb4c1336d0}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files\audio">
<UniqueIdentifier>{5f42ff18-01e2-4266-a4e0-6dc77ba54e75}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\main.cpp">
@@ -45,4 +51,24 @@
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="assets\textures\ball\basic_ball_5.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\hud\line.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\paddles\basic_paddle.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\paddles\basic_paddle_2.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\spaces\basic_space.png">
<Filter>Resource Files\textures</Filter>
</Image>
<Image Include="assets\textures\spaces\walls.png">
<Filter>Resource Files\textures</Filter>
</Image>
</ItemGroup>
</Project>
+30 -8
View File
@@ -13,16 +13,27 @@ void Paddle::Update() {
}
// Limit movement
if (position.y <= 0) {
position.y = 0;
if (position.y <= 20.0f) {
position.y = 20.0f;
}
if (position.y + height >= GetScreenHeight()) {
position.y = GetScreenHeight() - height;
if (position.y + height >= GetScreenHeight() - 20.0f) {
position.y = GetScreenHeight() - 20.0f - height;
}
}
void Paddle::Draw() {
DrawRectangleRounded(Rectangle{ position.x, position.y, width, height }, 0.8f, 0, color);
if (texture.id > 0) {
DrawTexturePro(
texture,
Rectangle{ 0.0f, 0.0f, (float)texture.width, (float)texture.height },
Rectangle{ position.x, position.y, width, height },
Vector2{ 0.0f, 0.0f },
0.0f,
WHITE
);
} else {
DrawRectangleRounded(Rectangle{ position.x, position.y, width, height }, 0.8f, 0, color);
}
}
@@ -32,12 +43,23 @@ void Ball::Update() {
position.x += velocity.x;
position.y += velocity.y;
if (position.y + radius >= GetScreenHeight() || position.y - radius <= 0) {
if (position.y + radius >= GetScreenHeight() - 20.0f || position.y - radius <= 20.0f) {
velocity.y *= -1;
}
}
void Ball::Draw() {
// Cast to int as DrawCircle expects integers for coordinates
DrawCircle((int)position.x, (int)position.y, radius, color);
if (texture.id > 0) {
DrawTexturePro(
texture,
Rectangle{ 0.0f, 0.0f, (float)texture.width, (float)texture.height },
Rectangle{ position.x - radius, position.y - radius, radius * 2.0f, radius * 2.0f },
Vector2{ 0.0f, 0.0f },
0.0f,
WHITE
);
} else {
// Cast to int as DrawCircle expects integers for coordinates
DrawCircle((int)position.x, (int)position.y, radius, color);
}
}
+268 -32
View File
@@ -8,8 +8,13 @@ Color Dark_Green = Color{ 20, 160, 133, 255 };
Color Light_Green = Color{ 129, 204, 184, 255 };
Color Yellow = Color{ 243, 213, 91, 255 };
int player_score = 0;
int cpu_score = 0;
struct ScoreBoard
{
int player_score = 0;
int player2_score = 0;
int cpu_score = 0;
};
// Helper function to reset the ball
void ResetBall(Ball& ball, int screenWidth, int screenHeight) {
@@ -25,78 +30,305 @@ int main() {
std::cout << "Starting the game" << std::endl;
const int screen_width = 1280;
const int screen_height = 800;
InitWindow(screen_width, screen_height, "My Pong Game!");
InitWindow(screen_width, screen_height, "Pong Reloaded");
SetTargetFPS(60);
// --- Instantiate Objects using the new Constructors ---
Ball ball(Vector2{ screen_width / 2.0f, screen_height / 2.0f }, Yellow, 20.0f);
Ball ball(
Vector2{ screen_width / 2.0f, screen_height / 2.0f },
Yellow, 20.0f,
"assets/textures/ball/basic_ball_5.png"
);
ball.velocity = Vector2{ 7.0f, 7.0f };
Paddle player(
Vector2{ screen_width - 35.0f, screen_height / 2.0f - 60.0f },
WHITE, 25.0f, 120.0f
Vector2{ screen_width - 20.0f - 10.0f - 25.0f, screen_height / 2.0f - 60.0f },
WHITE, 25.0f, 120.0f,
"assets/textures/paddles/basic_paddle.png"
);
CpuPaddle cpu(
Vector2{ 10.0f, screen_height / 2.0f - 60.0f },
Vector2{ 20.0f + 10.0f, screen_height / 2.0f - 60.0f },
WHITE, 25.0f, 120.0f,
Difficulty::Normal
);
Difficulty::Normal,
"assets/textures/paddles/basic_paddle_2.png"
);
// --- Setup Menu and Game State ---
GameState currentState = GameState::MainMenu;
Menu mainMenu("PONG RELOADED", { "Start Game", "Quit" });
float sessionPlayTime = 0.0f;
bool isPaused = false;
bool shouldQuit = false;
Menu mainMenu("PONG RELOADED", { "Singleplayer", "Multiplayer", "Settings", "Quit" });
Menu difficultyMenu("SELECT DIFFICULTY", { "Easy", "Normal", "Hard", "Back" });
ScoreBoard score;
// --- Load Background and Wall Textures ---
Texture2D courtBackground = LoadTexture("assets/textures/spaces/basic_space.png");
Texture2D wallsTexture = LoadTexture("assets/textures/spaces/walls.png");
Texture2D lineTexture = LoadTexture("assets/textures/hud/line.png");
// --- Main Game Loop ---
while (WindowShouldClose() == false && currentState != GameState::GameOver) {
while (WindowShouldClose() == false && !shouldQuit) {
BeginDrawing();
ClearBackground(Dark_Green);
switch (currentState) {
case GameState::MainMenu:
{
mainMenu.Update(currentState);
int selected = mainMenu.Update();
if (selected == 0) {
currentState = GameState::DifficultySelect;
}
else if (selected == 1) {
currentState = GameState::Multiplayer;
}
else if (selected == 2) {
currentState = GameState::Settings;
}
else if (selected == 3) {
shouldQuit = true;
}
mainMenu.Draw();
break;
}
case GameState::DifficultySelect:
{
int selected = difficultyMenu.Update();
if (selected >= 0 && selected <= 2) {
if (selected == 0) {
cpu.SetDifficulty(Difficulty::Easy);
}
else if (selected == 1) {
cpu.SetDifficulty(Difficulty::Normal);
}
else if (selected == 2) {
cpu.SetDifficulty(Difficulty::Hard);
}
// Reset game session stats
score.player_score = 0;
score.cpu_score = 0;
sessionPlayTime = 0.0f;
isPaused = false;
ResetBall(ball, screen_width, screen_height);
currentState = GameState::Playing;
}
else if (selected == 3) {
currentState = GameState::MainMenu;
}
difficultyMenu.Draw();
break;
}
case GameState::Playing:
{
ball.Update();
player.Update();
cpu.Update(ball.position.y);
if (CheckCollisionCircleRec(ball.position, ball.radius, Rectangle{ player.position.x, player.position.y, player.width, player.height })) {
ball.velocity.x *= -1;
// Toggle pause state with 'P' key
if (IsKeyPressed(KEY_P)) {
isPaused = !isPaused;
}
if (CheckCollisionCircleRec(ball.position, ball.radius, Rectangle{ cpu.position.x, cpu.position.y, cpu.width, cpu.height })) {
ball.velocity.x *= -1;
// Force GameOver with SPACEBAR key
if (IsKeyPressed(KEY_SPACE)) {
currentState = GameState::GameOver;
}
if (ball.position.x + ball.radius >= screen_width) {
cpu_score++;
ResetBall(ball, screen_width, screen_height);
}
if (ball.position.x - ball.radius <= 0) {
player_score++;
ResetBall(ball, screen_width, screen_height);
if (!isPaused) {
sessionPlayTime += GetFrameTime();
ball.Update();
player.Update();
cpu.Update(ball.position.y);
if (CheckCollisionCircleRec(ball.position, ball.radius, Rectangle{ player.position.x, player.position.y, player.width, player.height })) {
ball.velocity.x *= -1;
}
if (CheckCollisionCircleRec(ball.position, ball.radius, Rectangle{ cpu.position.x, cpu.position.y, cpu.width, cpu.height })) {
ball.velocity.x *= -1;
}
if (ball.position.x + ball.radius >= screen_width - 20.0f) {
score.cpu_score++;
if (score.cpu_score >= 5) {
currentState = GameState::GameOver;
}
else {
ResetBall(ball, screen_width, screen_height);
}
}
if (ball.position.x - ball.radius <= 20.0f) {
score.player_score++;
if (score.player_score >= 5) {
currentState = GameState::GameOver;
}
else {
ResetBall(ball, screen_width, screen_height);
}
}
}
DrawRectangle(screen_width / 2, 0, screen_width / 2, screen_height, Green);
DrawCircle(screen_width / 2, screen_height / 2, 150, Light_Green);
DrawLine(screen_width / 2, 0, screen_width / 2, screen_height, WHITE);
// --- Draw Textured Court Background ---
// Left Court (basic_space.png)
DrawTexturePro(
courtBackground,
Rectangle{ 0.0f, 0.0f, (float)courtBackground.width, (float)courtBackground.height },
Rectangle{ 20.0f, 20.0f, 570.0f, 760.0f },
Vector2{ 0.0f, 0.0f },
0.0f,
WHITE
);
DrawText(TextFormat("%i", cpu_score), screen_width / 4 - 20, 20, 80, WHITE);
DrawText(TextFormat("%i", player_score), 3 * screen_width / 4 - 20, 20, 80, WHITE);
// Right Court (basic_space.png)
DrawTexturePro(
courtBackground,
Rectangle{ 0.0f, 0.0f, (float)courtBackground.width, (float)courtBackground.height },
Rectangle{ 690.0f, 20.0f, 570.0f, 760.0f },
Vector2{ 0.0f, 0.0f },
0.0f,
WHITE
);
// Center strip (Dark Green background area) - already cleared by ClearBackground
// Center Circle
DrawCircle(screen_width / 2, screen_height / 2, 50.0f, Color{ 102, 51, 153, 100 });
DrawCircleLines(screen_width / 2, screen_height / 2, 50.0f, Color{ 50, 25, 75, 250 });
// Tiled Center Dashed Line (line.png)
int lineY = 20;
while (lineY < 780) {
DrawTexture(lineTexture, screen_width / 2 - lineTexture.width / 2, lineY, WHITE);
lineY += lineTexture.height;
}
// Top Wall (walls.png)
DrawTexturePro(
wallsTexture,
Rectangle{ 0.0f, 0.0f, (float)wallsTexture.width, (float)wallsTexture.height },
Rectangle{ 0.0f, 0.0f, (float)screen_width, 20.0f },
Vector2{ 0.0f, 0.0f },
0.0f,
WHITE
);
// Bottom Wall (walls.png)
DrawTexturePro(
wallsTexture,
Rectangle{ 0.0f, 0.0f, (float)wallsTexture.width, (float)wallsTexture.height },
Rectangle{ 0.0f, (float)screen_height - 20.0f, (float)screen_width, 20.0f },
Vector2{ 0.0f, 0.0f },
0.0f,
WHITE
);
// Left Wall (walls.png)
DrawTexturePro(
wallsTexture,
Rectangle{ 0.0f, 0.0f, (float)wallsTexture.width, (float)wallsTexture.height },
Rectangle{ 0.0f, 0.0f, 20.0f, (float)screen_height },
Vector2{ 0.0f, 0.0f },
0.0f,
WHITE
);
// Right Wall (walls.png)
DrawTexturePro(
wallsTexture,
Rectangle{ 0.0f, 0.0f, (float)wallsTexture.width, (float)wallsTexture.height },
Rectangle{ (float)screen_width - 20.0f, 0.0f, 20.0f, (float)screen_height },
Vector2{ 0.0f, 0.0f },
0.0f,
WHITE
);
DrawText(TextFormat("%i", score.cpu_score), screen_width / 4 - 20, 20, 80, WHITE);
DrawText(TextFormat("%i", score.player_score), 3 * screen_width / 4 - 20, 20, 80, WHITE);
// Draw Playtime Counter
int minutes = (int)sessionPlayTime / 60;
int seconds = (int)sessionPlayTime % 60;
int timeTextWidth = MeasureText(TextFormat("%02i:%02i", minutes, seconds), 32);
// Draw background box to block out the center line and increase contrast
DrawRectangle(screen_width / 2 - timeTextWidth / 2 - 15, 715, timeTextWidth + 30, 44, Color{ 15, 15, 15, 220 });
DrawRectangleLines(screen_width / 2 - timeTextWidth / 2 - 15, 715, timeTextWidth + 30, 44, Color{ 100, 100, 100, 255 });
DrawText(TextFormat("%02i:%02i", minutes, seconds), screen_width / 2 - timeTextWidth / 2, 721, 32, YELLOW);
ball.Draw();
cpu.Draw();
player.Draw();
// Draw Pause Overlay and Text
if (isPaused) {
// Semi-transparent overlay inside the borders
DrawRectangle(20, 20, screen_width - 40, screen_height - 40, Color{ 0, 0, 0, 150 });
int pausedTextWidth = MeasureText("PAUSED", 60);
DrawText("PAUSED", screen_width / 2 - pausedTextWidth / 2, screen_height / 2 - 30, 60, YELLOW);
}
break;
}
case GameState::GameOver:
{
if (IsKeyPressed(KEY_SPACE)) {
currentState = GameState::MainMenu;
}
// Draw a nice centered dark panel for game over info
int panelWidth = 600;
int panelHeight = 400;
int panelX = screen_width / 2 - panelWidth / 2;
int panelY = screen_height / 2 - panelHeight / 2;
// Semi-transparent background panel
DrawRectangle(panelX, panelY, panelWidth, panelHeight, Color{ 15, 15, 15, 230 });
DrawRectangleLines(panelX, panelY, panelWidth, panelHeight, Color{ 120, 120, 120, 255 });
// Draw "GAME OVER" header
int gameOverWidth = MeasureText("GAME OVER", 60);
DrawText("GAME OVER", screen_width / 2 - gameOverWidth / 2, panelY + 40, 60, RED);
// Determine winner text and color
std::string winnerText = "GAME ENDED";
Color winnerColor = YELLOW;
if (score.player_score > score.cpu_score) {
winnerText = "YOU WIN!";
winnerColor = Green;
}
else if (score.cpu_score > score.player_score) {
winnerText = "CPU WINS!";
winnerColor = RED;
}
int winnerWidth = MeasureText(winnerText.c_str(), 40);
DrawText(winnerText.c_str(), screen_width / 2 - winnerWidth / 2, panelY + 120, 40, winnerColor);
// Draw final scores
std::string playerScoreStr = "Player Score: " + std::to_string(score.player_score);
std::string cpuScoreStr = "CPU Score: " + std::to_string(score.cpu_score);
int playerTextWidth = MeasureText(playerScoreStr.c_str(), 30);
int cpuTextWidth = MeasureText(cpuScoreStr.c_str(), 30);
DrawText(playerScoreStr.c_str(), screen_width / 2 - playerTextWidth / 2, panelY + 200, 30, WHITE);
DrawText(cpuScoreStr.c_str(), screen_width / 2 - cpuTextWidth / 2, panelY + 250, 30, WHITE);
// Draw playtime
int minutes = (int)sessionPlayTime / 60;
int seconds = (int)sessionPlayTime % 60;
std::string timeStr = "Playtime: " + std::to_string(minutes) + "m " + std::to_string(seconds) + "s";
int timeTextWidth = MeasureText(timeStr.c_str(), 20);
DrawText(timeStr.c_str(), screen_width / 2 - timeTextWidth / 2, panelY + 300, 20, LIGHTGRAY);
// Instruction to return
int instWidth = MeasureText("Press SPACEBAR to return to Main Menu", 20);
DrawText("Press SPACEBAR to return to Main Menu", screen_width / 2 - instWidth / 2, panelY + 340, 20, YELLOW);
break;
}
default:
@@ -106,6 +338,10 @@ int main() {
EndDrawing();
}
UnloadTexture(courtBackground);
UnloadTexture(wallsTexture);
UnloadTexture(lineTexture);
CloseWindow();
return 0;
}
+8 -10
View File
@@ -1,25 +1,21 @@
#include "menu.h"
void Menu::Update(GameState& currentState) {
int Menu::Update() {
// Handle Navigation
if (IsKeyPressed(KEY_DOWN)) {
selectedIndex++;
if (selectedIndex >= options.size()) selectedIndex = 0;
if (selectedIndex >= static_cast<int>(options.size())) selectedIndex = 0;
}
if (IsKeyPressed(KEY_UP)) {
selectedIndex--;
if (selectedIndex < 0) selectedIndex = options.size() - 1;
if (selectedIndex < 0) selectedIndex = static_cast<int>(options.size()) - 1;
}
// Handle Selection
if (IsKeyPressed(KEY_ENTER)) {
if (options[selectedIndex] == "Start Game") {
currentState = GameState::Playing;
}
else if (options[selectedIndex] == "Quit") {
currentState = GameState::GameOver;
}
return selectedIndex;
}
return -1;
}
void Menu::Draw() {
@@ -30,8 +26,10 @@ void Menu::Draw() {
DrawText(title.c_str(), screenWidth / 2 - MeasureText(title.c_str(), 60) / 2, screenHeight / 4, 60, WHITE);
// Draw Options
for (int i = 0; i < options.size(); i++) {
for (int i = 0; i < static_cast<int>(options.size()); i++) {
Color textColor = (i == selectedIndex) ? YELLOW : WHITE;
DrawText(options[i].c_str(), screenWidth / 2 - MeasureText(options[i].c_str(), 40) / 2, screenHeight / 2 + (i * 60), 40, textColor);
}
// Draw Control keys
}