贪吃蛇-双缓冲Debug版

控制台可以有多个屏幕缓冲区,但只能有一个活动屏幕缓冲区,这个就叫ActiveScreen。
可以访问非活动屏幕缓冲区进行读取和写入,但只显示活动屏幕缓冲区。 若要使新屏幕缓冲区成为活动屏幕缓冲区,使用 SetConsoleActiveScreenBuffer 函数。(轮流成为活动显示区)
要用到的函数:
1)CreateConsoleScreenBuffer:Creates a console screen buffer.
2)WriteConsoleOutputCharacterA:指定一个缓存区,将需要输出的内容(这规定的类型是字符数组)输出到控制台,向控制台屏幕缓冲区的连续单元格内复制一组字符.
3)SetConsoleActiveScreenBuffer:把生成的的屏幕缓冲区设置成活动屏幕缓冲区,切换两个缓存的。在这里插入图片描述

有些 Win32 控制台函数使用的是预定义的数据结构,包括 COORD 和 SMALL_RECT。COORD 结构包含的是控制台屏幕缓冲区内字符单元格的坐标。坐标原点(0, 0)位于左上角单元格
HANDLE:句柄,是WINDOWS用来表示对象的,是一个通用句柄表示。
在WINDOWS程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源时为他们分配内存,并返回标示这些资源的标示号,即句柄。
但是如果这些资源的位置变了呢?
HANDLE是固定的,不会变,但是对象的地址会变,当对象在内存中的位置发生改变后,我们不能通过之前的对象指针找到对象。HANDLE能用来记录对象的最新地址。

参数 hConsoleOutput   控制台屏幕缓冲区的句柄。 dwSize   使用 COORD 来指定控制台屏幕缓冲区的宽度(X)和高度(Y),指定的宽度和高度不能小于当前窗口的字符宽度和字符高度。
上一章节的闪烁版贪吃蛇:

#include<iostream>;
#include<windows.h>;
#include<conio.h>;
using namespace std;

bool gameOver;
const int width = 20;
const int height = 20;
int x, y, fruitX, fruitY, score;
int tailX[100], tailY[100];
int nTail = 1;
int i, j;
enum eDirection { STOP = 0, LEFT, RIGHT, UP, DOWN };
eDirection dir;


void Initial()
{
	gameOver = false;
	dir = STOP;
	x = width / 2;
	y = height / 2;
	fruitX = rand() % width;
	fruitY = rand() % height;
}
void Draw()
{
	system("cls");
	HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
	int textColor = 0x06;
	SetConsoleTextAttribute(h, textColor);
	for (int i = 0; i < width + 2; i++)
		cout << "1";
	cout << endl;
	for (int j = 0; j < height; j++)
	{
		for (int i = 0; i < width; i++)
		{
			if (i == 0)
				cout << "2";
			if (i == x && j == y) {
				textColor = 0X0a;
				SetConsoleTextAttribute(h, textColor);
				cout << "O";
			}

			else if (i == fruitX && j == fruitY)
			{
				textColor = 0x084;
				SetConsoleTextAttribute(h, textColor);
				cout << "F";
			}
			else
			{
				bool FlagPrint = false;
				for (int k = 1; k < nTail; k++)
				{
					//todo:画尾巴
					if (tailX[k] == i && tailY[k] == j)
					{
						cout << "o";
						FlagPrint = true;
					}
				}
				textColor = 0x06;
				SetConsoleTextAttribute(h, textColor);
				if (!FlagPrint)
					cout << " ";
			}
			if (i == width - 1)
				cout << "3";

		}
		cout << endl;

	}
	for (i = 0; i < width + 2; i++)
		cout << "4";
	cout << endl;
}

