From d0c8bfa4918b7dfe422a5c4da8f2ba3d02ca360e Mon Sep 17 00:00:00 2001 From: Mathias Wagner Date: Mon, 25 Nov 2024 21:59:07 +0100 Subject: [PATCH] Make codebase modular --- Main.cpp | 9 -- Helper.cpp => src/Renderer.cpp | 3 +- Helper.h => src/Renderer.h | 3 + src/entities/Opponent.cpp | 8 + src/entities/Opponent.h | 8 + src/entities/Player.cpp | 13 ++ src/entities/Player.h | 11 ++ src/main.cpp | 56 +++++++ src/states/GameState.h | 32 ++++ Game.cpp => src/states/InGameState.cpp | 205 +++++++++++-------------- Game.h => src/states/InGameState.h | 40 ++--- src/states/MainMenuState.cpp | 26 ++++ src/states/MainMenuState.h | 11 ++ 13 files changed, 268 insertions(+), 157 deletions(-) delete mode 100644 Main.cpp rename Helper.cpp => src/Renderer.cpp (97%) rename Helper.h => src/Renderer.h (85%) create mode 100644 src/entities/Opponent.cpp create mode 100644 src/entities/Opponent.h create mode 100644 src/entities/Player.cpp create mode 100644 src/entities/Player.h create mode 100644 src/main.cpp create mode 100644 src/states/GameState.h rename Game.cpp => src/states/InGameState.cpp (59%) rename Game.h => src/states/InGameState.h (59%) create mode 100644 src/states/MainMenuState.cpp create mode 100644 src/states/MainMenuState.h diff --git a/Main.cpp b/Main.cpp deleted file mode 100644 index a637039..0000000 --- a/Main.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "Game.h" - -using namespace std; - -int main() { - Game game{}; - - game.run(); -} \ No newline at end of file diff --git a/Helper.cpp b/src/Renderer.cpp similarity index 97% rename from Helper.cpp rename to src/Renderer.cpp index 7fb7f3c..41bd87e 100644 --- a/Helper.cpp +++ b/src/Renderer.cpp @@ -1,4 +1,5 @@ -#include "Helper.h" +#include "Renderer.h" + void drawTexture(SDL_Renderer *renderer, SDL_Texture *texture, int x, int y, int w, int h) { SDL_Rect src; src.x = 0; diff --git a/Helper.h b/src/Renderer.h similarity index 85% rename from Helper.h rename to src/Renderer.h index 2c5c16c..ca12e04 100644 --- a/Helper.h +++ b/src/Renderer.h @@ -4,6 +4,9 @@ #include #include +#define WINDOW_WIDTH 700 +#define WINDOW_HEIGHT 500 + void drawTexture(SDL_Renderer *renderer, SDL_Texture *texture, int x, int y, int w, int h); void drawText(SDL_Renderer *renderer, TTF_Font *font, const std::string text, int x, int y); diff --git a/src/entities/Opponent.cpp b/src/entities/Opponent.cpp new file mode 100644 index 0000000..4873365 --- /dev/null +++ b/src/entities/Opponent.cpp @@ -0,0 +1,8 @@ +#include "Opponent.h" + +void Opponent::move(int timeMultiplier) { + if (this->x < this->targetX) this->x += timeMultiplier; + if (this->x > this->targetX) this->x -= timeMultiplier; + if (this->y < this->targetY) this->y += timeMultiplier; + if (this->y > this->targetY) this->y -= timeMultiplier; +} \ No newline at end of file diff --git a/src/entities/Opponent.h b/src/entities/Opponent.h new file mode 100644 index 0000000..fa805d4 --- /dev/null +++ b/src/entities/Opponent.h @@ -0,0 +1,8 @@ +#include "Player.h" + +class Opponent : public Player { +public: + int targetX = 0; + int targetY = 0; + void move(int multiplier); +}; diff --git a/src/entities/Player.cpp b/src/entities/Player.cpp new file mode 100644 index 0000000..3832697 --- /dev/null +++ b/src/entities/Player.cpp @@ -0,0 +1,13 @@ +#include "Player.h" + +void Player::moveX(int dx) { + if (x + dx < 0 || x + dx > WINDOW_WIDTH - 80) return; + if (y < 40 && this->x + dx < 30 * 3 + 15) return; + x += dx; +} + +void Player::moveY(int dy) { + if (y + dy < 0 || y + dy > WINDOW_HEIGHT - 96) return; + if (y + dy < 40 && this->x < 30 * 3 + 15) return; + y += dy; +} diff --git a/src/entities/Player.h b/src/entities/Player.h new file mode 100644 index 0000000..665631d --- /dev/null +++ b/src/entities/Player.h @@ -0,0 +1,11 @@ +#include "../Renderer.h" + +class Player { +public: + int x = 40; + int y = 40; + int unsigned health = 3; + + void moveX(int dx); + void moveY(int dy); +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..eb9927c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,56 @@ +#include "states/GameState.h" +#include "states/MainMenuState.h" +#include "states/InGameState.h" +#include "Renderer.h" +#include +#include +#include + +int main() { + TTF_Init(); + + SDL_Window* window = SDL_CreateWindow("Obstacle Game", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN); + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + + std::unordered_map> states; + states["main_menu"] = std::make_shared(); + states["game"] = std::make_shared(); + + std::shared_ptr currentState = states["main_menu"]; + + bool running = true; + while (running) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + running = false; + } + currentState->handleEvents(event); + } + + currentState->update(); + currentState->render(renderer); + + if (!currentState->getNextState().empty()) { + std::string nextStateName = currentState->getNextState(); + if (nextStateName == "exit") { + running = false; + } else if (states.find(nextStateName) != states.end()) { + currentState = states[nextStateName]; + currentState->init(); + } + currentState->clearNextState(); + } + + SDL_Delay(16); + } + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + TTF_Quit(); + + return 0; +} diff --git a/src/states/GameState.h b/src/states/GameState.h new file mode 100644 index 0000000..267d357 --- /dev/null +++ b/src/states/GameState.h @@ -0,0 +1,32 @@ +#ifndef GAME_STATE_H +#define GAME_STATE_H + +#include +#include + +class GameState { +protected: + std::string nextStateName; + +public: + virtual ~GameState() = default; + + virtual void init() = 0; + virtual void handleEvents(SDL_Event& event) = 0; + virtual void update() = 0; + virtual void render(SDL_Renderer* renderer) = 0; + + void changeRoom(const std::string& stateName) { + nextStateName = stateName; + } + + std::string getNextState() const { + return nextStateName; + } + + void clearNextState() { + nextStateName.clear(); + } +}; + +#endif diff --git a/Game.cpp b/src/states/InGameState.cpp similarity index 59% rename from Game.cpp rename to src/states/InGameState.cpp index a3a2a72..d338fea 100644 --- a/Game.cpp +++ b/src/states/InGameState.cpp @@ -1,14 +1,9 @@ -#include "Game.h" -#include "Helper.h" -#include -#include - -#define WINDOW_WIDTH 700 -#define WINDOW_HEIGHT 500 +#include "InGameState.h" +#include bool keys[4] = {false, false, false, false}; -void Game::spawnItem(Type type) { +void InGameState::spawnItem(Type type) { Item item{}; item.type = type; item.x = rand() % WINDOW_WIDTH; @@ -17,26 +12,17 @@ void Game::spawnItem(Type type) { items.push_back(item); } -void Opponent::move(int timeMultiplier) { - if (this->x < this->targetX) this->x += timeMultiplier; - if (this->x > this->targetX) this->x -= timeMultiplier; - if (this->y < this->targetY) this->y += timeMultiplier; - if (this->y > this->targetY) this->y -= timeMultiplier; +void InGameState::renderPlayer(SDL_Renderer* renderer, SDL_Texture* playerTexture, Player p) const { + drawTexture(renderer, playerTexture, p.x, p.y, 80, 96); + + if (showHitboxes) { + SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE); + SDL_Rect hitbox = {p.x, p.y, 80, 96}; + SDL_RenderDrawRect(renderer, &hitbox); + } } -void Player::moveX(int dx) { - if (x + dx < 0 || x + dx > WINDOW_WIDTH - 80) return; - if (y < 40 && this->x + dx < 30 * 3 + 15) return; - x += dx; -} - -void Player::moveY(int dy) { - if (y + dy < 0 || y + dy > WINDOW_HEIGHT - 96) return; - if (y + dy < 40 && this->x < 30 * 3 + 15) return; - y += dy; -} - -void Game::checkCollision() { +void InGameState::checkCollision() { for (int i = 0; i < opponents.size(); i++) { Opponent o = opponents[i]; if (o.x == o.targetX && o.y == o.targetY) { @@ -73,27 +59,7 @@ void Game::checkCollision() { } } -void Game::renderHUD(SDL_Renderer* renderer, SDL_Texture* heart) const { - SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); - SDL_Rect bgRect = {0, 0, 30 * 3 + 15, 40}; - SDL_RenderFillRect(renderer, &bgRect); - - for (int i = 0; i < this->player.health; i++) { - drawTexture(renderer, heart, 10 + i * 30, 10, 24, 24); - } -} - -void Game::renderPlayer(SDL_Renderer* renderer, SDL_Texture* playerTexture, Player p) const { - drawTexture(renderer, playerTexture, p.x, p.y, 80, 96); - - if (showHitboxes) { - SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE); - SDL_Rect hitbox = {p.x, p.y, 80, 96}; - SDL_RenderDrawRect(renderer, &hitbox); - } -} - -void Game::renderItem(SDL_Renderer* renderer, Item i) const { +void InGameState::renderItem(SDL_Renderer* renderer, Item i) const { SDL_Texture* itemTexture = IMG_LoadTexture(renderer, i.type == Type::HEALTH ? "./res/heart.png" : "./res/damage.png"); switch (i.type) { case Type::HEALTH: @@ -115,7 +81,7 @@ void Game::renderItem(SDL_Renderer* renderer, Item i) const { } } -void Game::createOpponent() { +void InGameState::createOpponent() { Opponent o; int edge = rand() % 4; @@ -149,7 +115,7 @@ void Game::createOpponent() { opponents.push_back(o); } -void Game::renderOpponent(SDL_Renderer* renderer, SDL_Texture* opponentTexture, Opponent o) const { +void InGameState::renderOpponent(SDL_Renderer* renderer, SDL_Texture* opponentTexture, Opponent o) const { drawTexture(renderer, opponentTexture, o.x, o.y, 80, 96); if (showHitboxes) { @@ -159,12 +125,83 @@ void Game::renderOpponent(SDL_Renderer* renderer, SDL_Texture* opponentTexture, } } -void Game::render(SDL_Renderer* renderer, SDL_Texture* bg) { +void InGameState::renderHUD(SDL_Renderer* renderer, SDL_Texture* heart) const { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_Rect bgRect = {0, 0, 30 * 3 + 15, 40}; + SDL_RenderFillRect(renderer, &bgRect); + + for (int i = 0; i < this->player.health; i++) { + drawTexture(renderer, heart, 10 + i * 30, 10, 24, 24); + } +} + +void InGameState::init() { + startTime = time(nullptr); +} + + +void InGameState::handleEvents(SDL_Event& event) { + if (event.type == SDL_KEYDOWN) { + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + std::cout << "Switching to main menu\n"; + this->changeRoom("main_menu"); + break; + default: + break; + } + } + + if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) { + bool isPressed = (event.type == SDL_KEYDOWN); + switch (event.key.keysym.sym) { + case SDLK_w: + keys[0] = isPressed; + break; + case SDLK_a: + keys[1] = isPressed; + break; + case SDLK_s: + keys[2] = isPressed; + break; + case SDLK_d: + keys[3] = isPressed; + break; + case SDLK_h: + if (isPressed) showHitboxes = !showHitboxes; + break; + } + } +} + +void InGameState::update() { + + checkCollision(); + + if (rand() % 100 == 0) createOpponent(); + + if (time(nullptr) - startTime > 5) { + startTime = time(nullptr); + spawnItem(Type::HEALTH); + } + + for (Opponent& o : opponents) { + o.move(10); + } + + if (keys[0]) player.moveY(-20); + if (keys[1]) player.moveX(-20); + if (keys[2]) player.moveY(20); + if (keys[3]) player.moveX(20); +} + +void InGameState::render(SDL_Renderer* renderer) { SDL_RenderClear(renderer); SDL_SetRenderDrawColor(renderer, 35, 39, 42, SDL_ALPHA_OPAQUE); - drawTexture(renderer, bg, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + SDL_Texture* background = IMG_LoadTexture(renderer, "./res/background.png"); + drawTexture(renderer, background, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); SDL_Texture* playerTexture = IMG_LoadTexture(renderer, "./res/player.png"); renderPlayer(renderer, playerTexture, this->player); @@ -191,69 +228,3 @@ void Game::render(SDL_Renderer* renderer, SDL_Texture* bg) { SDL_RenderPresent(renderer); } - -void Game::run() { - SDL_Window* window = SDL_CreateWindow("Obstacle Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN); - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE); - - SDL_Texture* background = IMG_LoadTexture(renderer, "./res/background.png"); - - int startTime = time(nullptr); - - TTF_Init(); - - bool gameOpen = true; - while (gameOpen) { - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) gameOpen = false; - - if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) { - bool isPressed = (event.type == SDL_KEYDOWN); - switch (event.key.keysym.sym) { - case SDLK_w: - keys[0] = isPressed; - break; - case SDLK_a: - keys[1] = isPressed; - break; - case SDLK_s: - keys[2] = isPressed; - break; - case SDLK_d: - keys[3] = isPressed; - break; - case SDLK_h: - if (isPressed) showHitboxes = !showHitboxes; - break; - } - } - } - - if (keys[0]) player.moveY(-20); - if (keys[1]) player.moveX(-20); - if (keys[2]) player.moveY(20); - if (keys[3]) player.moveX(20); - - checkCollision(); - - if (rand() % 100 == 0) createOpponent(); - - if (time(nullptr) - startTime > 5) { - startTime = time(nullptr); - spawnItem(Type::HEALTH); - } - - for (Opponent& o : opponents) { - o.move(10); - } - - render(renderer, background); - SDL_Delay(16); - } - - SDL_DestroyTexture(background); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); -} diff --git a/Game.h b/src/states/InGameState.h similarity index 59% rename from Game.h rename to src/states/InGameState.h index 05081dd..b5a9bed 100644 --- a/Game.h +++ b/src/states/InGameState.h @@ -1,22 +1,8 @@ +#include "../entities/Opponent.h" +#include "GameState.h" #include -#include - -#ifndef CPP_LEARNING_GAME_H -#define CPP_LEARNING_GAME_H - -#include #include -class Player { -public: - int x = 40; - int y = 40; - int unsigned health = 3; - - void moveX(int dx); - void moveY(int dy); -}; - enum class Type { HEALTH, DAMAGE @@ -29,33 +15,27 @@ struct Item { Type type; }; -class Opponent : public Player { -public: - int targetX = 0; - int targetY = 0; - void move(int multiplier); -}; - -class Game { +class InGameState final : public GameState { Player player; std::vector opponents; std::vector items; + int startTime = 0; bool showHitboxes = false; void renderPlayer(SDL_Renderer* renderer, SDL_Texture* player, Player p) const; public: + void init() override; + void handleEvents(SDL_Event& event) override; + void update() override; + void render(SDL_Renderer* renderer) override; + void spawnItem(Type type); void createOpponent(); void checkCollision(); void renderHUD(SDL_Renderer* renderer, SDL_Texture* heart) const; void renderItem(SDL_Renderer* renderer, Item i) const; - void render(SDL_Renderer* renderer, SDL_Texture* bg); - void run(); - void renderOpponent(SDL_Renderer* renderer, SDL_Texture* opponentTexture, Opponent o) const; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/states/MainMenuState.cpp b/src/states/MainMenuState.cpp new file mode 100644 index 0000000..859fd90 --- /dev/null +++ b/src/states/MainMenuState.cpp @@ -0,0 +1,26 @@ +#include "MainMenuState.h" +#include + +void MainMenuState::handleEvents(SDL_Event& event) { + if (event.type == SDL_KEYDOWN) { + switch (event.key.keysym.sym) { + case SDLK_RETURN: + std::cout << "Switching to game state\n"; + this->changeRoom("game"); + break; + case SDLK_ESCAPE: + std::cout << "Exiting game\n"; + this->changeRoom("exit"); + break; + default: + break; + } + } +} + +void MainMenuState::render(SDL_Renderer* renderer) { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer); + + SDL_RenderPresent(renderer); +} diff --git a/src/states/MainMenuState.h b/src/states/MainMenuState.h new file mode 100644 index 0000000..d11caa4 --- /dev/null +++ b/src/states/MainMenuState.h @@ -0,0 +1,11 @@ +#include "GameState.h" +#include +#include + +class MainMenuState final : public GameState { +public: + void init() override; + void handleEvents(SDL_Event& event) override; + void update() override; + void render(SDL_Renderer* renderer) override; +};