C语言 双缓冲控制台防闪屏技术

来源:互联网 发布:在虚拟机上安装mac os 编辑:程序博客网 时间:2024/06/05 23:42

  对于这个东西相信大家非常陌生,因为现在除了学“C语言”和“数据结构”这些基础课程的大学生,基本没人会用到控制台了。哪怕是用到,也不会关心它闪不闪屏的问题。

  但在一种特殊的情况下需要用到,那就是写“贪吃蛇”这个游戏的时候……

  贪吃蛇游戏的设计原理,就是不断的重复”擦除->显示”控制台打印的内容,显示的内容由时间和用户的输入做出相应的变化。

  控制台的擦除会用到如下语句:

system("cls");

  也正是这一语句导致了闪屏,下面放出一套闪瞎眼的代码

#include "stdio.h"#include "stdlib.h"#define LENGTH 15void 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();    }}

  具体原因可见如下的程序控制台显示流程图

这里写图片描述

  即控制台从键盘或者程序命令中获得要输出的数据,然后输出到显示缓冲区进行输出。在这个流程之中,假设程序要打一万个 *,则第一个 * 的输出时间和第一万个 * 的输出时间是有差别的。而假设我们要程序去显示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 15HANDLE 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();    }}

  好啦,这就搞定啦,下篇文章就用这个来实现贪吃蛇吧!

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