1. 程式人生 > >C語言 雙緩衝控制檯防閃屏技術

C語言 雙緩衝控制檯防閃屏技術

  對於這個東西相信大家非常陌生,因為現在除了學“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

  好啦,這就搞定啦,下篇文章就用這個來實現貪吃蛇吧!

  參考文章:http://m.bubuko.com/infodetail-582228.html