void Input()
{
	if (_kbhit())
	{
		switch (_getch())
		{
		case'a':
			dir = LEFT;
			x--;

			break;
		case'd':
			dir = RIGHT;
			x++;

			break;
		case'w':
			dir = UP;
			y--;

			break;
		case's':
			dir = DOWN;
			y++;

			break;
		case'x':
			gameOver = true;
			break;
		default:
			break;
		}
	}
}
void Logic()
{
	for (i = nTail; i >= 1; i--)
	{
		tailX[i] = tailX[i - 1];
		tailY[i] = tailY[i - 1];

	}
	tailX[0] = x;
	tailY[0] = y;

	cout << tailY[1];
	cout << tailX[1];

	for (int i = 0; i < nTail; i++)
		if (tailX[i] == x && tailY[i] == y)
			gameOver == true;

	if (x > width || x<0 || y>height || y < 0)
		gameOver = true;
	/*if (x >= width) x = 0;
	else if (x < 0)
		x = width - 1;
	if (y >= height)y = 0;
	else if (y < 0)
		y = height - 1;*/
	if (x == fruitX && y == fruitY)
	{
		score += 10;
		fruitX = rand() % width;
		fruitY = rand() % height;
		nTail++;

	}

}

int main()
{


	Initial();
	while (!gameOver)
	{
		Draw();
		Input();

		Logic();
	}

	system("pause");
	return 0;
}



在这里插入图片描述

闪屏无缓冲版如上。
再添加一些双缓冲设置后,
我们慢慢看到底是哪里出了问题。

我们把Draw2()写入

void Draw2()
{
	cout << "进入draw2()循环";
	WORD textColor = 0X06;///墙体的颜色
	short j=0;
	COORD coord_wall;
	int currentLine = 0;
	WORD textColor_Head = 0X0a;
	WORD textColor_Fruit = 0x084;
	WORD textColor_Tail = 0x084;
	WORD textColor_blank = 0x06;

	for (int i = 0; i < width - 2; i++)
	{
		coord_wall.X = i;
		coord_wall.Y = currentLine;
		WriteConsoleOutputAttribute(hOutBuf, &textColor, 1, coord_wall, &bytes);
		WriteConsoleOutputAttribute(hOutput, &textColor, 1, coord_wall, &bytes);
		ScreenData[currentLine][i] = '1';
	}
	currentLine++;
	cout << "打印1墙";
	for ( j = 0; j < height; j++)
	{
		for (int i = 0; i < width; i++)
		{
			if ( i == 0)
			{
				coord_wall.X = i;
				coord_wall.Y = currentLine + j;
				WriteConsoleOutputAttribute(hOutBuf, &textColor, 1, coord_wall, &bytes);
				WriteConsoleOutputAttribute(hOutput, &textColor, 1, coord_wall, &bytes);
				ScreenData[currentLine+j][i] = '2';
				
			}
			cout << "打印2墙";
			if (i == x && j == y) {
				coord_wall.X = i;
				coord_wall.Y = currentLine + j;
				WriteConsoleOutputAttribute(hOutBuf, &textColor_Head, 1,coord_wall, &bytes);
				WriteConsoleOutputAttribute(hOutput, &textColor_Head, 1, coord_wall, &bytes);
				ScreenData[currentLine + j][i] = 'O';
			}
			else if (i == fruitX && j == fruitY)
			{
				coord_wall.X = i;
				coord_wall.Y= currentLine + j;
				WriteConsoleOutputAttribute(hOutBuf, &textColor_Fruit, 1, coord_wall, &bytes);
				WriteConsoleOutputAttribute(hOutput, &textColor_Fruit, 1, coord_wall, &bytes);
				ScreenData[currentLine + j][i] = 'F';

			}
			else
			{
				bool FlagPrint = false;
				for (int k = 1; k < nTail; k++)
				{
					//todo:画尾巴
					if (tailX[k] == i && tailY[k] == j)
					{
						coord_wall.X = i;
						coord_wall.Y = currentLine + j;
						WriteConsoleOutputAttribute(hOutBuf, &textColor_Tail, 1, coord_wall, &bytes);
						WriteConsoleOutputAttribute(hOutput, &textColor_Tail, 1, coord_wall,&bytes);
						ScreenData[currentLine + j][i] = 'o';
						FlagPrint = true;
				
					}
				}
				
				if (!FlagPrint)
				{
					coord_wall.X = i;
					coord_wall.Y = currentLine + j;
					WriteConsoleOutputAttribute(hOutBuf, &textColor_blank, 1, coord_wall, &bytes);
					WriteConsoleOutputAttribute(hOutput, &textColor_blank, 1, coord_wall, &bytes);
					ScreenData[currentLine + j][i] = ' ';
	
				}
					
			}
			if (i == width - 1)
			{
				coord_wall.X = i;
				coord_wall.Y = currentLine + j;
				WriteConsoleOutputAttribute(hOutBuf, &textColor, 1, coord_wall, &bytes);
				WriteConsoleOutputAttribute(hOutput, &textColor, 1, coord_wall, &bytes);
				ScreenData[currentLine + j][i] = '3';
			}

		}
		cout << endl;
		
	}
	cout << "循环结束";
	///short j = height;
	for (int i = 0; i < width + 2; i++)
	{
		coord_wall.X = i;
		coord_wall.Y = currentLine + j;
		WriteConsoleOutputAttribute(hOutBuf, &textColor, 1, coord_wall, &bytes);
		WriteConsoleOutputAttribute(hOutput, &textColor, 1, coord_wall, &bytes);
		ScreenData[currentLine + j][i] = '4';

	}
	currentLine++;
	sprintf_s(ScreenData[currentLine], "游戏得分:%4d", score);

}

