怎么实现双缓冲双缓冲
求一个小的案例: 用c++实现可以实现双缓冲,不用很长,我对window.h库函数不了解 所以希望 有些注释。。。
或者帮忙看一下 下面的俄罗斯方块 代码
我设计的是在某一行满了后,将这一行消除掉,用的是重新打印整个操作区的页面,因为是重新一行一行的打印,所以会有闪动,
想用双缓冲实现一下图形界面重新打印的问题。。。
#include<iostream>
#include<Windows.h>
#include<conio.h>
#include<time.h>
using namespace std;
int score = 0; //分数
bool check = true; //检查方块还能不能下落
int point_X, point_Y; //操作区的数组的位置坐标
int time1 = 0; //睡眠时间
int scope[32][36] = { 0 }; //操作范围 这里注意一个方格是占两个横坐标的 29-4 为25行
int block[4][4] = { 0 }; //提示区的块数组
int Block[4][4] = { 0 }; //显示区的数组
int tmp[4][4] = { 0 }; //临时变量 用于旋转等操作时 的替代变量
int block0[4][4] = {{0,1,1,0},{0,0,1,0},{0,0,1,0},{0,0,0,0}};// 7 个方块 用数组表示 正“7”
int block1[4][4] = {{0,1,1,0},{0,1,0,0},{0,1,0,0},{0,0,0,0}};// 反“7”
int block2[4][4] = {{0,0,0,0},{0,0,1,1},{0,1,1,0},{0,0,0,0}};// 反“z”
int block3[4][4] = {{0,0,0,0},{1,1,0,0},{0,1,1,0},{0,0,0,0}};// 正“z”
int block4[4][4] = {{0,0,0,0},{0,1,0,0},{1,1,1,0},{0,0,0,0}};// 倒“T”
int block5[4][4] = {{0,0,0,0},{0,1,1,0},{0,1,1,0},{0,0,0,0}};// 正方形
int block6[4][4] = {{0,0,0,0},{0,0,0,0},{1,1,1,1},{0,0,0,0}};// 直条
//-----------函数声明------开始-------------
void console(); //设置窗口位置和大小,为了让程序运行得更好看
void gotoxy(int x, int y); //设置光标输出的位置函数 坐标不能为 负数
void my_print(); //设置页面
void Appear_random(); //产生随机块 并打印在 提示区
void Print(); //打印函数 根据 point_X 和point_Y点的位置就可以打印出来 该点在小方格的左上角
void DownBlock(); //向下移动 一位
void UpBblock(); //顺时针旋转 90度
void LeftBlock(); //向左移动一位
void RightBlock(); //向右移动一位
void OperAreaPrint(); //重新打印整个墙内部的所有小块
int clear(); //清除满的行
//-----------函数声明------开始-------------
//设置窗口位置和大小,为了让程序运行得更好看
void console()
{
//获取标准设备输出句柄
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO bInfo;//窗口缓冲区信息
GetConsoleScreenBufferInfo(hOut, &bInfo ); // 获取窗口缓冲区信息
SetConsoleTitle("俄罗斯方块 C版");//设置窗口的标题
COORD size = {180, 120}; //不能小于默认的(80, 25)
SetConsoleScreenBufferSize(hOut,size); // 重新设置缓冲区大小*/
SMALL_RECT rc = {0,0, 150, 100}; //不能大于缓冲区大小 上下左右的点
SetConsoleWindowInfo(hOut,true ,&rc); // 重置窗口大小
}
//设置光标输出的位置函数 坐标不能为 负数
void gotoxy(int x, int y)//x为 横坐标 y为纵坐标
{
HANDLE app;
COORD pos;
pos.X = x;
pos.Y = y;
app = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(app, pos);
}
//设置页面
void my_print()
{
system("color 70");//白底黑字
for(int i = 1; i < 30; i++)//共 29 行
cout<<"■\t\t\t\t■ | |"<< endl;//用tab键对齐 所以如开头的 "■\t"占8个字节
cout<<"■■■■■■■■■■■■■■■■■ |--------------------------------------|";//框架
gotoxy(36, 0);
cout<<"------------------右面--------------------"<< endl;
gotoxy(40, 3);
cout<<"分 数: "<< score<< endl;
gotoxy(36,6);
printf("下一个方块:");
gotoxy(36,14);
printf("操作方法:");
gotoxy(40,16);
printf("↑:旋转 ↓:沉淀");
gotoxy(40,18);
printf("→:右移 ←:左移");
}
//产生随机块 并打印在 提示区
void Appear_random()
{
srand((unsigned int)time(0));
if(rand()%7 == 0)
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j++)
block[i][j] = block0[i][j];
else if(rand()%7 == 1)
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j++)
block[i][j] = block1[i][j];
else if(rand()%2 == 0)
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j++)
block[i][j] = block2[i][j];
else if(rand()%7 == 3)
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j++)
block[i][j] = block3[i][j];
else if(rand()%7 == 4)
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j++)
block[i][j] = block4[i][j];
else if(rand()%7 == 5)
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j++)
block[i][j] = block5[i][j];
else
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j++)
block[i][j] = block6[i][j];
for(int i = 0; i < 4; i++) //打印出块
{
//gotoxy(44, 8+i); //将光标移动到 要打印的位置
int a = 44;
int b = 8+i;
for(int j = 0; j < 4; j++)
if(block[i][j] == 1)
{
gotoxy(a + j*2, b);
cout<<"■";
}
else
{
gotoxy(a + j*2, b);
cout<<" "; //将原来的块 覆盖
}
}
}
//打印函数 根据 point_X 和point_Y点的位置就可以打印出来 该点在小方格的左上角
void Print()
{
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
{
gotoxy(point_X + j*2, point_Y + i);//因为 一个小块占两个横坐标 所以要使 j*2
cout<<"■";
}
}
void DownBlock()
{
if(check == true)
return;
for(int i = 0; i < 4; i++) //将原来的小方块覆盖掉
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
{
gotoxy(point_X + j*2, point_Y + i);
cout<<" "; //将原来的小方块覆盖掉
}
for(int i = 0; i < 4; i++) //在其下面输出新的方块
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
{
gotoxy(point_X + j*2, point_Y + i + 1);
cout<<"■"; //在其下面输出新的方块
}
point_Y++; //注意:光标一定要在 块的左上角 跟踪 块落到那里了
//判断下落后 是否到底部
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
if(scope[point_Y + i + 1][point_X + j*2]==1 || point_Y + i > 27)
{
check = true; //到达底部
return ;
}
}
void UpBblock()//向右旋转 90度
{
//先将原来位置上的块覆盖掉
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
{
gotoxy(point_X + j*2, point_Y + i);
cout<<" "; //覆盖
}
//旋转
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
tmp[j][3-i] = Block[i][j];
//判断旋转后 是否出界
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(tmp[i][j] == 1)
{
if(point_X + j*2 < 2 || point_X+ j*2 > 30 ||
(!Block[i][j] && scope[point_Y+j*2][point_X+i]))
return;
}
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
Block[i][j] = tmp[i][j];
Print();
}
void LeftBlock()
{
//判断是否出界
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
if(point_X + j*2 < 4 || scope[point_Y + i][point_X + j*2 -2]==1)//左面的空间不够向左移动的
return;
//左移动一位
//将原来的覆盖掉
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
{
gotoxy(point_X + j*2, point_Y + i);
cout<<" ";
}
//移动
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
{
gotoxy(point_X + j*2 - 2, point_Y + i);
cout<<"■";
}
point_X -= 2;
}
void RightBlock()
{
//判断是否出界
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] ==1)
if(point_X + j*2 > 28 || scope[point_Y + i][point_X+j*2+2] == 1) //右面的空间不够向右移动的(注意边界 从32开始 到32结束)
return;
//右移动一位
//将原来的覆盖掉
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
{
gotoxy(point_X + j*2, point_Y + i);
cout<<" ";
}
//移动
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
{
gotoxy(point_X + j*2 + 2, point_Y + i);
cout<<"■";
}
point_X += 2;
}
void OperAreaPrint()
{
//遍历整个操作区 进行 打印
for(int i = 0; i < 29; i++)//行
{
for(int j = 2; j < 30; j += 2)
{
if (scope[i][j] == 1)
{
gotoxy(j, i);
cout<<"■";
}
else if(scope[i][j] != 1)
{
gotoxy(j, i);
cout<<" "; //将原来的 小方块 覆盖掉
}
}
}
gotoxy(40, 3);
cout<<"分 数: "<< score<< endl;
}
int clear() //清除满的行
{
int flag = 0; //标记 若为 1 代表需要清除 重新打印
//循环整个块 看是否都满了
for(int i = 0; i < 4; i++) //四行都要判断
{
for(int j = 4; j < 30; j += 2) //判断该行的所有空间 是否都有 块
{
if(scope[point_Y + i][j] == 0) //操作区没块存在
{
break;
}
else if(j == 28) //这一行 都有块
{
flag = 1;
score ++; //分数 加一
//清除这一行
for(int tmp = 2; tmp < 30; tmp++)
{
scope[point_Y + i][tmp] = 0; //将操作区的数组 置为 0
}
//这一行上面的 都向下移动一位
for(int a1 = point_Y + i; a1 > 0; a1--)
{
for(int a2 = 2; a2 < 30; a2 += 2)
scope[a1][a2] = scope[a1-1][a2];
}
break;
}
}
//重新打印操作区的 块
if(flag == 1)
OperAreaPrint();
}
return 0;
}
int main()
{
//设置页面
console();
my_print();
//产生随机块 并打印在 提示区
Appear_random();
//----开始游戏---进行循环----
while(1) //无限循环
{
//若没有正在下落的随机块时 产生随机块 并打印
if(check == true) //没有方块下落
{
check = false; //将标记值置为 false
//初始化每次 块开始下落时的操作区数组的坐标位置
point_X = 14; //横坐标从 14开始 注意 横坐标 两个字符位置代表一个小方格的位置
point_Y = 0; //纵坐标从 0 开始
//将提示区的块赋值给操作块 并在操作区打印出块
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
Block[i][j] = block[i][j];
Print(); //在操作区打印出 块
Appear_random(); //产生随机块
}
//若有输入键 就执行 输入键的操作
if(kbhit()) //检测是否有按键 若有 就执行
{
char key = getch(); //捕获按键
//判断按键 执行不同的操作
switch (key)
{
case 72: //上
UpBblock();
break;
case 75: //左
LeftBlock();
break;
case 77: //右
RightBlock();
break;
case 80: //下
DownBlock();
break;
default:
break;
}
}
//间隔一段时间 自动下移一位
Sleep(30); //间隔 30ms
if(++time1%30 == 0) //每隔30 个30ms就自动 下移一位
DownBlock();
//若没有正在下落的随机块 就把下落的方格块赋值给操作区的数组
if(check == true)
{
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j++)
if(Block[i][j] == 1)
scope[point_Y + i][point_X + 2*j] = Block[i][j];//将操作区的该点的值赋 为 1 代表已经有 块了
int k = clear(); //清除满的行
//if(k == 9)
// return -1;
OperAreaPrint();//打印操作区的块
}
}
return 0;
}
[此贴子已经被作者于2017-3-2 13:36编辑过]