Update Console Without Flickering - C++

Update console without flickering - c++

Ah, this brings back the good old days. I did similar things in high school :-)

You're going to run into performance problems. Console I/O, especially on Windows, is slow. Very, very slow (sometimes slower than writing to disk, even). In fact, you'll quickly become amazed how much other work you can do without it affecting the latency of your game loop, since the I/O will tend to dominate everything else. So the golden rule is simply to minimize the amount of I/O you do, above all else.

First, I suggest getting rid of the system("cls") and replace it with calls to the actual Win32 console subsystem functions that cls wraps (docs):

#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

void cls()
{
// Get the Win32 handle representing standard output.
// This generally only has to be done once, so we make it static.
static const HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

CONSOLE_SCREEN_BUFFER_INFO csbi;
COORD topLeft = { 0, 0 };

// std::cout uses a buffer to batch writes to the underlying console.
// We need to flush that to the console because we're circumventing
// std::cout entirely; after we clear the console, we don't want
// stale buffered text to randomly be written out.
std::cout.flush();

// Figure out the current width and height of the console window
if (!GetConsoleScreenBufferInfo(hOut, &csbi)) {
// TODO: Handle failure!
abort();
}
DWORD length = csbi.dwSize.X * csbi.dwSize.Y;

DWORD written;

// Flood-fill the console with spaces to clear it
FillConsoleOutputCharacter(hOut, TEXT(' '), length, topLeft, &written);

// Reset the attributes of every character to the default.
// This clears all background colour formatting, if any.
FillConsoleOutputAttribute(hOut, csbi.wAttributes, length, topLeft, &written);

// Move the cursor back to the top left for the next sequence of writes
SetConsoleCursorPosition(hOut, topLeft);
}

Indeed, instead of redrawing the entire "frame" every time, you're much better off drawing (or erasing, by overwriting them with a space) individual characters at a time:

// x is the column, y is the row. The origin (0,0) is top-left.
void setCursorPosition(int x, int y)
{
static const HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
std::cout.flush();
COORD coord = { (SHORT)x, (SHORT)y };
SetConsoleCursorPosition(hOut, coord);
}

// Step through with a debugger, or insert sleeps, to see the effect.
setCursorPosition(10, 5);
std::cout << "CHEESE";
setCursorPosition(10, 5);
std::cout 'W';
setCursorPosition(10, 9);
std::cout << 'Z';
setCursorPosition(10, 5);
std::cout << " "; // Overwrite characters with spaces to "erase" them
std::cout.flush();
// Voilà, 'CHEESE' converted to 'WHEEZE', then all but the last 'E' erased

Note that this eliminates the flicker, too, since there's no longer any need to clear the screen completely before redrawing -- you can simply change what needs changing without doing an intermediate clear, so the previous frame is incrementally updated, persisting until it's completely up to date.

I suggest using a double-buffering technique: Have one buffer in memory that represents the "current" state of the console screen, initially populated with spaces. Then have another buffer that represents the "next" state of the screen. Your game update logic will modify the "next" state (exactly like it does with your battleField array right now). When it comes time to draw the frame, don't erase everything first. Instead, go through both buffers in parallel, and write out only the changes from the previous state (the "current" buffer at that point contains the previous state). Then, copy the "next" buffer into the "current" buffer to set up for your next frame.

char prevBattleField[MAX_X][MAX_Y];
std::memset((char*)prevBattleField, 0, MAX_X * MAX_Y);

// ...

for (int y = 0; y != MAX_Y; ++y)
{
for (int x = 0; x != MAX_X; ++x)
{
if (battleField[x][y] == prevBattleField[x][y]) {
continue;
}
setCursorPosition(x, y);
std::cout << battleField[x][y];
}
}
std::cout.flush();
std::memcpy((char*)prevBattleField, (char const*)battleField, MAX_X * MAX_Y);

You can even go one step further and batch runs of changes together into a single I/O call (which is significantly cheaper than many calls for individual character writes, but still proportionally more expensive the more characters are written).

// Note: This requires you to invert the dimensions of `battleField` (and
// `prevBattleField`) in order for rows of characters to be contiguous in memory.
for (int y = 0; y != MAX_Y; ++y)
{
int runStart = -1;
for (int x = 0; x != MAX_X; ++x)
{
if (battleField[y][x] == prevBattleField[y][x]) {
if (runStart != -1) {
setCursorPosition(runStart, y);
std::cout.write(&battleField[y][runStart], x - runStart);
runStart = -1;
}
}
else if (runStart == -1) {
runStart = x;
}
}
if (runStart != -1) {
setCursorPosition(runStart, y);
std::cout.write(&battleField[y][runStart], MAX_X - runStart);
}
}
std::cout.flush();
std::memcpy((char*)prevBattleField, (char const*)battleField, MAX_X * MAX_Y);

