c++drawsfmlbad-alloc

Can't draw sf::RectangleShape s stored in vector (Tetris clone)


I am trying to storesf::RectangleShape's into thestd::vectorand then draw each of them into thesf::RenderWindow.

Single rectangle shape is representing 1x1 tetromino and i would like to store it into the vector each time it reaches the bottom of the window. Then I would like to reset the position of the current tetromino to the default position.

I think I'm not even to able store it correctly. Each time the tetromino reaches the bottom it gives me this error message:

    terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc

Bellow please find my current code. I just started working on it and already got stuck.

Definitions.h

#pragma once

// Point structure
struct Point
{
    int dim_x;
    int dim_y;
};

// Field Dimensions
const int fieldRows = 10;
const int fieldColumns = 9;
const int pointSize = 50.f;

// For checkingEdges funntion within the Tetrnomino.h
enum Edge
{
    leftEdge,
    rightEdge,
    noneEdge
};

Game.h

#pragma once

#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>

#include "Definitions.h"
#include "Tetromino.h"

class Game
{
public:
    Game();
    ~Game();
    // Game starter
    void run();

    // Accessors
    bool running();
private:

    // Updating and rendering the game window
    void update();
    void render();

    // Initialization
    void initVariables();
    void initWindow();
    void initBacgroundMusic();

    // Polling
    void pollEvents();

    // Window logic stuff
    sf::RenderWindow* _window;
    sf::Event _event;
    void drawStack();

    // Bacground Music
    sf::Music _ost;

    // Tetromino + Its logic
    Tetromino _T;
    sf::Time delayTime = sf::milliseconds(300);
    sf::Clock clock;

};

Tetromino.h

#pragma once

#include <SFML/Graphics.hpp>
#include <vector>

#include "Definitions.h"

class Tetromino
{
public:
    Tetromino();
    ~Tetromino();

    // Initialization
    void initTetromino();

    // Tetromonino logic
    void moveTetromino();
    Edge checkEdges();

    // Getters & Setters
    sf::RectangleShape getTetromino();
    sf::RectangleShape getStackPart(int part);
    int getStackSize();
    void setTetromino(sf::RectangleShape &t);

private:
    // The current tetromino
    sf::RectangleShape _tetromino;
    std::vector<sf::RectangleShape> _stack;
};

Game.cpp

#include "Game.h"

//-----Consturcotrs and Destructors-----//
Game::Game()
{
    //Basic Initialization
    _T.initTetromino();
    initVariables();
}

Game::~Game()
{
    delete _window;
}

//-----Private Functions-----//
void Game::run()
{
     update();
     render();
}

bool Game::running()
{
    return _window->isOpen();
}

void Game::update()
{
    sf::Time elapsed = clock.getElapsedTime();

    pollEvents();
    if (elapsed >= delayTime)
    {
        _T.moveTetromino();
        clock.restart();
    }
}

void Game::render()
{
    _window->clear(sf::Color::White);
    _window->draw(_T.getTetromino());
    drawStack();
    _window->display();
}

void Game::initVariables()
{
    _window = nullptr;
    initWindow();
    initBacgroundMusic();
}

void Game::initWindow()
{
    _window = new sf::RenderWindow(sf::VideoMode(fieldColumns * pointSize, fieldRows * pointSize), "Tetris v0.2", sf::Style::Default);
    _window->setVerticalSyncEnabled(true);
    _window->setFramerateLimit(60);
}

void Game::initBacgroundMusic()
{
    _ost.openFromFile("../QT_SFML_Tetris/Music.ogg");
    _ost.play();
    _ost.setLoop(true);
    _ost.setVolume(50.f);
}

void Game::pollEvents()
{
    while (_window->pollEvent(_event))
    {
        if (_event.type == sf::Event::Closed) {_window->close();}
        if (_event.type == sf::Event::KeyPressed)
        {
            if (_event.key.code == sf::Keyboard::Escape){_window->close();}
            if (_event.key.code == sf::Keyboard::Left && _T.checkEdges() != leftEdge)
            {
                sf::RectangleShape t = _T.getTetromino();
                t.setPosition(t.getPosition().x - pointSize, t.getPosition().y);
                _T.setTetromino(t);
                render();
            }
            if (_event.key.code == sf::Keyboard::Right && _T.checkEdges() != rightEdge)
            {
                sf::RectangleShape t = _T.getTetromino();
                t.setPosition(t.getPosition().x + pointSize, t.getPosition().y);
                _T.setTetromino(t);
                render();
            }
            if (_event.key.code == sf::Keyboard::Down)
            {
                sf::RectangleShape t = _T.getTetromino();
                t.setPosition(t.getPosition().x, t.getPosition().y+ pointSize);
                _T.setTetromino(t);
                render();
            }
        }
    }
}

**void Game::drawStack()**
{
        for (unsigned int i = _T.getStackSize(); i > 0; --i)
        {
            _window->draw(_T.getStackPart(i));
        }
}

main.cpp

#include <Game.h>

int main()
{
    Game game;
    while (game.running())
    {
        game.run();
    }
    return 0;
}

Tetromino.cpp

#include "Tetromino.h"

//-----Consturcotrs and Destructors-----//
Tetromino::Tetromino()
{

}

Tetromino::~Tetromino()
{

}

//-----Public Functions-----//
void Tetromino::initTetromino()
{
    _tetromino.setPosition(sf::Vector2f((fieldColumns * pointSize - pointSize) / 2, 0.f));
    _tetromino.setSize(sf::Vector2f(pointSize, pointSize));
    _tetromino.setFillColor(sf::Color::Red);
}

void Tetromino::moveTetromino()
{
    _tetromino.move(0.f, pointSize);

    if (_tetromino.getPosition().y > fieldRows * pointSize - pointSize)
    {
        _stack.push_back(_tetromino);
        _tetromino.setPosition(sf::Vector2f((fieldColumns * pointSize - pointSize) / 2, 0.f));
    }
}

Edge Tetromino::checkEdges()
{
    if (_tetromino.getPosition().x == 0)
    {
        return leftEdge;
    }
    else if (_tetromino.getPosition().x == (fieldColumns * pointSize) - pointSize)
    {
        return rightEdge;
    }
    else return noneEdge;
}

sf::RectangleShape Tetromino::getTetromino()
{
    return _tetromino;
}

sf::RectangleShape Tetromino::getStackPart(int part)
{
    return _stack[part];
}

int Tetromino::getStackSize()
{
    return _stack.size();
}

void Tetromino::setTetromino(sf::RectangleShape &t)
{
    _tetromino = t;
}

I think the main issue could be within this line:

        _stack.push_back(_tetromino);

Solution

  • In the drawStack() method you try to iterate backwards.

    1. There is a reverse iterator doing this for you.
    2. you have an off-by one error in your index calculation, which works only with an empty vector (exactly to prevent these errors you should use the iterator!)

    You may want to read about iterators in C++ here is a small example. According to your code it will look like (note that you need also a getStack()-method in Tetromino.hpp, returning the reference of the vector):

    void Game::drawStack()
    {
        for (auto it = _T.getStack().rbegin(); it != _T.getStack().rend(); it++)
        {
            _window->draw(*it);
        }
    }
    

    If you want to keep the index, I this is a fix:

        void Game::drawStack()
        {
            for (int i = _T.getStackSize()-1; i >= 0; --i)
            {
                _window->draw(_T.getStackPart(i));
            }
        }