然后进行报错
把输出行改成:

	cout<<ScreenData[currentLine], "游戏得分:%4d", score;

Draw2()不再报错。
(虽然也不知道为啥)
打印结果:Draw()和Draw2()交替打印
在这里插入图片描述
在这里插入图片描述
真不错。黄色是DRAW()的结果,白色是Draw2()结果。
我们把用来检测的汉字删掉。int main()里面同时打印draw()和draw2()
在这里插入图片描述
看的出来,画面闪烁非常巨大,但是所幸!没有报错运行正常。太不容易了。
接下来我们加入Show_doublebuffer()函数。展示双缓冲的功效。

void Show_doublebuffer()
{
	int i;
	Draw2();
	if (bufferSwapFlag == false)
	{
		bufferSwapFlag = true;
		for (i = 0; i < height + 5; i++)
		{
			coord.Y = i;
			WriteConsoleOutputCharacterA(hOutBuf, ScreenData[i], width, coord, &bytes);
		}
		SetConsoleActiveScreenBuffer(hOutBuf);
	}
	else
	{
		bufferSwapFlag = false;
		for (i = 0; i < height + 5; i++)
		{
			coord.Y = i;
			WriteConsoleOutputCharacterA(hOutput, ScreenData[i], width, coord, &bytes);
		}
		SetConsoleActiveScreenBuffer(hOutput);
	}

}

这里我们又遇到了问题:在这里插入图片描述
本应该显示的区域变成了一片空白。但在Draw2()里面cout<<screendata时,所出现的数组时正确的。所以我合理推测,应该是这个函数出现了问题,导致无法正确显示双缓冲功能。
然后我惊奇的发现。我竟然在Initial()里面没有写完整。
在这里插入图片描述
补充完整之后,代码运行结果如图:
在这里插入图片描述
画面基本不再闪烁。双缓冲暂时成功。
问题:1.缺少墙体2号
2.游戏得分打印位置出错

#include<iostream>;
#include<windows.h>;
#include<conio.h>;
#define _CRT_SECURE_NO_DEPRECATE

using namespace std;

