对于这个东西相信大家非常陌生,因为现在除了学“C语言”和“数据结构”这些基础课程的大学生,基本没人会用到控制台了。哪怕是用到,也不会关心它闪不闪屏的问题。
但在一种特殊的情况下需要用到,那就是写“贪吃蛇”这个游戏的时候……
贪吃蛇游戏的设计原理,就是不断的重复”擦除->显示”控制台打印的内容,显示的内容由时间和用户的输入做出相应的变化。
控制台的擦除会用到如下语句:
system("cls");
也正是这一语句导致了闪屏,下面放出一套闪瞎眼的代码
#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步
示例代码如下:
#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
好啦,这就搞定啦,下篇文章就用这个来实现贪吃蛇吧!