add sounds, finish main game, add new textures, add tetromino preview, fix some bugs
This commit is contained in:
@@ -1 +1,16 @@
|
|||||||
# Tetris written in SDL2 and C++
|
# Tetris written in SDL2 and C++
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Game Over screen
|
||||||
|
- Add Gamemodes
|
||||||
|
- Gamemode selection screen
|
||||||
|
- Save score
|
||||||
|
- Think of a better scoring system
|
||||||
|
- Save to a file
|
||||||
|
- Maybe Multiplayer
|
||||||
|
- Select 1 Player/2 Player in the title
|
||||||
|
- Peer2Peer or server based?
|
||||||
|
- Server based could also cloud save the savegame
|
||||||
|
- Cleanup code/Refractor
|
||||||
|
- Comment/Document stuff
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 236 B After Width: | Height: | Size: 236 B |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+58
-5
@@ -11,7 +11,7 @@ bool Game::init(const char* title, int w, int h) {
|
|||||||
title,
|
title,
|
||||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||||
w, h,
|
w, h,
|
||||||
SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE
|
SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN
|
||||||
));
|
));
|
||||||
|
|
||||||
if (!window) {
|
if (!window) {
|
||||||
@@ -19,8 +19,6 @@ bool Game::init(const char* title, int w, int h) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetWindowMinimumSize(window.get( ), 800, 650);
|
|
||||||
|
|
||||||
renderer = std::shared_ptr<SDL_Renderer>(
|
renderer = std::shared_ptr<SDL_Renderer>(
|
||||||
SDL_CreateRenderer(window.get( ), -1, SDL_RENDERER_ACCELERATED),
|
SDL_CreateRenderer(window.get( ), -1, SDL_RENDERER_ACCELERATED),
|
||||||
[ ](SDL_Renderer* r) { SDL_DestroyRenderer(r); }
|
[ ](SDL_Renderer* r) { SDL_DestroyRenderer(r); }
|
||||||
@@ -69,15 +67,24 @@ void Game::run( ) {
|
|||||||
gameOver = true;
|
gameOver = true;
|
||||||
Mix_PauseMusic( );
|
Mix_PauseMusic( );
|
||||||
|
|
||||||
|
Mix_Chunk* rotateSound = Mix_LoadWAV("assets/sound_effects/game_over.wav");
|
||||||
|
if (rotateSound == nullptr)
|
||||||
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, rotateSound, 0);
|
||||||
|
}
|
||||||
|
|
||||||
while (gameOver) {
|
while (gameOver) {
|
||||||
if (quit) return;
|
if (quit) return;
|
||||||
inputHandler( );
|
inputHandler( );
|
||||||
gameRenderer->renderGameOver(gameBoard->getScore( ), gameBoard->getLevel( ));
|
render( );
|
||||||
|
gameRenderer->renderGameOver(gameBoard->getScore( ), gameBoard->getLevel( ), gameBoard->getLines( ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::inputHandler( ) {
|
void Game::inputHandler( ) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
Mix_Chunk* movePieceSound = Mix_LoadWAV("assets/sound_effects/move_piece.wav");
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
if (event.type == SDL_QUIT) {
|
if (event.type == SDL_QUIT) {
|
||||||
SDL_Quit( );
|
SDL_Quit( );
|
||||||
@@ -87,10 +94,20 @@ void Game::inputHandler( ) {
|
|||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
case SDLK_a:
|
case SDLK_a:
|
||||||
gameBoard->tryMoveCurrentTetromino(-1, 0);
|
gameBoard->tryMoveCurrentTetromino(-1, 0);
|
||||||
|
if (movePieceSound == nullptr)
|
||||||
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, movePieceSound, 0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
case SDLK_d:
|
case SDLK_d:
|
||||||
gameBoard->tryMoveCurrentTetromino(1, 0);
|
gameBoard->tryMoveCurrentTetromino(1, 0);
|
||||||
|
if (movePieceSound == nullptr)
|
||||||
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, movePieceSound, 0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
case SDLK_s:
|
case SDLK_s:
|
||||||
@@ -102,8 +119,15 @@ void Game::inputHandler( ) {
|
|||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
break;
|
break;
|
||||||
case SDLK_g:
|
case SDLK_g:
|
||||||
if (startSequence)
|
if (startSequence) {
|
||||||
startSequence = false;
|
startSequence = false;
|
||||||
|
Mix_Chunk* menuSound = Mix_LoadWAV("assets/sound_effects/menu.wav");
|
||||||
|
if (menuSound == nullptr)
|
||||||
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, menuSound, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_r:
|
case SDLK_r:
|
||||||
if (isGameOver( ))
|
if (isGameOver( ))
|
||||||
@@ -112,10 +136,38 @@ void Game::inputHandler( ) {
|
|||||||
case SDLK_q:
|
case SDLK_q:
|
||||||
SDL_Quit( );
|
SDL_Quit( );
|
||||||
quit = true;
|
quit = true;
|
||||||
|
case SDLK_EQUALS:
|
||||||
|
SDL_Log("Test %d", Mix_GetMusicVolume(bgm.get( )));
|
||||||
|
Mix_VolumeMusic(Mix_GetMusicVolume(bgm.get( )) + 8);
|
||||||
|
break;
|
||||||
|
case SDLK_MINUS:
|
||||||
|
SDL_Log("Test %d", Mix_GetMusicVolume(bgm.get( )));
|
||||||
|
|
||||||
|
Mix_VolumeMusic(Mix_GetMusicVolume(bgm.get( )) - 8);
|
||||||
|
break;
|
||||||
|
case SDLK_m:
|
||||||
|
if (Mix_PausedMusic( ))
|
||||||
|
Mix_ResumeMusic( );
|
||||||
|
else
|
||||||
|
Mix_PauseMusic( );
|
||||||
|
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
} else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
|
int TARGET_ASPECT_RATIO = 3 / 4;
|
||||||
|
int newHeight = event.window.data2, newWidth = event.window.data1;
|
||||||
|
|
||||||
|
float newAspectRatio = static_cast<float>(newWidth) / newHeight;
|
||||||
|
|
||||||
|
if (newAspectRatio > TARGET_ASPECT_RATIO)
|
||||||
|
newWidth = static_cast<int>(newHeight * TARGET_ASPECT_RATIO);
|
||||||
|
else
|
||||||
|
newHeight = static_cast<int>(newWidth / TARGET_ASPECT_RATIO);
|
||||||
|
|
||||||
|
SDL_SetWindowSize(window.get( ), newWidth, newHeight);
|
||||||
|
|
||||||
handleWindowResize( );
|
handleWindowResize( );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,6 +202,7 @@ void Game::render( ) {
|
|||||||
SDL_RenderClear(renderer.get( ));
|
SDL_RenderClear(renderer.get( ));
|
||||||
|
|
||||||
gameRenderer->renderBoard(*gameBoard);
|
gameRenderer->renderBoard(*gameBoard);
|
||||||
|
gameRenderer->renderTetrominoPreview(gameBoard->getNextTetromino( ));
|
||||||
|
|
||||||
SDL_RenderPresent(renderer.get( ));
|
SDL_RenderPresent(renderer.get( ));
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-6
@@ -2,7 +2,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
GameBoard::GameBoard( )
|
GameBoard::GameBoard( )
|
||||||
: lockedTetrominos(20, vector<int>(10, 0)), lockedColors(20, std::vector<SDL_Color>(10, { 0, 0, 0, 255 })), score(0), level(1), collision(false) {
|
: lockedTetrominos(20, vector<int>(10, 0)), lockedColors(20, std::vector<SDL_Color>(10, { 0, 0, 0, 255 })), score(0), level(0), lines(0), collision(false) {
|
||||||
spawnNewTetromino( );
|
spawnNewTetromino( );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,6 +20,14 @@ void GameBoard::tryRotateCurrentTetromino( ) {
|
|||||||
if (checkCollision(*currentTetromino))
|
if (checkCollision(*currentTetromino))
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
currentTetromino->rotate(*this);
|
currentTetromino->rotate(*this);
|
||||||
|
else {
|
||||||
|
Mix_Chunk* rotateSound = Mix_LoadWAV("assets/sound_effects/rotate_piece.wav");
|
||||||
|
if (rotateSound == nullptr)
|
||||||
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, rotateSound, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameBoard::checkCollision(const Tetromino& tetromino) const {
|
bool GameBoard::checkCollision(const Tetromino& tetromino) const {
|
||||||
@@ -97,9 +105,17 @@ void GameBoard::lockTetromino( ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mix_Chunk* pieceLanded = Mix_LoadWAV("assets/sound_effects/piece_landed.wav");
|
||||||
|
if (pieceLanded == nullptr)
|
||||||
|
SDL_Log("Failed to play sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, pieceLanded, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameBoard::clearLines( ) {
|
void GameBoard::clearLines( ) {
|
||||||
|
int clearedLines = 0;
|
||||||
for (int row = 0; row < height; row++) {
|
for (int row = 0; row < height; row++) {
|
||||||
if (all_of(lockedTetrominos[row].begin( ), lockedTetrominos[row].end( ), [ ](int cell) { return cell != 0; })) {
|
if (all_of(lockedTetrominos[row].begin( ), lockedTetrominos[row].end( ), [ ](int cell) { return cell != 0; })) {
|
||||||
lockedTetrominos.erase(lockedTetrominos.begin( ) + row);
|
lockedTetrominos.erase(lockedTetrominos.begin( ) + row);
|
||||||
@@ -111,20 +127,61 @@ void GameBoard::clearLines( ) {
|
|||||||
score += 100;
|
score += 100;
|
||||||
if (score % 1000 == 0) {
|
if (score % 1000 == 0) {
|
||||||
level++;
|
level++;
|
||||||
|
Mix_Chunk* levelUpSound = Mix_LoadWAV("assets/sound_effects/level_up.wav");
|
||||||
|
if (levelUpSound == nullptr)
|
||||||
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, levelUpSound, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearedLines++;
|
||||||
|
lines++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearedLines >= 4) {
|
||||||
|
Mix_Chunk* clearedLinesSound = Mix_LoadWAV("assets/sound_effects/tetris_line_clear.wav");
|
||||||
|
if (clearedLinesSound == nullptr)
|
||||||
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, clearedLinesSound, 0);
|
||||||
|
}
|
||||||
|
} else if (clearedLines > 0) {
|
||||||
|
Mix_Chunk* clearedLinesSound = Mix_LoadWAV("assets/sound_effects/line_clear.wav");
|
||||||
|
if (clearedLinesSound == nullptr)
|
||||||
|
SDL_Log("Failed to play rotate sound effect: %s", Mix_GetError( ));
|
||||||
|
else {
|
||||||
|
Mix_PlayChannel(-1, clearedLinesSound, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void GameBoard::spawnNewTetromino( ) {
|
void GameBoard::spawnNewTetromino( ) {
|
||||||
|
//Ensure on startup that we have a tetromino
|
||||||
|
if (!nextTetromino) {
|
||||||
random_device dev;
|
random_device dev;
|
||||||
mt19937 rng(dev( ));
|
mt19937 rng(dev( ));
|
||||||
uniform_int_distribution<mt19937::result_type> dist6(0, static_cast<int>(TetrominoShape::COUNT) - 1);
|
uniform_int_distribution<mt19937::result_type> dist6(0, static_cast<int>(TetrominoShape::COUNT) - 1);
|
||||||
TetrominoShape shape = static_cast<TetrominoShape>(dist6(rng));
|
TetrominoShape shape = static_cast<TetrominoShape>(dist6(rng));
|
||||||
currentTetromino = make_unique<Tetromino>(shape);
|
|
||||||
|
nextTetromino = make_shared<Tetromino>(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTetromino = move(nextTetromino);
|
||||||
currentTetromino->move(width / 2 - 1, 0);
|
currentTetromino->move(width / 2 - 1, 0);
|
||||||
|
|
||||||
if (checkCollision(*currentTetromino))
|
// Generate next tetromino
|
||||||
|
random_device dev;
|
||||||
|
mt19937 rng(dev( ));
|
||||||
|
uniform_int_distribution<mt19937::result_type> dist6(0, static_cast<int>(TetrominoShape::COUNT) - 1);
|
||||||
|
TetrominoShape shape = static_cast<TetrominoShape>(dist6(rng));
|
||||||
|
nextTetromino = make_shared<Tetromino>(shape);
|
||||||
|
|
||||||
|
if (checkCollision(*currentTetromino)) {
|
||||||
collision = true;
|
collision = true;
|
||||||
|
lockedTetrominos.clear( );
|
||||||
|
lockedColors.clear( );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameBoard::update( ) {
|
void GameBoard::update( ) {
|
||||||
@@ -132,8 +189,7 @@ void GameBoard::update( ) {
|
|||||||
lockTetromino( );
|
lockTetromino( );
|
||||||
clearLines( );
|
clearLines( );
|
||||||
spawnNewTetromino( );
|
spawnNewTetromino( );
|
||||||
} else
|
}
|
||||||
clearLines( );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameBoard::isValidPosition(const vector<vector<int>>& shape, int x, int y) const {
|
bool GameBoard::isValidPosition(const vector<vector<int>>& shape, int x, int y) const {
|
||||||
@@ -156,6 +212,9 @@ void GameBoard::moveToBottom( ) {
|
|||||||
const bool GameBoard::isCollision( ) const { return collision; }
|
const bool GameBoard::isCollision( ) const { return collision; }
|
||||||
const int GameBoard::getScore( ) const { return score; }
|
const int GameBoard::getScore( ) const { return score; }
|
||||||
const int GameBoard::getLevel( ) const { return level; }
|
const int GameBoard::getLevel( ) const { return level; }
|
||||||
|
const int GameBoard::getLines( ) const { return lines; }
|
||||||
|
const shared_ptr<Tetromino> GameBoard::getNextTetromino( ) const { return nextTetromino; }
|
||||||
|
|
||||||
|
|
||||||
const vector<vector<int>>& GameBoard::getLockedTetrominos( ) const { return lockedTetrominos; }
|
const vector<vector<int>>& GameBoard::getLockedTetrominos( ) const { return lockedTetrominos; }
|
||||||
const vector<vector<SDL_Color>>& GameBoard::getLockedColors( ) const { return lockedColors; }
|
const vector<vector<SDL_Color>>& GameBoard::getLockedColors( ) const { return lockedColors; }
|
||||||
|
|||||||
+9
-1
@@ -1,5 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <SDL2/SDL_mixer.h>
|
||||||
|
}
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -14,12 +18,14 @@ private:
|
|||||||
|
|
||||||
vector<vector<int>> lockedTetrominos;
|
vector<vector<int>> lockedTetrominos;
|
||||||
vector<vector<SDL_Color>> lockedColors;
|
vector<vector<SDL_Color>> lockedColors;
|
||||||
unique_ptr<Tetromino> currentTetromino;
|
shared_ptr<Tetromino> currentTetromino;
|
||||||
|
shared_ptr<Tetromino> nextTetromino;
|
||||||
const int width = 10;
|
const int width = 10;
|
||||||
const int height = 20;
|
const int height = 20;
|
||||||
bool collision;
|
bool collision;
|
||||||
int score;
|
int score;
|
||||||
int level;
|
int level;
|
||||||
|
int lines;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GameBoard( );
|
GameBoard( );
|
||||||
@@ -33,6 +39,8 @@ public:
|
|||||||
const bool isCollision( ) const;
|
const bool isCollision( ) const;
|
||||||
const int getScore( ) const;
|
const int getScore( ) const;
|
||||||
const int getLevel( ) const;
|
const int getLevel( ) const;
|
||||||
|
const int getLines( ) const;
|
||||||
|
const shared_ptr<Tetromino> getNextTetromino( ) const;
|
||||||
|
|
||||||
const vector<vector<int>>& getLockedTetrominos( ) const;
|
const vector<vector<int>>& getLockedTetrominos( ) const;
|
||||||
const vector<vector<SDL_Color>>& getLockedColors( ) const;
|
const vector<vector<SDL_Color>>& getLockedColors( ) const;
|
||||||
|
|||||||
+130
-35
@@ -28,13 +28,68 @@ void Renderer::renderBoard(const GameBoard& gameBoard) {
|
|||||||
drawGrid(gameBoard);
|
drawGrid(gameBoard);
|
||||||
drawLockedBlocks(gameBoard);
|
drawLockedBlocks(gameBoard);
|
||||||
drawTetromino(gameBoard.getCurrentTetromino( ));
|
drawTetromino(gameBoard.getCurrentTetromino( ));
|
||||||
drawScoreboard(gameBoard.getScore( ), gameBoard.getLevel( ));
|
drawScoreboard(gameBoard.getScore( ), gameBoard.getLevel( ), gameBoard.getLines( ));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::drawScoreboard(int score, int level) {
|
void Renderer::drawScoreboard(int score, int level, int lines) {
|
||||||
|
int gapSize = blockSize / 8;
|
||||||
|
|
||||||
|
// 6 Because the gameBoard is 10 blocks, half that is 5 + 1 for the wall = 6
|
||||||
|
int blackAreaWidth = (windowWidth / 2) - (blockSize * 6) - gapSize;
|
||||||
|
|
||||||
|
SDL_Rect blackRect = { 0, 0, blackAreaWidth, windowHeight };
|
||||||
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255);
|
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 255);
|
||||||
renderText(fmt::format("Score: {0}", score), 350, 100, 24, SDL_Color{ 200,128,200 });
|
SDL_RenderFillRect(renderer.get( ), &blackRect);
|
||||||
renderText(fmt::format("Level: {0}", level), 350, 200, 24, SDL_Color{ 128,200,200 });
|
|
||||||
|
SDL_Surface* scoreboardSurface = IMG_Load("assets/scoreboard.png");
|
||||||
|
if (!scoreboardSurface) {
|
||||||
|
SDL_Log("Failed to load title surface: %s", SDL_GetError( ));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scoreboardTexture = SDL_CreateTextureFromSurface(renderer.get( ), scoreboardSurface);
|
||||||
|
SDL_FreeSurface(scoreboardSurface);
|
||||||
|
if (!scoreboardTexture) {
|
||||||
|
SDL_Log("Failed to create title texture: %s", SDL_GetError( ));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
SDL_QueryTexture(scoreboardTexture, nullptr, nullptr, &width, &height);
|
||||||
|
|
||||||
|
int scoreboardWidth = static_cast<int>(width * static_cast<float>(windowHeight) / height);
|
||||||
|
|
||||||
|
SDL_Rect scoreboardRect = {
|
||||||
|
windowWidth - scoreboardWidth,
|
||||||
|
0,
|
||||||
|
scoreboardWidth,
|
||||||
|
windowHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
SDL_RenderCopy(renderer.get( ), scoreboardTexture, nullptr, &scoreboardRect);
|
||||||
|
SDL_DestroyTexture(scoreboardTexture);
|
||||||
|
|
||||||
|
renderRightAlignedText(fmt::format("{0}", score), windowWidth - 30, 97, 32, SDL_Color{ 0,0,0 });
|
||||||
|
renderText(fmt::format("{0}", level), windowWidth - 92, 230, 32, SDL_Color{ 0,0,0 });
|
||||||
|
renderText(fmt::format("{0}", lines), windowWidth - 92, 330, 32, SDL_Color{ 0,0,0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::renderRightAlignedText(const std::string& text, int x, int y, int fontSize, const SDL_Color& color) {
|
||||||
|
TTF_Font* font = TTF_OpenFont("assets/tetris-gb.ttf", fontSize);
|
||||||
|
if (!font) {
|
||||||
|
SDL_Log("Failed to open font: %s", TTF_GetError( ));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int textWidth;
|
||||||
|
TTF_SizeText(font, text.c_str( ), &textWidth, nullptr);
|
||||||
|
|
||||||
|
int rightAlignedX = x - textWidth;
|
||||||
|
|
||||||
|
renderText(text, rightAlignedX, y, fontSize, color);
|
||||||
|
|
||||||
|
TTF_CloseFont(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::drawGrid(const GameBoard& board) {
|
void Renderer::drawGrid(const GameBoard& board) {
|
||||||
@@ -47,6 +102,9 @@ void Renderer::drawGrid(const GameBoard& board) {
|
|||||||
int totalWidth = w * blockSize;
|
int totalWidth = w * blockSize;
|
||||||
int totalHeight = h * blockSize;
|
int totalHeight = h * blockSize;
|
||||||
|
|
||||||
|
SDL_SetTextureBlendMode(textures[TetrisAssets::BORDER], SDL_BLENDMODE_BLEND);
|
||||||
|
SDL_SetTextureColorMod(textures[TetrisAssets::BORDER], 165, 42, 42);
|
||||||
|
|
||||||
for (int y = 0; y < (h + (innerBorderThickness * h)); ++y) {
|
for (int y = 0; y < (h + (innerBorderThickness * h)); ++y) {
|
||||||
SDL_Rect leftBlock = { offsetX - blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
SDL_Rect leftBlock = { offsetX - blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
||||||
renderTexture(TetrisAssets::BORDER, leftBlock.x, leftBlock.y - (innerBorderThickness * y + 1), leftBlock.w, leftBlock.h);
|
renderTexture(TetrisAssets::BORDER, leftBlock.x, leftBlock.y - (innerBorderThickness * y + 1), leftBlock.w, leftBlock.h);
|
||||||
@@ -130,33 +188,25 @@ void Renderer::drawTetromino(const Tetromino& tetromino) {
|
|||||||
SDL_SetTextureColorMod(textures[shapeToAsset(tetrominoShape)], color.r, color.g, color.b);
|
SDL_SetTextureColorMod(textures[shapeToAsset(tetrominoShape)], color.r, color.g, color.b);
|
||||||
|
|
||||||
if (tetrominoShape == TetrominoShape::I) {
|
if (tetrominoShape == TetrominoShape::I) {
|
||||||
|
if (angle == 90 || angle == 270) {
|
||||||
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_STARTR], color.r, color.g, color.b);
|
||||||
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_MIDR], color.r, color.g, color.b);
|
||||||
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_ENDR], color.r, color.g, color.b);
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
SDL_Rect destRect = { offsetX + (x + i) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
||||||
|
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_MIDR], nullptr, &destRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Rect startRect = { offsetX + (x + 0) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
||||||
|
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_ENDR], nullptr, &startRect);
|
||||||
|
|
||||||
|
SDL_Rect endRect = { offsetX + (x + 3) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
||||||
|
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_STARTR], nullptr, &endRect);
|
||||||
|
} else {
|
||||||
SDL_SetTextureColorMod(textures[TetrisAssets::I_START], color.r, color.g, color.b);
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_START], color.r, color.g, color.b);
|
||||||
SDL_SetTextureColorMod(textures[TetrisAssets::I_MID], color.r, color.g, color.b);
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_MID], color.r, color.g, color.b);
|
||||||
SDL_SetTextureColorMod(textures[TetrisAssets::I_END], color.r, color.g, color.b);
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_END], color.r, color.g, color.b);
|
||||||
|
|
||||||
if (angle == 90) {
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
SDL_Rect destRect = { offsetX + (x + i) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
|
||||||
SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_MID], nullptr, &destRect, angle, nullptr, SDL_FLIP_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Rect startRect = { offsetX + (x + 0) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
|
||||||
SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_START], nullptr, &startRect, angle, nullptr, SDL_FLIP_NONE);
|
|
||||||
|
|
||||||
SDL_Rect endRect = { offsetX + (x + 3) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
|
||||||
SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_END], nullptr, &endRect, angle, nullptr, SDL_FLIP_NONE);
|
|
||||||
} else if (angle == 270) {
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
SDL_Rect destRect = { offsetX + (x + i) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
|
||||||
SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_MID], nullptr, &destRect, angle, nullptr, SDL_FLIP_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Rect startRect = { offsetX + (x + 0) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
|
||||||
SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_END], nullptr, &startRect, angle, nullptr, SDL_FLIP_NONE);
|
|
||||||
|
|
||||||
SDL_Rect endRect = { offsetX + (x + 3) * blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
|
||||||
SDL_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_START], nullptr, &endRect, angle, nullptr, SDL_FLIP_NONE);
|
|
||||||
} else {
|
|
||||||
SDL_Rect destRect1 = { offsetX + (x + 0) * blockSize, offsetY + (y + 0) * blockSize, blockSize, blockSize };
|
SDL_Rect destRect1 = { offsetX + (x + 0) * blockSize, offsetY + (y + 0) * blockSize, blockSize, blockSize };
|
||||||
SDL_Rect destRect2 = { offsetX + (x + 0) * blockSize, offsetY + (y + 1) * blockSize, blockSize, blockSize };
|
SDL_Rect destRect2 = { offsetX + (x + 0) * blockSize, offsetY + (y + 1) * blockSize, blockSize, blockSize };
|
||||||
SDL_Rect destRect3 = { offsetX + (x + 0) * blockSize, offsetY + (y + 2) * blockSize, blockSize, blockSize };
|
SDL_Rect destRect3 = { offsetX + (x + 0) * blockSize, offsetY + (y + 2) * blockSize, blockSize, blockSize };
|
||||||
@@ -274,25 +324,70 @@ void Renderer::renderStartScreen( ) {
|
|||||||
TTF_SizeText(font, "PRESS START", &w, &h);
|
TTF_SizeText(font, "PRESS START", &w, &h);
|
||||||
TTF_CloseFont(font);
|
TTF_CloseFont(font);
|
||||||
|
|
||||||
renderText("press G to start", (windowWidth / 2) - (w / 2), windowHeight - 100, 16, SDL_Color{ 0, 0, 0 });
|
renderText("press G to start", (windowWidth / 2) - (w / 2), windowHeight - 70, 16, SDL_Color{ 0, 0, 0 });
|
||||||
|
|
||||||
SDL_RenderPresent(renderer.get( ));
|
SDL_RenderPresent(renderer.get( ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::renderGameOver(int score, int level, int lines) {
|
||||||
|
int fontSize = 24;
|
||||||
|
int lineSpacing = fontSize + 10;
|
||||||
|
|
||||||
|
int totalHeight = 5 * lineSpacing;
|
||||||
|
|
||||||
void Renderer::renderGameOver(int score, int level) {
|
int centerX = (windowWidth / 2);
|
||||||
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 128);
|
int centerY = (windowHeight / 2) - (totalHeight / 2);
|
||||||
SDL_RenderClear(renderer.get( ));
|
|
||||||
|
|
||||||
renderText("Game Over", 100, 100, 128, SDL_Color{ 200, 200, 200 });
|
|
||||||
renderText(fmt::format("Score: {0}", score), 100, 250, 64, SDL_Color{ 200, 128, 200 });
|
|
||||||
renderText(fmt::format("Level: {0}", level), 100, 350, 64, SDL_Color{ 128, 200, 200 });
|
|
||||||
|
|
||||||
|
renderText("game", centerX, centerY, fontSize, SDL_Color{ 0, 0, 0 });
|
||||||
|
renderText("over", centerX, centerY + lineSpacing, fontSize, SDL_Color{ 0, 0, 0 });
|
||||||
|
renderText("please", centerX, centerY + 2 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 });
|
||||||
|
renderText("try", centerX, centerY + 3 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 });
|
||||||
|
renderText("again @", centerX, centerY + 4 * lineSpacing, fontSize, SDL_Color{ 0, 0, 0 });
|
||||||
|
|
||||||
SDL_RenderPresent(renderer.get( ));
|
SDL_RenderPresent(renderer.get( ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::renderTetrominoPreview(const shared_ptr<Tetromino> nextTetromino) {
|
||||||
|
const auto& shape = nextTetromino->getShape( );
|
||||||
|
int x = nextTetromino->getX( ), y = nextTetromino->getY( );
|
||||||
|
|
||||||
|
double angle = nextTetromino->getRotationAngle( );
|
||||||
|
|
||||||
|
TetrominoShape tetrominoShape = nextTetromino->getShapeEnumn( );
|
||||||
|
|
||||||
|
SDL_SetTextureBlendMode(textures[shapeToAsset(tetrominoShape)], SDL_BLENDMODE_BLEND);
|
||||||
|
SDL_Color color = nextTetromino->getColor( );
|
||||||
|
SDL_SetTextureColorMod(textures[shapeToAsset(tetrominoShape)], color.r, color.g, color.b);
|
||||||
|
|
||||||
|
if (tetrominoShape == TetrominoShape::I) {
|
||||||
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_STARTR], color.r, color.g, color.b);
|
||||||
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_MIDR], color.r, color.g, color.b);
|
||||||
|
SDL_SetTextureColorMod(textures[TetrisAssets::I_ENDR], color.r, color.g, color.b);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
SDL_Rect destRect = { (windowWidth - 155) + (x + i) * blockSize, (windowHeight - 120) + y * blockSize, blockSize, blockSize };
|
||||||
|
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_MIDR], nullptr, &destRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Rect startRect = { (windowWidth - 155) + (x + 0) * blockSize, (windowHeight - 120) + y * blockSize, blockSize, blockSize };
|
||||||
|
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_ENDR], nullptr, &startRect);
|
||||||
|
|
||||||
|
SDL_Rect endRect = { (windowWidth - 155) + (x + 3) * blockSize, (windowHeight - 120) + y * blockSize, blockSize, blockSize };
|
||||||
|
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_STARTR], nullptr, &endRect);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (int row = 0; row < shape.size( ); ++row) {
|
||||||
|
for (int col = 0; col < shape[row].size( ); ++col) {
|
||||||
|
if (shape[row][col] != 0) {
|
||||||
|
SDL_Rect destRect = { (windowWidth - 140) + col * blockSize, (windowHeight - 130) + row * blockSize, blockSize, blockSize };
|
||||||
|
SDL_Point center = { blockSize / 2, blockSize / 2 };
|
||||||
|
SDL_RenderCopy(renderer.get( ), textures[shapeToAsset(tetrominoShape)], nullptr, &destRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Renderer::renderText(string text, int x, int y, int size, SDL_Color color) {
|
void Renderer::renderText(string text, int x, int y, int size, SDL_Color color) {
|
||||||
TTF_Font* font = TTF_OpenFont("assets/tetris-gb.ttf", size);
|
TTF_Font* font = TTF_OpenFont("assets/tetris-gb.ttf", size);
|
||||||
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str( ), color);
|
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str( ), color);
|
||||||
|
|||||||
+4
-2
@@ -35,7 +35,7 @@ private:
|
|||||||
void drawGrid(const GameBoard& gameBoard);
|
void drawGrid(const GameBoard& gameBoard);
|
||||||
void drawLockedBlocks(const GameBoard& gameBoard);
|
void drawLockedBlocks(const GameBoard& gameBoard);
|
||||||
void drawTetromino(const Tetromino& tetromino);
|
void drawTetromino(const Tetromino& tetromino);
|
||||||
void drawScoreboard(int score, int level);
|
void drawScoreboard(int score, int level, int lines);
|
||||||
|
|
||||||
TetrisAssets shapeToAsset(const TetrominoShape shape);
|
TetrisAssets shapeToAsset(const TetrominoShape shape);
|
||||||
|
|
||||||
@@ -58,8 +58,10 @@ public:
|
|||||||
bool loadTexture(const string& filePath, const TetrisAssets shape);
|
bool loadTexture(const string& filePath, const TetrisAssets shape);
|
||||||
void renderTexture(const TetrisAssets shape, int x, int y, int width, int height);
|
void renderTexture(const TetrisAssets shape, int x, int y, int width, int height);
|
||||||
void renderStartScreen( );
|
void renderStartScreen( );
|
||||||
void renderGameOver(int score, int level);
|
void renderGameOver(int score, int level, int lines);
|
||||||
void renderText(string text, int x, int y, int size, SDL_Color color);
|
void renderText(string text, int x, int y, int size, SDL_Color color);
|
||||||
|
void renderRightAlignedText(const std::string& text, int x, int y, int fontSize, const SDL_Color& color);
|
||||||
|
void renderTetrominoPreview(const shared_ptr<Tetromino> nextTetromino);
|
||||||
|
|
||||||
const int getBlockSize( ) const;
|
const int getBlockSize( ) const;
|
||||||
void setBlockSize(int newBlockSize);
|
void setBlockSize(int newBlockSize);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#include "Tetromino.hpp"
|
#include "Tetromino.hpp"
|
||||||
#include "GameBoard.hpp"
|
#include "GameBoard.hpp"
|
||||||
#include <iostream>
|
|
||||||
Tetromino::Tetromino(TetrominoShape shape) : x(0), y(0), textureShape(shape) {
|
Tetromino::Tetromino(TetrominoShape shape) : x(0), y(0), textureShape(shape) {
|
||||||
initializeShape(shape);
|
initializeShape(shape);
|
||||||
currentRotationState = 1;
|
currentRotationState = 1;
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ int main( ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Game game;
|
Game game;
|
||||||
if (!game.init("Tetris", 800, 600)) {
|
if (!game.init("Tetris", 810, 600)) {
|
||||||
SDL_Log("Failed to init game");
|
SDL_Log("Failed to init game");
|
||||||
SDL_Quit( );
|
SDL_Quit( );
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user