Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
name: CMake Build

on:
push:
branches: [ develop ]
pull_request:
branches: [ main, develop ]
branches: [ main ]

permissions:
contents: read
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# Memory in SDL3

![Screenshot of the game](in_game.png)

This is a simple Memory game with creepy themes.
This is a simple Memory game with scary themes, developed with Simple DirectMedia Layer (SDL3).

I am building this project to improve my system design and architecture skills.

It is developed with Simple DirectMedia Layer (SDL3).
And also for the fun of it, of course.

I deliberately avoided game engines for this project, allowing me implement everything that I want myself.
***
All used assets are made by me.

## Playing the game
Expand Down
2 changes: 0 additions & 2 deletions include/core/Game.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include <memory>

class AssetManager;
class GridLayout;
class SceneManager;
class SoundSystem;

Expand All @@ -33,7 +32,6 @@ class Game

// Systems
std::unique_ptr<AssetManager> m_assetManager;
std::unique_ptr<GridLayout> m_grid;
std::unique_ptr<SoundSystem> m_soundSystem;
std::unique_ptr<SceneManager> m_sceneManager;

Expand Down
13 changes: 4 additions & 9 deletions include/core/GameContext.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#ifndef MEMORYSDL_GAMECONTEXT_H
#define MEMORYSDL_GAMECONTEXT_H
#include "ui/GridLayout.h"
#pragma once
#include "systems/AssetManager.h"
#include "systems/SoundSystem.h"

Expand All @@ -10,20 +8,17 @@ struct GameContext
{
SDL_Renderer *renderer;
AssetManager *assetManager;
GridLayout *grid;
SoundSystem *soundSystem;

int windowWidth;
int windowHeight;
int texWidth;
int texHeight;

GameContext(SDL_Renderer* r, AssetManager *am, GridLayout *gl, SoundSystem *ss,
GameContext(SDL_Renderer* r, AssetManager *am, SoundSystem *ss,
const int wWidth, const int wHeight, const int tw, const int th)
: renderer(r), assetManager(am), grid(gl), soundSystem(ss), windowWidth(wWidth),
: renderer(r), assetManager(am), soundSystem(ss), windowWidth(wWidth),
windowHeight(wHeight), texWidth(tw),
texHeight(th)
{}
};

#endif //MEMORYSDL_GAMECONTEXT_H
};
38 changes: 38 additions & 0 deletions include/game/Card.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once
#include "game/CardData.h"

#include "SDL3/SDL_rect.h"
#include "SDL3/SDL_render.h"

class Card
{
public:
enum class CardState { FaceDown, FaceUp, Matched };

Card(const CardData& data, const SDL_FRect& rect, SDL_Texture* front, SDL_Texture* back)
: m_data(data), m_rect(rect), m_front(front), m_back(back)
{}

void Render(SDL_Renderer* renderer) const;
void HandleEvent(const SDL_Event& event);

bool TryFlip();
void SetMatched();
void FlipDown();

[[nodiscard]] bool IsFaceDown() const { return m_state == CardState::FaceDown; }
[[nodiscard]] bool IsFaceUp() const { return m_state == CardState::FaceUp; }

//[[nodiscard]] const char *GetTexName() const { return m_data.frontKey.c_str(); } // NOTE: for testing
[[nodiscard]] int GetPairId() const { return m_data.pairId; }
[[nodiscard]] SDL_FRect GetRect() const { return m_rect; }

private:
CardData m_data;
SDL_FRect m_rect;

SDL_Texture* m_front;
SDL_Texture* m_back;

CardState m_state = CardState::FaceDown;
};
10 changes: 10 additions & 0 deletions include/game/CardData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once
#include <string>

struct CardData
{
int uniqueId = -1;
int pairId = -1;
std::string frontKey;
float rotation = 0.f;
};
22 changes: 22 additions & 0 deletions include/game/CardDeck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include "game/Card.h"

#include <random>
#include <string>
#include <vector>

class CardDeck
{
public:
CardDeck() : m_rng(std::random_device{}())
{}

void BuildDeck(const std::vector<std::string>& frontKeys);
void ShuffleDeck();

[[nodiscard]] const std::vector<CardData>& GetCards() const { return m_cards; }

private:
std::vector<CardData> m_cards;
std::mt19937 m_rng;
};
35 changes: 12 additions & 23 deletions include/scenes/EndScene.h
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
#ifndef MEMORYSDL_ENDSCENE_H
#define MEMORYSDL_ENDSCENE_H
#pragma once
#include "scenes/Scene.h"

