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

  • 10
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值