C語言 雙緩衝控制檯防閃屏技術
阿新 • • 發佈:2018-11-30
對於這個東西相信大家非常陌生,因為現在除了學“C語言”和“資料結構”這些基礎課程的大學生,基本沒人會用到控制檯了。哪怕是用到,也不會關心它閃不閃屏的問題。
但在一種特殊的情況下需要用到,那就是寫“貪吃蛇”這個遊戲的時候……
貪吃蛇遊戲的設計原理,就是不斷的重複”擦除->顯示”控制檯列印的內容,顯示的內容由時間和使用者的輸入做出相應的變化。
控制檯的擦除會用到如下語句:
system("cls");
- 1
也正是這一語句導致了閃屏,下面放出一套閃瞎眼的程式碼
#include "stdio.h"
#include "stdlib.h"
#define LENGTH 15
void show()
{
system("cls");
int i, j;
for (i = 0; i < LENGTH; i++)
{
for (j = 0; j < LENGTH; j++)
{
printf("* ");
}
printf("\n");
}
}
void main()
{
while (1)
{
show();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
具體原因可見如下的程式控制臺顯示流程圖
即控制檯從鍵盤或者程式命令中獲得要輸出的資料,然後輸出到顯示緩衝區進行輸出。在這個流程之中,假設程式要打一萬個 *,則第一個 * 的輸出時間和第一萬個 * 的輸出時間是有差別的。而假設我們要程式去顯示15*15的 * ,顯示完後馬上擦除,再來一次,則顯示緩衝區不滿15*15的 * 的狀態會居多,因此就造成了閃爍
針對於貪吃蛇這個遊戲,有一種比較取巧的解決方法,就是呼叫“SetConsoleCursorPosition”這個控制檯API去動態設定游標,來列印和覆蓋掉一些資料,以實現區域性的重新整理,也就是說原來可能需要重繪一萬個字元現在可能只需要重繪1000個,這樣可以減少閃爍的可能性。但該實現方法依然沒能躲過上面的流程出現的問題,隨著重繪的字元增多,閃爍的概率也在加大,因此也不算是好的解決方法。
那麼有沒有終極一點的解決方法呢?有,那就是我們在遊戲繪圖時用到吐的雙緩衝技術,原理見下圖
說簡單也簡單
1. 將要輸出的資料寫在緩衝區一(寫的過程中顯示的是緩衝區二的內容)
2.顯示緩衝區一的內容
3.將要輸出的資料寫在緩衝區二(寫的過程中顯示的是緩衝區一的內容)
4.顯示緩衝區二的內容 ,回到第1步
示例程式碼如下:
/*
* File : myRetroSnaker.cpp
* Author : weixinhum
* Date : 2017.5.16
* Function : Snake game of overcoming the splash screen
*/
#include "stdio.h"
#include "stdlib.h"
#include <Windows.h>
#define LENGTH 15
HANDLE hOutput, hOutBuf;//控制檯螢幕緩衝區控制代碼
COORD coord = { 0,0 };
//雙緩衝處理顯示
DWORD bytes = 0;
char data[LENGTH][LENGTH];
void show()
{
int i, j;
for (i = 0; i < LENGTH; i++)
{
for (j = 0; j < LENGTH; j++)
{
data[i][j]='*';
}
}
for (i = 0; i < LENGTH; i++)
{
coord.Y = i;
WriteConsoleOutputCharacterA(hOutBuf, data[i], LENGTH, coord, &bytes);
}
//設定新的緩衝區為活動顯示緩衝
SetConsoleActiveScreenBuffer(hOutBuf);
Sleep(500);
for (i = 0; i < LENGTH; i++)
{
for (j = 0; j < LENGTH; j++)
{
data[i][j] = '-';
}
}
for (i = 0; i < LENGTH; i++)
{
coord.Y = i;
WriteConsoleOutputCharacterA(hOutput, data[i], LENGTH, coord, &bytes);
}
//設定新的緩衝區為活動顯示緩衝
SetConsoleActiveScreenBuffer(hOutput);
Sleep(500);
}
void main()
{
//建立新的控制檯緩衝區
hOutBuf = CreateConsoleScreenBuffer(
GENERIC_WRITE,//定義程序可以往緩衝區寫資料
FILE_SHARE_WRITE,//定義緩衝區可共享寫許可權
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
hOutput = CreateConsoleScreenBuffer(
GENERIC_WRITE,//定義程序可以往緩衝區寫資料
FILE_SHARE_WRITE,//定義緩衝區可共享寫許可權
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
//隱藏兩個緩衝區的游標
CONSOLE_CURSOR_INFO cci;
cci.bVisible = 0;
cci.dwSize = 1;
SetConsoleCursorInfo(hOutput, &cci);
SetConsoleCursorInfo(hOutBuf, &cci);
while (1)
{
show();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82