add sounds, finish main game, add new textures, add tetromino preview, fix some bugs

This commit is contained in:
Rene Kievits
2024-10-15 05:14:41 +02:00
parent 24c8c3fe2c
commit f19f183686
21 changed files with 279 additions and 47 deletions

View File

@@ -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( ));
}

View File

@@ -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));
nextTetromino = make_shared<Tetromino>(shape);
}
currentTetromino = move(nextTetromino);
currentTetromino->move(width / 2 - 1, 0);
// 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));
currentTetromino = make_unique<Tetromino>(shape);
currentTetromino->move(width / 2 - 1, 0);
nextTetromino = make_shared<Tetromino>(shape);
if (checkCollision(*currentTetromino))
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; }

View File

@@ -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;

View File

@@ -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) {
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) {
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_RenderCopyEx(renderer.get( ), textures[TetrisAssets::I_MID], nullptr, &destRect, angle, nullptr, SDL_FLIP_NONE);
SDL_RenderCopy(renderer.get( ), textures[TetrisAssets::I_MIDR], nullptr, &destRect);
}
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_RenderCopy(renderer.get( ), textures[TetrisAssets::I_ENDR], nullptr, &startRect);
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);
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);
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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;