#include "ui/UIElement.h"
#include "scenes/SceneManager.h"

#include <vector>

class EndScene : public Scene
{
public:
explicit EndScene(SceneManager &manager, GameContext &context)
: Scene(context), m_sceneManager(manager)
explicit EndScene(SceneManager &manager, GameContext &context, const bool hasPlayerWon)
: Scene(context), m_sceneManager(manager), m_hasPlayerWon(hasPlayerWon)
{
m_uiPlayButtonRect.x = static_cast<float>(m_context.windowWidth) * 0.15f;
m_uiPlayButtonRect.y = static_cast<float>(m_context.windowHeight) * 0.75f;
m_uiPlayButtonRect.w = static_cast<float>(m_context.texWidth) * 0.5f;
m_uiPlayButtonRect.h = static_cast<float>(m_context.texHeight) * 0.5f;

m_uiQuitButtonRect.x = static_cast<float>(m_context.windowWidth) * 0.75f;
m_uiQuitButtonRect.y = static_cast<float>(m_context.windowHeight) * 0.75f;
m_uiQuitButtonRect.w = static_cast<float>(m_context.texWidth) * 0.5f;
m_uiQuitButtonRect.h = static_cast<float>(m_context.texHeight) * 0.5f;
InitUI();
}

void InitUI();
void HandleEvent(const SDL_Event &event) override;
void Update(float dt) override;
void Render(SDL_Renderer *renderer) override;

private:
SceneManager &m_sceneManager;

bool m_hasPlayerWon;
// UI
const char* m_uiPlayButtonText = "PLAY";
const char* m_uiQuitButtonText = "QUIT";
SDL_FRect m_uiPlayButtonRect{};
SDL_FRect m_uiQuitButtonRect{};
bool uiPlayPressed = false;
bool uiQuitPressed = false;
};

#endif //MEMORYSDL_ENDSCENE_H
std::vector<std::unique_ptr<UIElement>> m_ui;
};
43 changes: 21 additions & 22 deletions include/scenes/GameScene.h
Original file line number Diff line number Diff line change
@@ -1,53 +1,52 @@
#ifndef MEMORYSDL_GAMESCENE_H
#define MEMORYSDL_GAMESCENE_H
#pragma once
#include "game/CardDeck.h"
#include "scenes/Scene.h"
#include "scenes/SceneManager.h"
#include "ui/GridLayout.h"

#include <memory>

#include "ui/UIAttempts.h"
#include "ui/UIElement.h"

constexpr auto MAX_ATTEMPTS = 5;

class GameScene : public Scene
{
public:
explicit GameScene(SceneManager &manager, GameContext &context)
: Scene(context), m_sceneManager(manager), m_gameState(GameState::Paused), m_numOfCardsMatched(0),
: Scene(context), m_sceneManager(manager), m_numOfCardsMatched(0),
m_cardsSelected(CardSelected::NoCard),
m_firstCardIdx(-1),
m_secondCardIdx(-1), m_resolveCardsAtMs(0),
m_attempts(MAX_ATTEMPTS)
m_secondCardIdx(-1), m_attempts(MAX_ATTEMPTS),
m_resolveCardsAtMs(0)
{
constexpr auto size = static_cast<size_t>(MAX_ATTEMPTS);
m_uiHeartRects.resize(size);
for (size_t i = 0; i < size; ++i) {
m_uiHeartRects[i].x = (static_cast<float>(m_context.windowWidth) * 0.5f + static_cast<float>(
m_context.texWidth * i) - static_cast<float>(m_context.texWidth * m_attempts) /
2);
m_uiHeartRects[i].y = static_cast<float>(m_context.windowHeight) * 0.001f;
m_uiHeartRects[i].w = static_cast<float>(m_context.texWidth);
m_uiHeartRects[i].h = static_cast<float>(m_context.texHeight);
}
Init();
}

void Init();
void HandleEvent(const SDL_Event &event) override;
void Update(float dt) override;
void Render(SDL_Renderer *renderer) override;

private:
SceneManager &m_sceneManager;

enum class GameState { Running, Ended, Paused, Starting };
GameState m_gameState;

// State
enum class CardSelected { NoCard, OneCard, TwoCards };
size_t m_numOfCardsMatched;
CardSelected m_cardsSelected;
int m_firstCardIdx;
int m_secondCardIdx;
int m_attempts;

Uint64 m_resolveCardsAtMs;
static constexpr Uint64 m_revealDelayMs = 800;

std::vector<SDL_FRect> m_uiHeartRects;
int m_attempts;
};
std::unique_ptr<CardDeck> m_deck;
std::vector<Card> m_cards;