HANDLE hOutput, hOutBuf;
COORD coord = { 0,0 };
DWORD bytes = 0;
bool BufferSwapFlag = false;
bool gameOver;
bool bufferSwapFlag;
const int width = 20;
const int height = 20;
int x, y, fruitX, fruitY, score;
int tailX[100], tailY[100];
int nTail = 1;
int i, j;
enum eDirection { STOP = 0, LEFT, RIGHT, UP, DOWN };
eDirection dir;
char ScreenData[width + 5][height + 5];

void Initial()
{
	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);
	gameOver = false;
	dir = STOP;
	x = width / 2;
	y = height / 2;
	fruitX = rand() % width;
	fruitY = rand() % height;
}
/*
void Draw()
{
	system("cls");
	HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
	int textColor = 0x06;
	SetConsoleTextAttribute(h, textColor);
	for (int i = 0; i < width + 2; i++)
		cout << "1";
	cout << endl;
	for (int j = 0; j < height; j++)
	{
		for (int i = 0; i < width; i++)
		{
			if (i == 0)
				cout << "2";
			if (i == x && j == y) {
				textColor = 0X0a;
				SetConsoleTextAttribute(h, textColor);
				cout << "O";
			}

			else if (i == fruitX && j == fruitY)
			{
				textColor = 0x084;
				SetConsoleTextAttribute(h, textColor);
				cout << "F";
			}
			else
			{
				bool FlagPrint = false;
				for (int k = 1; k < nTail; k++)
				{
					//todo:画尾巴
					if (tailX[k] == i && tailY[k] == j)
					{
						cout << "o";
						FlagPrint = true;
					}
				}
				textColor = 0x06;
				SetConsoleTextAttribute(h, textColor);
				if (!FlagPrint)
					cout << " ";
			}
			if (i == width - 1)
				cout << "3";

		}
		cout << endl;

	}
	for (i = 0; i < width + 2; i++)
		cout << "4";
	cout << endl;
}
*/
void Draw2()
{
	//cout << "进入draw2()循环";
	WORD textColor = 0X06;///墙体的颜色
	short j = 0;
	COORD coord_wall;
	int currentLine = 0;
	WORD textColor_Head = 0X0a;
	WORD textColor_Fruit = 0x084;
	WORD textColor_Tail = 0x084;
	WORD textColor_blank = 0x06;

	for (int i = 0; i < width - 2; i++)
	{
		coord_wall.X = i;
		coord_wall.Y = currentLine;
		WriteConsoleOutputAttribute(hOutBuf, &textColor, 1, coord_wall, &bytes);
		WriteConsoleOutputAttribute(hOutput, &textColor, 1, coord_wall, &bytes);
		ScreenData[currentLine][i] = '1';
	}
	currentLine++;
	//cout << "打印1墙";
	for (j = 0; j < height; j++)
	{
		for (int i = 0; i < width; i++)
		{
			if (i == 0)
			{
				coord_wall.X = i;
				coord_wall.Y = currentLine + j;
				WriteConsoleOutputAttribute(hOutBuf, &textColor, 1, coord_wall, &bytes);
				WriteConsoleOutputAttribute(hOutput, &textColor, 1, coord_wall, &bytes);
				ScreenData[currentLine + j][i] = '2';

			}

			if (i == x && j == y) {
				coord_wall.X = i;
				coord_wall.Y = currentLine + j;
				WriteConsoleOutputAttribute(hOutBuf, &textColor_Head, 1, coord_wall, &bytes);
				WriteConsoleOutputAttribute(hOutput, &textColor_Head, 1, coord_wall, &bytes);
				ScreenData[currentLine + j][i] = 'O';
			}
			else if (i == fruitX && j == fruitY)
			{
				coord_wall.X = i;
				coord_wall.Y = currentLine + j;
				WriteConsoleOutputAttribute(hOutBuf, &textColor_Fruit, 1, coord_wall, &bytes);
				WriteConsoleOutputAttribute(hOutput, &textColor_Fruit, 1, coord_wall, &bytes);
				ScreenData[currentLine + j][i] = 'F';

			}
			else
			{
				bool FlagPrint = false;
				for (int k = 1; k < nTail; k++)
				{
					//todo:画尾巴
					if (tailX[k] == i && tailY[k] == j)
					{
						coord_wall.X = i;
						coord_wall.Y = currentLine + j;
						WriteConsoleOutputAttribute(hOutBuf, &textColor_Tail, 1, coord_wall, &bytes);
						WriteConsoleOutputAttribute(hOutput, &textColor_Tail, 1, coord_wall, &bytes);
						ScreenData[currentLine + j][i] = 'o';
						FlagPrint = true;

					}
				}

				if (!FlagPrint)
				{
					coord_wall.X = i;
					coord_wall.Y = currentLine + j;
					WriteConsoleOutputAttribute(hOutBuf, &textColor_blank, 1, coord_wall, &bytes);
					WriteConsoleOutputAttribute(hOutput, &textColor_blank, 1, coord_wall, &bytes);
					ScreenData[currentLine + j][i] = ' ';

				}

			}
			if (i == width - 1)
			{
				coord_wall.X = i;
				coord_wall.Y = currentLine + j;
				WriteConsoleOutputAttribute(hOutBuf, &textColor, 1, coord_wall, &bytes);
				WriteConsoleOutputAttribute(hOutput, &textColor, 1, coord_wall, &bytes);
				ScreenData[currentLine + j][i] = '3';
			}

		}
		cout << endl;

	}
	//cout << "循环结束";
	///short j = height;
	for (int i = 0; i < width + 2; i++)
	{
		coord_wall.X = i;
		coord_wall.Y = currentLine + j;
		WriteConsoleOutputAttribute(hOutBuf, &textColor, 1, coord_wall, &bytes);
		WriteConsoleOutputAttribute(hOutput, &textColor, 1, coord_wall, &bytes);
		ScreenData[currentLine + j][i] = '4';
		

	}
	currentLine++;
	sprintf_s( ScreenData[currentLine],"游戏得分:%4d", score);

}
void Show_doublebuffer()
{
	int j;
	Draw2();
	if (bufferSwapFlag == false)
	{
		bufferSwapFlag = true;
		for (j = 0; j < height + 5; j++)
		{
			coord.Y = j;
			WriteConsoleOutputCharacterA(hOutBuf, ScreenData[j], width, coord, &bytes);
		}
		SetConsoleActiveScreenBuffer(hOutBuf);
	}
	else
	{
		bufferSwapFlag = false;
		for (j = 0; j < height + 5; j++)
		{
			coord.Y = j;
			WriteConsoleOutputCharacterA(hOutput, ScreenData[j], width, coord, &bytes);
		}
		SetConsoleActiveScreenBuffer(hOutput);
	}

}

void Input()
{
	if (_kbhit())
	{
		switch (_getch())
		{
		case'a':
			dir = LEFT;
			x--;

			break;
		case'd':
			dir = RIGHT;
			x++;

			break;
		case'w':
			dir = UP;
			y--;

			break;
		case's':
			dir = DOWN;
			y++;

			break;
		case'x':
			gameOver = true;
			break;
		default:
			break;
		}
	}
}
void Logic()
{
	for (i = nTail; i >= 1; i--)
	{
		tailX[i] = tailX[i - 1];
		tailY[i] = tailY[i - 1];

	}
	tailX[0] = x;
	tailY[0] = y;

	for (int i = 0; i < nTail; i++)
		if (tailX[i] == x && tailY[i] == y)
			gameOver == true;

	if (x > width || x<0 || y>height || y < 0)
		gameOver = true;
	if (x == fruitX && y == fruitY)
	{
		score += 10;
		fruitX = rand() % width;
		fruitY = rand() % height;
		nTail++;

	}

}

int main()
{


	Initial();
	while (!gameOver)
	{

		Show_doublebuffer();
		Input();

		Logic();

	}

//	system("pause");
	return 0;
}



  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值