드디어 마지막 헤더파일이다.
이번엔 콘솔 화면을 제어하기 위한 Windows API 함수들과 함께한다.
저수준의 제어를 하지 않고 화면에 출력할 경우,
80컬럼 25라인째 되는 우측 하단에 한 문자를 찍는 순간 한 줄 자동 스크롤되어버리는걸 경험하게 될 것이다.
이 코드들을 통해서 화면전체를 마음껏 쓸 수 있게 되며,
고수준 함수에 비해 엄청난 출력속도를 보여줄 것이다.
//------------------------------+---------------------------------------------------------------
#include <conio.h> // _kbhit
#include <Windows.h>
// 기본 콘솔 크기는 80 컬럼, 25 라인
template< int x_size = 80, int y_size = 25 >
class SCREEN
{
char buffer[ 8192 ];
HANDLE handle;
// 화면의 특정 좌표로 커서를 옮기기 ( gotoxy )
// 그전까지 버퍼링 된 출력을 내보내야 ( fflush ) 의도대로 동작될 것이다.
void go( const short x, const short y ) const
{
fflush( stdout );
SetConsoleCursorPosition( handle, { x, y } );
}
// 출력할 색상 정하기
auto color( const u8 c ) const
{
fflush( stdout );
SetConsoleTextAttribute( handle, c );
return c;
}
// 창 크기 조절하기
void resize( short w, short h ) const
{
COORD coord { w, h };
SetConsoleScreenBufferSize( handle, coord );
SMALL_RECT rect = { 0,0, w - 1, h - 1 };
SetConsoleWindowInfo( handle, TRUE, &rect );
}
public:
// 화면에 그릴 페이지를 하나 만들어 두자. 일종의 off_screen buffer 구실을 한다.
PAGE< x_size, y_size > top_page;
///-------------------------+---------------------------------------------------------------
// 전체화면 모드를 지원할 수 있지만, 뭐 별로 쓸모 없을것 같다. 커서는 안보이게 해두자. 게임만들꺼니까.
CRE SCREEN( bool full_screen = false )
: handle( GetStdHandle( STD_OUTPUT_HANDLE ) )
{
setvbuf( stdout, (char*)&buffer, _IOFBF, sizeof buffer );
if( full_screen )
SetConsoleDisplayMode( handle, CONSOLE_FULLSCREEN_MODE, NULL );
SetConsoleMode( handle, 0x0008 ); // DISABLE_NEWLINE_AUTO_RETURN
cursor( false );
}
auto xsize() const { return x_size; }
auto ysize() const { return y_size; }
void cursor( const bool show ) const
{
CONSOLE_CURSOR_INFO info = { 1, show };
SetConsoleCursorInfo( handle, &info );
}
// 임시버퍼를 통해 실제 출력하는 함수. 모아서 버리기~
void flush()
{
resize( x_size, y_size );
u8 old_color = color( top_page.buffer[ 0 ].color );
for( int y = 0; y < y_size; ++y )
{
go( 0, y );
TEX* scanline = top_page.cells[ y ];
for( int x = 0; x < x_size; ++x )
{
if( old_color != scanline[ x ].color )
old_color = color( scanline[ x ].color );
putchar( scanline[ x ].letter );
}
}
fflush( stdout );
}
// fade in / out 을 지원하기 위한 함수. 뭐 별거 없다.
template< class F >
void fade( const int delay, F operation )
{
top_page.gray();
flush();
for( int i = 0; i < 4; ++i )
{
::Sleep( delay );
operation();
flush();
}
}
void fadeout( const int delay = 1000 )
{
fade( delay, [ this ]{ top_page.lighten(); } );
}
void fadein( const int delay = 1000 )
{
fade( delay, [ this ]{ top_page.darken(); } );
}
// 키입력을 대기하면서 뭔가 주어진 일을 하다가 입력받은 키를 리턴한다.
template< class F = void(*)() >
auto waitfor_press( F f = []{} )
{
while( !_kbhit() ) f();
return getchar();
}
};
//==============================================================================================
쉽죠~ 헤더파일은 끝났으니 바로 예제로 넘어가자.
퍄퍄 감사합니다 - dc App