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++
|
||||
|
||||
## 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,
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
w, h,
|
||||
SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE
|
||||
SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN
|
||||
));
|
||||
|
||||
if (!window) {
|
||||
@@ -19,8 +19,6 @@ bool Game::init(const char* title, int w, int h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetWindowMinimumSize(window.get( ), 800, 650);
|
||||
|
||||
renderer = std::shared_ptr<SDL_Renderer>(
|
||||
SDL_CreateRenderer(window.get( ), -1, SDL_RENDERER_ACCELERATED),
|
||||
[ ](SDL_Renderer* r) { SDL_DestroyRenderer(r); }
|
||||
@@ -69,15 +67,24 @@ void Game::run( ) {
|
||||
gameOver = true;
|
||||
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) {
|
||||
if (quit) return;
|
||||
inputHandler( );
|
||||
gameRenderer->renderGameOver(gameBoard->getScore( ), gameBoard->getLevel( ));
|
||||
render( );
|
||||
gameRenderer->renderGameOver(gameBoard->getScore( ), gameBoard->getLevel( ), gameBoard->getLines( ));
|
||||
}
|
||||
}
|
||||
|
||||
void Game::inputHandler( ) {
|
||||
SDL_Event event;
|
||||
Mix_Chunk* movePieceSound = Mix_LoadWAV("assets/sound_effects/move_piece.wav");
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_QUIT) {
|
||||
SDL_Quit( );
|
||||
@@ -87,10 +94,20 @@ void Game::inputHandler( ) {
|
||||
case SDLK_LEFT:
|
||||
case SDLK_a:
|
||||
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;
|
||||
case SDLK_RIGHT:
|
||||
case SDLK_d:
|
||||
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;
|
||||
case SDLK_DOWN:
|
||||
case SDLK_s:
|
||||
@@ -102,8 +119,15 @@ void Game::inputHandler( ) {
|
||||
case SDLK_ESCAPE:
|
||||
break;
|
||||
case SDLK_g:
|
||||
if (startSequence)
|
||||
if (startSequence) {
|
||||
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;
|
||||
case SDLK_r:
|
||||
if (isGameOver( ))
|
||||
@@ -112,10 +136,38 @@ void Game::inputHandler( ) {
|
||||
case SDLK_q:
|
||||
SDL_Quit( );
|
||||
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:
|
||||
break;
|
||||
}
|
||||
} 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( );
|
||||
}
|
||||
}
|
||||
@@ -150,6 +202,7 @@ void Game::render( ) {
|
||||
SDL_RenderClear(renderer.get( ));
|
||||
|
||||
gameRenderer->renderBoard(*gameBoard);
|
||||
gameRenderer->renderTetrominoPreview(gameBoard->getNextTetromino( ));
|
||||
|
||||
SDL_RenderPresent(renderer.get( ));
|
||||
}
|
||||
|
||||
+64
-5
@@ -2,7 +2,7 @@
|
||||
#include <iostream>
|
||||
|
||||
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( );
|
||||
}
|
||||
|
||||
@@ -20,6 +20,14 @@ void GameBoard::tryRotateCurrentTetromino( ) {
|
||||
if (checkCollision(*currentTetromino))
|
||||
for (int i = 0; i < 3; i++)
|
||||
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 {
|
||||
@@ -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( ) {
|
||||
int clearedLines = 0;
|
||||
for (int row = 0; row < height; row++) {
|
||||
if (all_of(lockedTetrominos[row].begin( ), lockedTetrominos[row].end( ), [ ](int cell) { return cell != 0; })) {
|
||||
lockedTetrominos.erase(lockedTetrominos.begin( ) + row);
|
||||
@@ -111,20 +127,61 @@ void GameBoard::clearLines( ) {
|
||||
score += 100;
|
||||
if (score % 1000 == 0) {
|
||||
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( ) {
|
||||
//Ensure on startup that we have a tetromino
|
||||
if (!nextTetromino) {
|
||||
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));
|
||||
currentTetromino = make_unique<Tetromino>(shape);
|
||||
|
||||
nextTetromino = make_shared<Tetromino>(shape);
|
||||
}
|
||||
|
||||
currentTetromino = move(nextTetromino);
|
||||
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;
|
||||
lockedTetrominos.clear( );
|
||||
lockedColors.clear( );
|
||||
}
|
||||
}
|
||||
|
||||
void GameBoard::update( ) {
|
||||
@@ -132,8 +189,7 @@ void GameBoard::update( ) {
|
||||
lockTetromino( );
|
||||
clearLines( );
|
||||
spawnNewTetromino( );
|
||||
} else
|
||||
clearLines( );
|
||||
}
|
||||
}
|
||||
|
||||
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 int GameBoard::getScore( ) const { return score; }
|
||||
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<SDL_Color>>& GameBoard::getLockedColors( ) const { return lockedColors; }
|
||||
|
||||
+9
-1
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
@@ -14,12 +18,14 @@ private:
|
||||
|
||||
vector<vector<int>> lockedTetrominos;
|
||||
vector<vector<SDL_Color>> lockedColors;
|
||||
unique_ptr<Tetromino> currentTetromino;
|
||||
shared_ptr<Tetromino> currentTetromino;
|
||||
shared_ptr<Tetromino> nextTetromino;
|
||||
const int width = 10;
|
||||
const int height = 20;
|
||||
bool collision;
|
||||
int score;
|
||||
int level;
|
||||
int lines;
|
||||
|
||||
public:
|
||||
GameBoard( );
|
||||
@@ -33,6 +39,8 @@ public:
|
||||
const bool isCollision( ) const;
|
||||
const int getScore( ) const;
|
||||
const int getLevel( ) const;
|
||||
const int getLines( ) const;
|
||||
const shared_ptr<Tetromino> getNextTetromino( ) const;
|
||||
|
||||
const vector<vector<int>>& getLockedTetrominos( ) const;
|
||||
const vector<vector<SDL_Color>>& getLockedColors( ) const;
|
||||
|
||||
+130
-35
@@ -28,13 +28,68 @@ void Renderer::renderBoard(const GameBoard& gameBoard) {
|
||||
drawGrid(gameBoard);
|
||||
drawLockedBlocks(gameBoard);
|
||||
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);
|
||||
renderText(fmt::format("Score: {0}", score), 350, 100, 24, SDL_Color{ 200,128,200 });
|
||||
renderText(fmt::format("Level: {0}", level), 350, 200, 24, SDL_Color{ 128,200,200 });
|
||||
SDL_RenderFillRect(renderer.get( ), &blackRect);
|
||||
|
||||
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) {
|
||||
@@ -47,6 +102,9 @@ void Renderer::drawGrid(const GameBoard& board) {
|
||||
int totalWidth = w * 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) {
|
||||
SDL_Rect leftBlock = { offsetX - blockSize, offsetY + y * blockSize, blockSize, blockSize };
|
||||
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);
|
||||
|
||||
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_MID], 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 destRect2 = { offsetX + (x + 0) * blockSize, offsetY + (y + 1) * 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_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( ));
|
||||
}
|
||||
|
||||
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) {
|
||||
SDL_SetRenderDrawColor(renderer.get( ), 0, 0, 0, 128);
|
||||
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 });
|
||||
int centerX = (windowWidth / 2);
|
||||
int centerY = (windowHeight / 2) - (totalHeight / 2);
|
||||
|
||||
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( ));
|
||||
}
|
||||
|
||||
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) {
|
||||
TTF_Font* font = TTF_OpenFont("assets/tetris-gb.ttf", size);
|
||||
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str( ), color);
|
||||
|
||||
+4
-2
@@ -35,7 +35,7 @@ private:
|
||||
void drawGrid(const GameBoard& gameBoard);
|
||||
void drawLockedBlocks(const GameBoard& gameBoard);
|
||||
void drawTetromino(const Tetromino& tetromino);
|
||||
void drawScoreboard(int score, int level);
|
||||
void drawScoreboard(int score, int level, int lines);
|
||||
|
||||
TetrisAssets shapeToAsset(const TetrominoShape shape);
|
||||
|
||||
@@ -58,8 +58,10 @@ public:
|
||||
bool loadTexture(const string& filePath, const TetrisAssets shape);
|
||||
void renderTexture(const TetrisAssets shape, int x, int y, int width, int height);
|
||||
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 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;
|
||||
void setBlockSize(int newBlockSize);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#include "Tetromino.hpp"
|
||||
#include "GameBoard.hpp"
|
||||
#include <iostream>
|
||||
|
||||
Tetromino::Tetromino(TetrominoShape shape) : x(0), y(0), textureShape(shape) {
|
||||
initializeShape(shape);
|
||||
currentRotationState = 1;
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ int main( ) {
|
||||
}
|
||||
|
||||
Game game;
|
||||
if (!game.init("Tetris", 800, 600)) {
|
||||
if (!game.init("Tetris", 810, 600)) {
|
||||
SDL_Log("Failed to init game");
|
||||
SDL_Quit( );
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user