#endif //MEMORYSDL_GAMESCENE_H
// UI
std::unique_ptr<GridLayout> m_grid;
std::unique_ptr<UIAttempts> m_uiAttempts;
};
7 changes: 2 additions & 5 deletions include/scenes/Scene.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#ifndef MEMORYSDL_SCENE_H
#define MEMORYSDL_SCENE_H
#pragma once
#include "core/GameContext.h"

#include "SDL3/SDL_render.h"
Expand All @@ -21,6 +20,4 @@ class Scene

protected:
GameContext& m_context;
};

#endif //MEMORYSDL_SCENE_H
};
7 changes: 2 additions & 5 deletions include/scenes/SceneManager.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#ifndef MEMORYSDL_SCENEMANAGER_H
#define MEMORYSDL_SCENEMANAGER_H
#pragma once
#include "scenes/Scene.h"

#include <memory>
Expand All @@ -22,6 +21,4 @@ class SceneManager
std::unique_ptr<Scene> m_nextScene;

bool m_quitRequested = false;
};

#endif //MEMORYSDL_SCENEMANAGER_H
};
32 changes: 11 additions & 21 deletions include/scenes/StartScene.h
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
#ifndef MEMORYSDL_STARTSCENE_H
#define MEMORYSDL_STARTSCENE_H
#pragma once
#include "scenes/Scene.h"

class SceneManager;
#include "ui/Button.h"
#include "scenes/SceneManager.h"

#include <memory>
#include <vector>

class StartScene : public Scene
{
public:
explicit StartScene(SceneManager &manager, GameContext& context)
: Scene(context), m_sceneManager(manager)
{
m_uiPlayButtonRect.x = static_cast<float>(m_context.windowWidth) * 0.15f;
m_uiPlayButtonRect.y = static_cast<float>(m_context.windowHeight) * 0.75f;
m_uiPlayButtonRect.w = static_cast<float>(m_context.texWidth) * 0.5f;
m_uiPlayButtonRect.h = static_cast<float>(m_context.texHeight) * 0.5f;

m_uiQuitButtonRect.x = static_cast<float>(m_context.windowWidth) * 0.75f;
m_uiQuitButtonRect.y = static_cast<float>(m_context.windowHeight) * 0.75f;
m_uiQuitButtonRect.w = static_cast<float>(m_context.texWidth) * 0.5f;
m_uiQuitButtonRect.h = static_cast<float>(m_context.texHeight) * 0.5f;
InitUI();
}

void InitUI();

void HandleEvent(const SDL_Event &event) override;
void Update(float dt) override {};
void Render(SDL_Renderer *renderer) override;
Expand All @@ -29,12 +26,5 @@ class StartScene : public Scene
SceneManager &m_sceneManager;

// UI
const char* m_uiPlayButtonText = "PLAY";
const char* m_uiQuitButtonText = "QUIT";
SDL_FRect m_uiPlayButtonRect{};
SDL_FRect m_uiQuitButtonRect{};
bool uiPlayPressed = false;
bool uiQuitPressed = false;
};

#endif //MEMORYSDL_STARTSCENE_H
std::vector<std::unique_ptr<UIElement>> m_ui;
};
14 changes: 0 additions & 14 deletions include/systems/AssetManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,6 @@

class AssetManager
{
//enum class TextureID
//{
// UI_Heart,
// UI_PlayButton,
// UI_QuitButton,
// Card_Back,
// Card_Bat,
// Card_Candle,
// Card_Coffin,
// Card_Dagger,
// Card_Door,
// Card_Skull
//};

public:
explicit AssetManager(SDL_Renderer* renderer);
~AssetManager();
Expand Down
Loading
Loading