-1

I am programming a Space Invaders clone in C++ on the console.
I have a spaceship class in which I create the shape on the console screen.
I use arrow keys to move it across the screen horizontally.
Here you have pictures, which show moving the spaceship to the right.
https://i.stack.imgur.com/Wmefx.jpg
This is my approach

class BaseSpaceShip{
protected:
private: 
    char ship[4][19] = {
    "     \xDB      ",
    "   \xDB\xDB\xDB\xDB\xDB    ",
    "      \xDB\xDB\xDB\xDB\xDB\xDB\xDB\xDB\xDB"
    };   
         const int mapy = 4;
         int x, y;
         void Invalidate();
         void cls();
public:
    BaseSpaceShip();
    ~BaseSpaceShip();
    void Init();
    virtual void MoveShip(int dx, int dy);
};
BaseSpaceShip::BaseSpaceShip() {
    x = 130;
    y = 69;
    Init();
}
BaseSpaceShip::~BaseSpaceShip() {
}
void BaseSpaceShip::Init() {
    cls();
    for (int i = 0; i < mapy; i++) {
        gotoxy(x - i, y + i);
        cout << ship[i] << endl;
    }       
}
void BaseSpaceShip::MoveShip(int dx , int dy) {
    x+= dx;
    y += dy;
    Init();
}

Why does this bug occur and how do I solve it?
Furthermore I heard of the double buffering concept to remove the screen flickering when I move my spaceship, but how do I implement this?

Daniel Ignjat
  • 73
  • 1
  • 1
  • 7
  • i think gotoxy(x - i, y + i); should be just gotoxy(x, y + i); you want to print all lines of the ship on the same x offset – FamZ Dec 25 '16 at 21:31
  • It is probably operating system specific. [ncurses](https://www.gnu.org/software/ncurses/) is not part of the C++14 standard, and is not available on every operating system. – Basile Starynkevitch Dec 25 '16 at 21:41
  • Tags work perfectly well here. They do not need your assistance by adding them redundantly in the title as well. It is totally unnecessary to repeat the tag info in the title and text of your post. Let the tags do their job. – Ken White Dec 25 '16 at 21:42

1 Answers1

0

gotoxy(x - i, y + i); should be just gotoxy(x, y + i); you want to print all lines of the ship on the same x offset. Also you need to watch that your ship isn't going over the console border, x = 130 is a bit much, isn't it? Flickering is appearing because you are clearing the whole console on update. There are two ways to solve this

  • Redraw only updated objects:

Let's say you want to update your ship because it moved. You could just move the cursor to the old ship location, write spaces over the ship, move to the new ship location and then redraw the ship.

  • Use the "double buffering" method you mentioned in your question:

This would look like that: You have two 2D char arrays(or a array/vector of strings, which is better) with the same size of your scene, both would be initialized with spaces at the start.

// Scene size is 100x100
std::vector<std::string> buffer_a(100, std::string(100, ' '));
std::vector<std::string> buffer_b(100, std::string(100, ' '));

Now, instead of directly couting to the screen, you are writing your changes to buffer_b, so instead of

for (int i = 0; i < mapy; i++)
{
    gotoxy(x, y + i);
    cout << ship[i] << endl;
}   

you are doing something like this:

for (unsigned int i = 0; i < ship_width; i++)
    for(unsigned int j = 0; j < ship_height; ++j)
        buffer_b[x + i][y + j] = ship[i][j];

When you are done updating, you need to loop over both buffers and draw all elements which are not the same as in buffer_a and then you are copying buffer_b to buffer_a.

for (unsigned int i = 0; i < scene_width; i++)
{
    for(unsigned int j = 0; j < scene_height; ++j)
    {
        if(buffer_a[i][j] != buffer_b[i][j])
        {
            gotoxy(i, j);
            std::cout << buffer_b[i][j];
            buffer_a[i][j] = buffer_b[i][j];
        }
    }
}

Note that, when you are writing changes to buffer_b, let's say if you are moving the ship, you still need to write spaces over the old ship position in buffer_b before you update it, except for the first update when buffer_b is empty. With this method it's good to implement a main loop, which is first reading input, then updating and then swapping buffers at the end of each iteration.

Maybe the first method is better if the scene is really big because you are testing each element of the buffers in the second method, There's already a great answer on that by Cameron here: Update console without flickering - c++

Community
  • 1
  • 1
FamZ
  • 461
  • 3
  • 13
  • Hi, the x=130 is for placing the ship in the middle of the screen, since it is a full screen console app. If i only update the ship, it would still flicker, but only the ship right? it still isnt moving correctly, is there a way to print all of my ship in one go since i think that would go around the problem. – Daniel Ignjat Dec 26 '16 at 14:48
  • I tried your code and it works for me, the only thing i changed is that i made the ship array a string array and removed some spaces at the start of the 3rd string std::string ship[] {" \xDB ", " \xDB\xDB\xDB\xDB\xDB ", " \xDB\xDB\xDB\xDB\xDB\xDB\xDB\xDB\xDB"}; – FamZ Dec 26 '16 at 15:20
  • Btw, should the lower line of ship stand still or is it part of the ship? that would change everything, also i tested the movement with ship.MoveShip(1, 0); in a loop – FamZ Dec 26 '16 at 15:25
  • Also i would recommend you to write the app with SFML or somthing like that, it's easier and you won't have problems with flickering – FamZ Dec 26 '16 at 15:33
  • I implemented your changes and now it is working fine although it is still flickering when I move it for a short distance. I know, that there are easier ways to do this, but i wanted to program it without using any libraries , kind of a challenge. – Daniel Ignjat Dec 27 '16 at 02:59
  • You need to know that the console isn't meant for such tasks – FamZ Dec 27 '16 at 15:14