In theory, that will run a lot faster than the first loop; however in practice it probably won't make a difference since std::cout is already buffering writes anyway. But it's a good example (and a common pattern that shows up a lot when there is no buffer in the underlying system), so I included it anyway.

Finally, note that you can reduce your sleep to 1 millisecond. Windows will actually often sleep longer, typically up 15ms, but it will prevent your CPU core from reaching 100% usage with a minimum of additional latency.

Note that this not at all the way "real" games do things; they almost always clear the buffer and redraw everything every frame. They don't get flickering because they use the equivalent of a double-buffer on the GPU, where the previous frame stays visible until the new frame is completely finished being drawn.

Bonus: You can change the colour to any of 8 different system colours, and the background too:

void setConsoleColour(unsigned short colour)
{
static const HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
std::cout.flush();
SetConsoleTextAttribute(hOut, colour);
}

// Example:
const unsigned short DARK_BLUE = FOREGROUND_BLUE;
const unsigned short BRIGHT_BLUE = FOREGROUND_BLUE | FOREGROUND_INTENSITY;

std::cout << "Hello ";
setConsoleColour(BRIGHT_BLUE);
std::cout << "world";
setConsoleColour(DARK_BLUE);
std::cout << "!" << std::endl;

Console flickering on redraw

The issue is that the existing content is being removed when you call Console.Clear();, even if it's unchanged. You're writing it back immediately, but there is enough of a delay for this to present as a flicker, as you've found.

Since you're rewriting the entire grid again every time, I would suggest you use Console.SetCursorPosition(0, 0); This will move the start position for writing back to the beginning, and then it will overwrite everything without first clearing the console. This should eliminate the flicker.

static void render()
{
string temp = "";

for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
string current = Grid[y, x];

temp += current;
}
temp += "\n";
}

Console.SetCursorPosition(0, 0); // reset the cursor position
Console.Write(temp);
}

I would go so far as to remove the string building altogether, and just update the individual characters in the console:

static void render()
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Console.SetCursorPosition(x, y); // set the position to x,y
string current = Grid[y, x];
Console.Write(current); // write this value
}
}
}

Console application: How to update the display without flicker?

Try Console.SetCursorPosition. More details here: How can I update the current line in a C# Windows Console App?

How do I stop flickering in a console application?

Clearing the screen will make the entire screen go black before you start drawing, causing flicker.

I'd recommend looking into the Windows Console API (assuming you're happy for this to work only in windows). Set the cursor position and then draw everything.

COORD coord;
coord.X = 0;
coord.Y = 0;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);

This is not code I've tested, and I don't know if it will work correctly with cout. You might want to replace look at other operations such as 'WriteConsole(...)'

Clear whole Console Without flicker c#

You can use Console.SetCursorPosition and then override everything that you need to:

public static void Main()
{
for (int i = 0; i < 100; i++)
{
Console.SetCursorPosition(0, 0);
Console.WriteLine("Index = " + i);
System.Threading.Thread.Sleep(500);
}
}

You can also create your own function to do it automatically:

public static void ClearConsole()
{
Console.SetCursorPosition(0, 0);
Console.CursorVisible = false;
for (int y = 0; y<Console.WindowHeight; y++)
Console.Write(new String(' ', Console.WindowWidth));
Console.SetCursorPosition(0, 0);
Console.CursorVisible = true;
}

Win32 C++ console clearing screen without blinking

The reason this is happening is because the display refreshes between the time you clear the console screen and actually draw to it. Usually this can happen so fast that you never see it but once in a while you do it at the right time and you experience flickering.

One great option is to create an offscreen buffer the same size and width as the console screen, do all of your text output and updating there, then send the entire buffer to the console screen using WriteConsoleOutput. Make sure you take into account that the screen buffer has to hold both text and attribute information, the same format as the console.

BOOL WINAPI WriteConsoleOutput(
_In_ HANDLE hConsoleOutput,
_In_ const CHAR_INFO *lpBuffer,
_In_ COORD dwBufferSize,
_In_ COORD dwBufferCoord,
_Inout_ PSMALL_RECT lpWriteRegion
);


Related Topics



Leave a reply



Submit