Commit b52b77d9 authored by LAFORÊT Nicolas's avatar LAFORÊT Nicolas 🐇
Browse files

Adding Context Renderer & 🚧 Fonts

parent 3a11ce48
......@@ -36,13 +36,17 @@ set(
SOURCES
src/main.cpp
src/application.cpp
src/tetris/tetromino.cpp
src/tetris/grid.cpp
src/tetris/randomGenerator.cpp
src/tetris/input/inputSystem.cpp
src/tetris/game.cpp
src/ui/textureRenderer.cpp
src/ui/gameRenderer.cpp
src/tetris/game.cpp
src/core/gameContext.cpp
)
# Set exexutable outout path to bin/ folder
......@@ -53,9 +57,9 @@ add_executable(${TARGET_NAME} ${SOURCES})
# SDL2 platform dependent config
if (${CMAKE_SYSTEM_NAME} MATCHES Linux)
target_include_directories(${TARGET_NAME} PUBLIC /usr/include/SDL2)
target_link_libraries(${TARGET_NAME} PUBLIC SDL2 SDL2main)
target_link_libraries(${TARGET_NAME} PUBLIC SDL2 SDL2main SDL2_ttf)
else()
target_link_libraries(${TARGET_NAME} PUBLIC SDL2::SDL2 SDL2::SDL2main)
target_link_libraries(${TARGET_NAME} PUBLIC SDL2::SDL2 SDL2::SDL2main SDL2::SDL2_ttf)
endif()
target_compile_definitions(${TARGET_NAME} PUBLIC _USE_MATH_DEFINES)
......
#include "application.h"
#include "core/contextStack.h"
#include "core/gameContext.h"
Application::Application(std::string title, int width, int height,
int resolution, Theme *theme)
: p_game(nullptr), m_resolution(resolution), p_theme(theme) {
Application::Application(std::string title, double framerate, int width,
int height, Theme *theme)
: m_framerate(framerate), b_quit(false), p_theme(theme) {
p_window =
SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN);
p_renderer = SDL_CreateRenderer(p_window, -1, SDL_RENDERER_ACCELERATED);
Init();
}
Application::~Application() {
delete p_input;
delete p_game;
delete p_gameRenderer;
ContextStack::Instance()->Destroy();
SDL_DestroyRenderer(p_renderer);
SDL_DestroyWindow(p_window);
}
void Application::InitGame(int rowCount, int colCount) {
p_game = new Game(rowCount, colCount, p_input);
p_gameRenderer =
new GameRenderer(p_game, p_renderer, colCount * m_resolution,
rowCount * m_resolution, m_resolution, p_theme);
}
void Application::RunGame() {
while (!p_game->IsGameOver()) {
void Application::Run() {
Uint32 currentTime = SDL_GetTicks();
Uint32 previousTime = currentTime;
// Run until b_quit is `true` or ContextStack is empty
while (!b_quit && !ContextStack::Instance()->Empty()) {
SDL_Event e;
if (SDL_PollEvent(&e)) {
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
b_quit = true;
break;
}
}
p_game->Update(1.);
p_gameRenderer->Render(0, 0, 0., SDL_FLIP_NONE);
currentTime = SDL_GetTicks();
m_dTime = currentTime - previousTime;
SDL_RenderPresent(p_renderer);
SDL_Delay(200);
if (m_dTime > 1000. / m_framerate) {
previousTime = currentTime;
if (ContextStack::Instance()->GetActiveContext()->Update(m_dTime) == -1) {
ContextStack::Instance()->Pop();
}
// Display
SDL_RenderPresent(p_renderer);
}
}
delete p_game;
p_game = nullptr;
}
void Application::Init() {
p_input = new InputSystem({
void Application::LaunchGame(int nRow, int nCol, int cellSize) {
InputSystem *input = new InputSystem({
{InputSystem::Action::MOVE_LEFT, SDL_Scancode::SDL_SCANCODE_LEFT},
{InputSystem::Action::MOVE_RIGHT, SDL_Scancode::SDL_SCANCODE_RIGHT},
{InputSystem::Action::MOVE_UP, SDL_Scancode::SDL_SCANCODE_UP},
......@@ -53,4 +54,9 @@ void Application::Init() {
{InputSystem::Action::TOGGLE_PAUSE, SDL_Scancode::SDL_SCANCODE_P},
{InputSystem::Action::QUIT, SDL_Scancode::SDL_SCANCODE_ESCAPE},
});
Game *game = new Game(nRow, nCol, input);
GameRenderer *gameRenderer =
new GameRenderer(game, p_renderer, cellSize, 200, p_theme);
GameContext *gContext = new GameContext(gameRenderer, game);
ContextStack::Instance()->Push(gContext);
}
......@@ -4,29 +4,26 @@
#include <SDL2/SDL.h>
#include <string>
#include "tetris/game.h"
#include "ui/gameRenderer.h"
#include "ui/theme.h"
class Application {
private:
SDL_Window *p_window;
SDL_Renderer *p_renderer;
GameRenderer *p_gameRenderer;
Theme *p_theme;
Game *p_game;
InputSystem *p_input;
int m_resolution;
double m_framerate = 60.;
double m_dTime = 0.;
bool b_quit;
public:
Application(std::string title, int width, int height, int resolution,
Application(std::string title, double framerate, int width, int height,
Theme *theme);
~Application();
void InitGame(int rowCount, int colCount);
void RunGame();
void Run();
private:
void Init();
void LaunchGame(int nRow, int nCol, int cellSize);
};
#endif
\ No newline at end of file
#ifndef _CONTEXT_H_
#define _CONTEXT_H_
#include "../ui/textureRenderer.h"
class Context {
public:
TextureRenderer *p_renderer;
public:
Context(TextureRenderer *renderer) : p_renderer(renderer) {}
virtual ~Context() {
delete p_renderer;
}
virtual int Update(double deltaTime) {
p_renderer->Render(0, 0, 0., SDL_FLIP_NONE);
return 0;
}
};
#endif
\ No newline at end of file
#ifndef _CONTEXTSTACK_H_
#define _CONTEXTSTACK_H_
#include <stack>
#include "context.h"
class ContextStack {
/* SINGLETON */
public:
ContextStack(const ContextStack &) = delete;
ContextStack &operator=(const ContextStack &) = delete;
static ContextStack *Instance() {
if (!m_instance) {
m_instance = new ContextStack();
}
return m_instance;
}
private:
ContextStack() {}
static ContextStack *m_instance;
std::stack<Context *> m_contextStack;
public:
Context *GetActiveContext() {
return m_contextStack.top();
}
void Push(Context *context) {
m_contextStack.push(context);
}
void Pop() {
Context *context = m_contextStack.top();
m_contextStack.pop();
delete context;
}
bool Empty() {
return m_contextStack.empty();
}
void Destroy() {
while (!m_contextStack.empty())
Pop();
}
};
ContextStack *ContextStack::m_instance = nullptr;
#endif
\ No newline at end of file
#include "gameContext.h"
GameContext::GameContext(GameRenderer *gameRenderer, Game *game)
: Context(gameRenderer), p_game(game) {}
GameContext::~GameContext() {
delete p_game;
}
int GameContext::Update(double deltaTime) {
if (p_game->IsGameOver())
return -1;
p_game->Update();
p_renderer->Render(0, 0, 0., SDL_FLIP_NONE);
return 0;
}
#ifndef _GAMECONTEXT_H_
#define _GAMECONTEXT_H_
#include "../tetris/game.h"
#include "../ui/gameRenderer.h"
#include "context.h"
class GameContext : public Context {
private:
Theme *p_theme;
Game *p_game;
public:
GameContext(GameRenderer *gameRenderer, Game *game);
~GameContext() override;
int Update(double deltaTime) override;
};
#endif
\ No newline at end of file
#include "application.h"
#include <SDL2/SDL_ttf.h>
#include <iostream>
int main(int argc, char const *argv[]) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
return 1;
}
if (TTF_Init() < 0) {
std::cout << "Couldn't initialize TTF lib: " << TTF_GetError() << std::endl;
return 1;
}
Theme theme = {
.bg = {.r = 0x00, .g = 0x00, .b = 0x00, .a = 0xFF},
.gLines = {.r = 0x7F, .g = 0x7F, .b = 0x7F, .a = 0xFF},
......@@ -15,9 +26,12 @@ int main(int argc, char const *argv[]) {
{Tetromino::Cell::S, {.r = 0x00, .g = 0xFF, .b = 0x00, .a = 0xFF}},
}};
/* APPICATION TESTS */
Application app("Tetris", 400, 800, 40, &theme);
app.InitGame(20, 10);
app.RunGame();
Application *app = new Application("Tetris", 60., 600, 800, &theme);
app->LaunchGame(20, 10, 40);
app->Run();
delete app;
SDL_Quit();
return 0;
}
......@@ -11,6 +11,7 @@ Game::~Game() {
delete p_currentTetromino;
delete p_heldTetromino;
delete p_nextTetromino;
delete m_input;
}
bool Game::IsGameOver() {
......@@ -21,17 +22,32 @@ bool Game::IsPaused() {
return b_pause;
}
void Game::Update(double deltaTime) {
void Game::Update() {
if (IsGameOver() || IsPaused())
return;
// TODO: Handle Timing / Falling of current Tetromino
IncrementCounters();
ApplyGravity();
HandleInput();
uint removedLines = m_grid.RemoveLines();
// m_scoring->ScoreLines(removedLines);
}
void Game::ApplyGravity() {
if (ShouldDrop()) {
ResetDropCounter();
if (m_grid.TryToMoveOrPlace(p_currentTetromino, {1, 0})) {
GenerateNewTetromino();
}
}
}
void Game::HandleInput() {
if (!CanMove())
return;
m_input->GetInput();
ResetMoveCounter();
if (m_input->IsPressed(InputSystem::Action::QUIT)) {
b_gameOver = true;
return;
......@@ -57,7 +73,8 @@ void Game::HandleInput() {
p_currentTetromino->Move({0, 1});
}
}
if (true || m_input->IsPressed(InputSystem::Action::MOVE_DOWN)) {
if (m_input->IsPressed(InputSystem::Action::MOVE_DOWN)) {
ResetDropCounter();
if (m_grid.TryToMoveOrPlace(p_currentTetromino, {1, 0})) {
GenerateNewTetromino();
}
......@@ -65,6 +82,7 @@ void Game::HandleInput() {
if (m_input->IsPressed(InputSystem::Action::MOVE_UP)) {
m_grid.TryToRotate(p_currentTetromino);
}
m_grid.Print();
}
......
......@@ -21,11 +21,16 @@ private:
bool b_gameOver = false;
bool b_pause = false;
uint m_moveCounter = 0;
uint m_dropCounter = 0;
const uint MOVE_SPEED = 6;
public:
Game(uint height, uint width, InputSystem *input);
~Game();
void Update(double deltaTime);
void Update();
bool IsGameOver();
bool IsPaused();
......@@ -35,6 +40,10 @@ public:
void SetPause(bool value) {
b_pause = value;
}
void IncrementCounters() {
m_moveCounter++;
m_dropCounter++;
}
Tetromino *GetCurrentTetromino() {
return p_currentTetromino;
......@@ -54,6 +63,20 @@ private:
void HandleInput();
void GenerateNewTetromino();
void HoldCurrentTetromino();
void ApplyGravity();
bool ShouldDrop() {
return m_dropCounter > 24 /*m_scoring->GetDropRate()*/;
}
bool CanMove() {
return m_moveCounter > MOVE_SPEED;
}
void ResetDropCounter() {
m_dropCounter = 0;
}
void ResetMoveCounter() {
m_moveCounter = 0;
}
};
#endif
\ No newline at end of file
......@@ -162,6 +162,7 @@ void Grid::Init() {
// TODO: Remove
void Grid::Print() {
return;
std::cout << "Grid: " << m_height << " rows x " << m_width << " cols"
<< std::endl;
for (int row = 0; row < m_height; row++) {
......
......@@ -5,7 +5,9 @@ InputSystem::InputSystem(std::unordered_map<Action, SDL_Scancode> controlMap)
p_keys = nullptr;
}
InputSystem::~InputSystem() {}
InputSystem::~InputSystem() {
m_controlMap.clear();
}
void InputSystem::GetInput() {
p_keys = SDL_GetKeyboardState(NULL);
......
......@@ -13,9 +13,9 @@ RandomGenerator::RandomGenerator(uint seed) {
RandomGenerator::~RandomGenerator() {}
uint RandomGenerator::Generate(uint min, uint max) {
return (rand() % (max - min)) + min;
return (rand() % ((max + 1) - min)) + min;
}
uint RandomGenerator::Generate(uint max) {
return rand() % max;
return rand() % (max + 1);
}
......@@ -87,10 +87,10 @@ std::array<bool, 4 * 4> Tetromino::GetShapeData(Tetromino::Shape shape) {
case Tetromino::Shape::O:
return {
0, 0, 0, 0, //
0, 1, 1, 0, //
0, 1, 1, 0, //
0, 0, 0, 0, //
0, 0, 0, 0, //
};
case Tetromino::Shape::T:
......
#include "gameRenderer.h"
GameRenderer::GameRenderer(Game *game, SDL_Renderer *renderer, int width,
int height, int resolution, Theme *theme)
: TextureRenderer(renderer, width, height), p_game(game),
m_resolution(resolution), p_theme(theme) {
GameRenderer::GameRenderer(Game *game, SDL_Renderer *renderer, int cellSize,
int sideBarSize, Theme *theme)
: TextureRenderer(renderer,
game->GetGrid().GetWidth() * cellSize + sideBarSize,
game->GetGrid().GetHeight() * cellSize),
p_game(game), m_cellSize(cellSize), m_sideBarSize(sideBarSize),
p_theme(theme) {
SetBackgroundColor(theme->bg);
p_font = TTF_OpenFont("Inconsolata-Bold.ttf", 24);
if (p_font == nullptr)
p_font = TTF_OpenFont("./assets/Inconsolata-Bold.ttf", 24);
if (p_font == nullptr)
p_font = TTF_OpenFont("../assets/Inconsolata-Bold.ttf", 24);
if (p_font == nullptr)
p_font = TTF_OpenFont("../../assets/Inconsolata-Bold.ttf", 24);
if (p_font == nullptr)
p_font = TTF_OpenFont("Sans.ttf", 24);
}
void GameRenderer::Draw() {
......@@ -14,26 +26,28 @@ void GameRenderer::Draw() {
DrawGridLines();
DrawGridCells();
DrawActiveTetromino();
DrawNextTetromino();
DrawHeldTetromino();
}
void GameRenderer::DrawGridLines() {
Grid &grid = p_game->GetGrid();
uint colCount = grid.GetWidth();
uint rowCount = grid.GetHeight();
int width = (int)colCount * m_resolution;
int height = (int)rowCount * m_resolution;
int width = (int)colCount * m_cellSize;
int height = (int)rowCount * m_cellSize;
SDL_Rect rect = {.x = 0, .y = 0, .w = width, .h = height};
// Draw horizontal lines
ChangeDrawColor(p_theme->gLines);
for (int i = 1; i < rowCount; i++) {
SDL_RenderDrawLine(p_renderer, 0, (i * m_resolution), width,
(i * m_resolution));
SDL_RenderDrawLine(p_renderer, 0, (i * m_cellSize), width,
(i * m_cellSize));
}
// Draw vertical lines
ChangeDrawColor(p_theme->gLines);
for (int i = 1; i < colCount; i++) {
SDL_RenderDrawLine(p_renderer, (i * m_resolution), 0, (i * m_resolution),
SDL_RenderDrawLine(p_renderer, (i * m_cellSize), 0, (i * m_cellSize),
height);
}
// Draw grid border
......@@ -51,10 +65,10 @@ void GameRenderer::DrawGridCells() {
Tetromino::Cell cell = grid.At(row, col);
if (cell != Tetromino::Cell::Empty) {
SDL_Rect rect = {.x = col * m_resolution,
.y = row * m_resolution,
.w = m_resolution,
.h = m_resolution};
SDL_Rect rect = {.x = col * m_cellSize,
.y = row * m_cellSize,
.w = m_cellSize,
.h = m_cellSize};
ChangeDrawColor(p_theme->cells[cell]);
SDL_RenderFillRect(p_renderer, &rect);
}
......@@ -70,10 +84,69 @@ void GameRenderer::DrawActiveTetromino() {
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
if (tetromino->IsSolid({row, col})) {
SDL_Rect rect = {.x = (pos.y + col) * m_resolution,
.y = (pos.x + row) * m_resolution,
.w = m_resolution,
.h = m_resolution};
SDL_Rect rect = {.x = (pos.y + col) * m_cellSize,
.y = (pos.x + row) * m_cellSize,
.w = m_cellSize,
.h = m_cellSize};
SDL_RenderFillRect(p_renderer, &rect);
}
}
}
}
void GameRenderer::DrawNextTetromino() {
int nCol = (int)p_game->GetGrid().GetWidth();
int padding = 20;
int blockPadding = 60;
int cellSize = 20;
if (p_font != nullptr)
DisplayText("NEXT", p_font, p_theme->gLines, (nCol * m_cellSize) + padding,
padding);
Tetromino *tetromino = p_game->GetNextTetromino();
if (tetromino == nullptr)
return;
ChangeDrawColor(p_theme->cells[tetromino->GetShape()]);
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
if (tetromino->IsSolid({row, col})) {
SDL_Rect rect = {.x = ((nCol * m_cellSize) + (col * cellSize)) +
blockPadding,
.y = (row * cellSize) + blockPadding,
.w = cellSize,
.h = cellSize};
SDL_RenderFillRect(p_renderer, &rect);
}
}
}
}
void GameRenderer::DrawHeldTetromino() {
int nCol = (int)p_game->GetGrid().GetWidth();
int padding = 20;
int paddinTop = 160;
int blockPadding = 60;
int cellSize = 20;
if (p_font != nullptr)
DisplayText("HELD", p_font, p_theme->gLines, (nCol * m_cellSize) + padding,
padding + paddinTop);
Tetromino *tetromino = p_game->GetHeldTetromino();
if (tetromino == nullptr)
return;
ChangeDrawColor(p_theme->cells[tetromino->GetShape()]);
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
if (tetromino->IsSolid({row, col})) {
SDL_Rect rect = {.x = ((nCol * m_cellSize) + (col * cellSize)) +
blockPadding,
.y = (row * cellSize) + blockPadding + paddinTop,
.w = cellSize,
.h = cellSize};
SDL_RenderFillRect(p_renderer, &rect);
}
}
......
......@@ -9,11 +9,13 @@ class GameRenderer : public TextureRenderer {