Well I was bored so wanted to make an animation in a console window.
Now when I setup the first bits I noticed it is very slow, something around 333ms for a whole screen to fill with characters..
I am wondering if there is a way to at least get ~20 fps?
Here is my code:
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include <array>
#define WIDTH (100)
#define HEIGHT (35)
bool SetWindow(int Width, int Height) {
_COORD coord;
coord.X = Width; coord.Y = Height;
_SMALL_RECT Rect;
Rect.Left = 0; Rect.Top = 0;
Rect.Bottom = Height - 1; Rect.Right = Width - 1;
HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (Handle == NULL)return FALSE;
SetConsoleScreenBufferSize(Handle, coord);
if(!SetConsoleWindowInfo(Handle, TRUE, &Rect)) return FALSE;
return TRUE;
}
std::array<std::array<unsigned char, WIDTH+1>, HEIGHT> Screen;//WIDTH+1 = prevent cout from undefined behaviour
void Putchars(unsigned char x){
for(int row = 0; row < HEIGHT; ++row){
std::fill(Screen[row].begin(),Screen[row].end(),x);
Screen[row].at(WIDTH) = 0;//here = prevent cout from undefined behaviour
}
}
void ShowFrame(DWORD delay = 0,bool fPutchars = false, unsigned char x = 0){
if(fPutchars)Putchars(x);
if(delay)Sleep(delay);
system("CLS");
for(int row = 0; row < HEIGHT; ++row)
std::cout << Screen[row].data() << std::flush;
}
int _tmain(int argc, _TCHAR* argv[]){//sould execute @~63 fps, yet it executes @~3-4 fps
if(SetWindow(100,HEIGHT)){
for(unsigned char i = 219; i != 0; --i)
ShowFrame(16,true, i);
}
return 0;
}
Edit: after reading numerous answers, tips and comments I finally worked it out, thank you guys, this is my final “base” code:
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include <array>
#define WIDTH (100)
#define HEIGHT (34)
HANDLE current;
HANDLE buffer;
bool SetWindow(int Width, int Height) {
_COORD coord;
coord.X = Width; coord.Y = Height;
_SMALL_RECT Rect;
Rect.Left = 0; Rect.Top = 0;
Rect.Bottom = Height - 1; Rect.Right = Width - 1;
HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (Handle == NULL)return FALSE;
SetConsoleScreenBufferSize(Handle, coord);
if(!SetConsoleWindowInfo(Handle, TRUE, &Rect)) return FALSE;
return TRUE;
}
std::array<std::array<CHAR, WIDTH+1>, HEIGHT> Screen;//WIDTH+1 = prevent cout from undefined behaviour
void Putchars(CHAR x){
for(int row = 0; row < HEIGHT; ++row){
std::fill(Screen[row].begin(),Screen[row].end(),x);
Screen[row].at(WIDTH) = 0;//here = prevent cout from undefined behaviour
}
}
void ShowFrame(DWORD delay = 0, bool fPutchars = false, CHAR x = 0){
if(fPutchars)Putchars(x);
if(delay)Sleep(delay);
//system("CLS");
_COORD coord;
coord.X = 0;
for(int row = 0; row < HEIGHT; ++row)
{
coord.Y = row;
FillConsoleOutputCharacterA(buffer,Screen[row].data()[0],100,coord,NULL);
}
}
int _tmain(int argc, _TCHAR* argv[]){//sould execute @~63 fps, yet it executes @~3-4 fps
SetWindow(WIDTH, HEIGHT);
current = GetStdHandle (STD_OUTPUT_HANDLE);
buffer = CreateConsoleScreenBuffer (
GENERIC_WRITE,
0,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
SetConsoleActiveScreenBuffer (buffer);
if(SetWindow(WIDTH,HEIGHT)){
for(CHAR i = 219; i != 0; --i)
ShowFrame(250,true, i);
}
CloseHandle (buffer); //clean up
return 0;
}
and it seems to work very fast 🙂
Just glancing at your code, you’re spawning a separate program (“CLS”) once per frame. That’s horrendously slow all by itself.
Contrary to some of the comments, the Windows console is capable of at least fairly reasonable speed if used even close to correctly (as in: you can update the console faster than any monitor can display the data).
Just for reference, here’s a version of John Conway’s Game of Life written for the Windows console. For timing purposes, it just generates a random starting screen and runs for 2000 generations, then stops. On my machine, it does 2000 generations in about 2 seconds, or around 1000 frames per second (useless, since a typical monitor can only update at around 60-120 Hz). Under 32-bit Windows with a full-screen console, it can roughly double that (again, at least on my machine). I’m pretty sure with a little work, this could be sped up some more, but I’ve never seen any reason to bother.
If nothing else, this has a
ClrScrnfunction you may find handy.