双缓冲的实现

  最近用C语言开发一些小游戏玩,遇上了“闪屏”的问题。问题一般源于计算机处理数据的时间影响到计算机显示。gotoXY()可以解决一般闪屏的问题,但是gotoXY()是将光标移回原点重新输出一遍结果,有时候会出现跟预期结果不一致的情况。进而去了解了双缓冲解决闪屏的办法。

  这是默认的控制台屏幕显示结构:

              

    从该图可以看出,如果处理的数据量过大时间过长,到达缓冲区的先后顺序会不一致,这就出现了“闪屏”的现象。

  解决方案:使用双缓冲技术:

                

    可以看出我们建立了一个新的缓冲区,默认缓冲区在将所有数据处理完毕后再将数据传递到新建的缓冲区之后直接显示到屏幕,有效的解决了数据不一致出现闪屏的情况。

  控制台上具体实现双缓冲的用例

     这是我写的一个简单的飞机游戏的源代码

    

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<Windows.h>
#include<tchar.h>
#pragma comment(lib,"winmm.lib")

int position_x, position_y;	//飞机坐标
int high, width;	//画面大小
int bullet_x, bullet_y;	//子弹位置
int enemy_x, enemy_y;	//敌机坐标
int score;

void gotoxy(int x,int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(handle,pos);
}

void startup()	//数据初始化
{
	high = 20;
	width = 40;
	position_x = width / 2;
	position_y = high / 2;
	bullet_x = -1;
	bullet_y = -1;
	enemy_x = position_x;
	enemy_y = 0;
	score = 0;
}

void show()
{
	//gotoxy(0, 0);
	system("cls");
	int i, j;
	for (j = 0; j < high; j++)
	{
		for (i = 0; i < width; i++)
		{
			if ((i == position_x)&&(j == position_y))
			{
				printf("*");
			}
			else if ((i==bullet_x)&&(j==bullet_y))
			{
				printf("|");
			}
			else if ((i == enemy_x) && (j==enemy_y))
			{
				printf("@");
			}
			else
			{
				printf(" ");;
			}
		}
		printf("\n");
	}
	printf("得分:%d", score);
}

void updateWithoutInput()	//与用户输入无关的更新
{
	if (bullet_y > -1)
	{
		bullet_y--;
	}

	static int Speed = 0;
	if (Speed < 10)
	{
		Speed++;
	}
	else
	{
		enemy_y++;
		Speed = 0;
	}

	if ((bullet_y == enemy_y)&&(bullet_x==enemy_x))
	{
		enemy_y = 0;
		enemy_x = rand()%width;
		bullet_y = -1;
		score = score + 1;
	}

	if (enemy_y > high)
	{
		enemy_y = 0;
		enemy_x = rand() % width;
	}
}

void updateWithInput()
{
	char input;
	if (_kbhit())
	{
		input = _getch();
		switch (input)
		{
		case 'w':
			position_y--;
			break;
		case 's':
			position_y++;
			break;
		case 'a':
			position_x--;
			break;
		case 'd':
			position_x++;
			break;
		case ' ':
			if (bullet_y > -1)
			{
				bullet_y--;
				break;
			}
			else
			{
				bullet_x = position_x;
				bullet_y = position_y - 1;
				break;
			}
		default:
			break;
		}
	}
}

void HideCursor(HANDLE handle)
{
	CONSOLE_CURSOR_INFO mycursor;
	GetConsoleCursorInfo(handle, &mycursor);
	mycursor.bVisible = false;
	SetConsoleCursorInfo(handle, &mycursor);
}

int main()
{	
	HANDLE ScreenDefault = GetStdHandle(STD_OUTPUT_HANDLE);

	//创建新的屏幕缓冲区
	HANDLE MyScreenBuf = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE
		, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);

	SetConsoleActiveScreenBuffer(MyScreenBuf);	//使新建的缓冲区为活动缓冲区
	HideCursor(ScreenDefault);
	HideCursor(MyScreenBuf);
	DWORD bytes = 0;
	COORD coord = { 0, 0 };
	char data[2048];

	mciSendString(_T("open music\\开场音乐.mp3 alias bkmusic"), NULL, 0, NULL);
	mciSendString(_T("play bkmusic"), NULL, 0, NULL);

	startup();
	while (true)
	{
		show();
		updateWithoutInput();
		updateWithInput();

		ReadConsoleOutputCharacterA(ScreenDefault, data, 2048, coord, &bytes);	//将默认缓冲区的数据保存至一个字符数组中
		WriteConsoleOutputCharacterA(MyScreenBuf, data, 2048, coord, &bytes);	//活动缓冲区读取该数组数据之后显示在屏幕上
	}
}

 

posted @ 2017-06-09 09:25  HarrietWong  阅读(462)  评论(0)    收藏  举报