|
花了好几天模仿了个 控制台俄罗斯方块,键盘操作是操网上的 ,但方块创建,移动,和消除都是自己想的,头发都掉了好几根,现在就差定时器定时下移了,这个定时器该怎么写
#include <windows.h>
#include <iostream>
#include <ctime>
#include <stdio.h>
#include <conio.h>
using namespace std;
class CPressKey{
public:
CPressKey(){};
~CPressKey(){};
void m_waitpresskey(); //等待玩家按键
enum keydir{
ESC=27,UP=72,RIGHT=77,DOWN=80,LEFT=75
};
int m_getDirKey();
private:
int m_dir;
};
void CPressKey::m_waitpresskey(){ //等待玩家按键
//等待玩家按下按键 必须两次才能得到真实按键
m_dir=getch();
m_dir=getch();
}
int CPressKey::m_getDirKey(){
switch(m_dir){
case ESC:case UP:case DOWN:case RIGHT:case LEFT:
return m_dir;
}
return 1;
}
class CBlocks{
public:
CBlocks(int,int,int,int,HANDLE,COORD); //游戏4个边界
~CBlocks();
void m_moveBlocks(int); //判断是否可以移动方块
//bool m_isBoom(int); //判断当前方块是否发生碰撞
//void m_clearBlocks(); //发生碰撞后消除满格那一行
int m_getcurblocks(); //得到当前方块种类
bool m_getgamestat();
private:
static const int m_maxblocks=7; //方块形状最多为7种 以下是方块形状
// □
//□□□
char const *m_f[4]={"23010111","32011101","23111010","32101110"};
//□□
// □□
char const *m_c[2]={"23110011","32011110"};
//□
//□□□
char const *m_a[4]={"23100111","32111010","23111001","32010111"};
// □
//□□□
char const *m_b[4]={"23001111","32101011","23111100","32110101"};
// □□
//□□
char const *m_d[2]={"23011110","32101101"};
//□□
//□□
char const *m_e[1]={"221111"};
//□□□□
char const *m_g[2]={"141111","411111"};
char const **m_blocks[m_maxblocks]={m_f,m_c,m_a,m_b,m_d,m_e,m_g};
int const m_num[m_maxblocks]={4,2,4,4,2,1,2}; //每种方块翻转类型
//api函数用的变量
HANDLE m_hd;
COORD m_pos;
//屏幕边界
int m_scr_top;
int m_scr_left;
//游戏4个边界
int m_Top;int m_Right;int m_Down;int m_Left;
bool m_isgameover; //游戏结束标志
//屏幕数组
char **screenarray;
int m_scrx; //方块在屏幕上出现列位置
int m_scry; //方块在屏幕上出现行位置
int m_memx; //方块在数组中出现行位置
int m_memy; //方块在数组上出现列位置
int m_curblock; //当前正操作方块
int m_curcolor; //方块当前颜色
int m_curblockstyle; //当前方块旋转的种类
int m_x; //当前操作方块行
int m_y; //当前操作方块列
//方向
enum keydir{
ESC=27,UP=72,RIGHT=77,DOWN=80,LEFT=75
};
private:
void m_setBlockStyle(); //旋转当前方块
void m_getRandBlock(); //随机得到要显示的方块
void m_showBlocks(int,int,bool);//方块要显示的y列,x行,显示方块
bool m_isCrossBorder(int); //判断是否越界
bool m_isBoom(int); //判断是否碰撞
void writemem(); //把方块写入屏幕对应的内存数组
void printmem(); //显示内存数组
void clearblocks(char* ,int); //动画方式消除屏幕上满行方块 并 把对应内存数组置0
int m_getCurBlocksValue(int); //根据参数返回当前方块内的值
void m_setCurBlocksXy(); //把当前方块行和列保存到 m_x,m_y中
bool isfull(char *); //判断当前方块组成行是否有满行
bool isNullLine(int const ); //判断指定行是否是空行
void fflush();
};
CBlocks::CBlocks(int t,int d,int l,int r,HANDLE hd,COORD pos)
:m_hd(hd),m_pos(pos){
//屏幕边界
m_scr_top=t;
m_scr_left=l;
//内存数组4个边界
m_Top=0;m_Down=d-t-1;
m_Left=0;m_Right=(r-2-l)/2;
//创建屏幕二维数数组
screenarray =new char*[m_Down];
for(int i=0;i<m_Down;i++)
screenarray[i] =new char[m_Right];
//对二维数组进行初始化
for(int i=0;i<m_Down;i++){
for(int j=0;j< m_Right;j++)
screenarray[i][j]=0;
}
m_isgameover=false; //游戏结束标志
srand(time(0));
m_getRandBlock(); //随机产生方块
//并在初始位置显示出来
m_showBlocks(m_scrx,m_scry,true); //初始是不可能满屏的所以直接把方块显示在左上角
}
int CBlocks::m_getcurblocks(){ //得到当前方块种类
m_getRandBlock(); //产生随机方块
return m_curblock; //返回方块种类
}
void CBlocks::m_getRandBlock(){ //产生随机方块
m_curblock=rand()% m_maxblocks; //指向当前方块
m_curcolor=1+rand()% m_maxblocks; //随机1-7种颜色
m_curblockstyle=0; //旋转样子设为默认
m_setCurBlocksXy(); //读取当前方块行和列值
//初始位置
m_scrx= m_scr_left+2; //方块在屏幕左边界向右第1列
m_scry= m_scr_top; //方块在屏幕上边界
m_memx=0; //内存中方块初始行位置
m_memy=0; //内存中方块初始列位置
}
//设置方向旋转样子
void CBlocks::m_setBlockStyle(){
if( m_curblockstyle+1 <m_num[m_curblock]) //如果方块可以旋转
m_curblockstyle++;
else
m_curblockstyle=0;
m_setCurBlocksXy(); //设置旋转后的当前方块行和列值
}
//第1方块列坐标,第2行坐标,第3个为true时显示方块,为false为擦除方块
void CBlocks::m_showBlocks(int startx,int starty,bool disp){
int start=0;
m_pos.X = startx;
m_pos.Y = starty;
char p[3]={"□"};
if(disp ==false){ //disp为false时用空格擦除方块
p[0]=' ';p[1]=' ';p[2]='\0';
}else{
SetConsoleTextAttribute(m_hd,FOREGROUND_INTENSITY | m_curcolor);
}
for(int i=0;i<m_x;i++){
for(int j=0;j<m_y;j++){
if(m_getCurBlocksValue(start)==1){ //方块内部为1时才打印 □ 为0时跳过
SetConsoleCursorPosition(m_hd, m_pos); //设置控制台光标输出的位置
cout<<p; //显示□ 或显示空格
}
m_pos.X=m_pos.X+2; //每个"□"占2位,所以列坐标必须+2
++start; //从指定位置读取数据
}
m_pos.X=startx; //还原列坐标位置
m_pos.Y++; //行坐标下移一行
}
}
void CBlocks::writemem(){
//把当前方块写入内存数组
int start=0;
for(int i=0;i<m_x;i++){
int n=0;
for(int j=0;j<m_y;j++){
if(m_getCurBlocksValue(start)==1){
screenarray[m_memx+i][m_memy+n]=m_curcolor;
}
++n;
++start;
}
}
printmem();
}
void CBlocks::m_moveBlocks(int dir){
if(dir==UP){ //向上旋转方块
int x1=m_y;int y1=m_x;
bool flag;
//如果行比列长(判断向下) 且 向下没有越界
if((x1>y1) && (m_memx+x1-1 < m_Down)){
for(int i=1;i<x1;i++){
for(int j=0;j<y1 ;j++){
flag=screenarray[m_memx+i][m_memy+j];
if(flag)
goto no1;
}
}
}else if((y1>x1)&& (m_memy+y1-1 < m_Right)){ //同上,只是对列 和向右边界判断
for(int i=1;i<y1;i++){
for(int j=0;j<x1 ;j++){
flag=screenarray[m_memx+j][m_memy+i];
if(flag)
goto no1;
}
}
}else //向下或向右越界 不作处理
return;
//没有越界且没有发生碰撞
m_showBlocks(m_scrx,m_scry,false); //先擦除当前方块
m_setBlockStyle(); //得到旋转后方块样子
m_showBlocks(m_scrx,m_scry,true); //在屏幕上显示旋转后的方块
no1: return;
}
bool d=m_isCrossBorder(dir); //根据当前按下方向键返回将要移动的方块是否越界
if(d==false && m_isBoom(dir)==false){ //没有越界 且 没有碰撞
m_showBlocks(m_scrx,m_scry,false); //擦除要移动方块
switch(dir){ //根据方向键进行移动
case RIGHT: //向右
m_scrx=m_scrx+2; //屏幕方块右移2格(每个方块占用2格移1格相当移2格)
++m_memy; //对应内存数组右移1格
break;
case DOWN: //向下
++m_scry; //屏幕方块向下移1格
++m_memx; //对应内存数组行+1
break;
case LEFT: //向左
m_scrx=m_scrx-2; //同向右
--m_memy; //对应内存数组列-1
break;
}
m_showBlocks(m_scrx,m_scry,true); //屏幕上显示移动后的方块
}else if((d==true && dir==DOWN) || (m_isBoom(dir)==true && dir==DOWN)){ //如果方向向下将发生越界 或者 向下将发生碰撞
if(d==false && m_isBoom(dir)==true && dir==DOWN){ //如果当前方块在初始行无法向下 游戏结束
if(m_memx== m_Top){
cout<<"游戏结束,无法继续"<<endl;
m_isgameover=true;
return;
}
}
writemem(); //把屏幕到边界或碰撞的方块写入数组
char *fullline=new char[m_x]; //用于记录满行位置数组
if(isfull(fullline)==true){ //如果当前方块有满行要进行消除
clearblocks(fullline,25); //消除屏幕上满行方块 并 把对应内存数组置0
//把当前方块内非空行移到空行
int aa,bb=-1; //aa指向空行,bb指向非空行
for(int i=m_x-1;i>=0;i--){
if(fullline[i]==1){ //空行?
aa=i; //aa指向空行
for(int j=aa-1;j>=0;j--){ //在空行上一行查找非空行
if(fullline[j]==0){
bb=j; //bb指向非空行
break; //找到非空行跳出for
}
}
if(bb ==-1)break;//-1表示没找到非空行 说明方块组成行内没有空行 继续判断方块上方有没方块
//否则把找到的非空行bb 移到aa指向的空行
for(int j=0;j<m_Right;j++){
screenarray[m_memx+aa][j]=screenarray[m_memx+bb][j];
screenarray[m_memx+bb][j]=0;
fullline[bb]=1; //移走后原先非空行变成空行
}
printmem();
//同时也对屏幕对应行相应处理
m_pos.X=m_scr_left+2;
m_pos.Y=m_scry+bb;
SetConsoleCursorPosition(m_hd, m_pos);
for(int j=0;j<m_Right;j++){
cout<<" "; //用空格清空
}
//把内存aa行内容 复制到屏幕aa行位置
m_pos.X=m_scr_left+2; //从每行第1列开始
m_pos.Y=m_scry+aa; //定位到屏幕a行对应的行
SetConsoleCursorPosition(m_hd, m_pos);
for(int j=0;j<m_Right;j++){
int c=screenarray[m_memx+aa][j];
if(c>0){
SetConsoleTextAttribute(m_hd, FOREGROUND_INTENSITY |c);
cout<<"□";
}else{
cout<<" ";
}
}
bb=-1; //bb重新设定成-1
}
}
//当前方块上面如果还有方块也要移动下来
aa=m_memx+aa;
bb=m_memx-1; //把bb指向当前方块上1行
do{
if(isNullLine(bb)){ //如果bb是空行
break; //说明当前方块之前没有其它方块
}else{ //不是空行,表示当前方块之前还有方块
//把内存非空行bb 移到内存空行aa
for(int j=0;j<m_Right;j++){
screenarray[aa][j]=screenarray[bb][j];
screenarray[bb][j]=0;
}
printmem();
//把屏幕非空行bb清空
m_pos.X=m_scr_left+2; //从屏幕每行第1列开始
m_pos.Y=bb+m_scr_top; //把内存bb行换算成屏幕bb行
SetConsoleCursorPosition(m_hd, m_pos);
for(int j=0;j<m_Right;j++){
cout<<" "; //用空格清空
}
//把内存非空行aa内容 复制到屏幕对应aa行位置
m_pos.X=m_scr_left+2;
m_pos.Y=aa+m_scr_top; //把内存aa行换算成屏幕aa行
SetConsoleCursorPosition(m_hd, m_pos);
for(int j=0;j<m_Right;j++){
int c=screenarray[aa][j];
if(c>0){
SetConsoleTextAttribute(m_hd, FOREGROUND_INTENSITY | c );
cout<<"□";
}else{
cout<<" ";
}
}
}
aa=aa-1;
bb=bb-1;
}while(1);
}
printmem();
delete fullline;
m_getRandBlock(); //随机产生新方块
//如果新方块不能显示在初始位置
for(int i=0;i<m_x;i++){
for(int j=0;j<m_y;j++){
if(screenarray[m_memx+i][m_memy+j] >0){
cout<<"游戏结束,无法继续"<<endl;
m_isgameover=true;
return;
}
}
}
fflush(); //清空键盘缓冲区中多余方向键
m_showBlocks(m_scrx,m_scry,true);
}
}
bool CBlocks::m_isCrossBorder(int dir){ //返回f没有越界,返回t表示越界
switch(dir){
case RIGHT: //方向是向右
//当前方块列的位置+2+当前方块列数 <右边界
{
int n=m_y;//得到方块列数
//n必须减1 向右移1位时只相当移n-1位,而不是移n位
if((m_memy+n)< m_Right)
return false;
}
break;
case DOWN: //向下
//(当前方块x列+1)<下边界
{
int n=m_x;//得到方块行数
if((m_memx+n)< m_Down)
return false;
}
break;
case LEFT: //向左
//当前方块y行-2+当前方块宽度>下边界
if((m_memy)> m_Left)
return false;
break;
}
return true; //表示越界
}
bool CBlocks::m_isBoom(int dir){
switch(dir){
case RIGHT:{ //向右
//如果当前方块每列都能右移,表示整个方块能够右移
for(int i=0;i<m_y;i++){
int z=m_y-i-1; //定位方块每行最右列,每次都向左移1位
for(int j=0;j<m_x;j++){
int n=m_getCurBlocksValue(z);//读取当前方块组成内容
//内存同样从最右列开始即值
int m=screenarray[m_memx+j][m_memy+m_y-i];
if(m) m=1; //把大于0的值 当成1
if(n+m>1) //相加 如果相加值大于说明发生碰撞
return true; //返回真,表示碰撞
else //否则继续判断
z=z+m_y; //下一行同样列
}
}
break;
}
case DOWN:{ //向下
//如果当前方块每行都能下移,整个方块就能下移
int d=0;
for(int i=m_x-1;i>=0;--i){
int z=i*m_y; //每行第1列位置,从尾行开始往前
for(int j=0;j<m_y;j++){ //如果当前行每1小列都可以下移,当前行就可以下移
int n=m_getCurBlocksValue(z); //读取当前方块组成内容
int m=screenarray[m_memx+m_x-d][m_memy+j]; //读取方块在内存每1行每1小列内容
if(m) m=1; //把大于0的值 当成1
if(n+m>1)
return true;
else
++z;
}
d++;
}
break;
}
case LEFT:{ //向左
//如果当前方块每列都能左移,表示整个方块能够左移
for(int i=0;i<m_y;i++){
int z=i; //定位方块每行最左列,每次都向右移1位
for(int j=0;j<m_x;j++){
int n=m_getCurBlocksValue(z); //读取当前方块组成内容
int m=screenarray[m_memx+j][m_memy-1+i]; //读取方块在内存最左列-1位置取值
if(m) m=1; //把大于0的值 当成1
if(n+m>1) //相加 如果相加值大于说明发生碰撞
return true; //返回真,表示碰撞
else //否则继续判断
z=z+m_y; //下一行最右列的位置
}
}
break;
}
}
return false;
}
CBlocks::~CBlocks(){
for(int i=0;i<m_Down;i++)
delete screenarray[i];
delete screenarray;
}
bool CBlocks::m_getgamestat(){
return m_isgameover;
}
void CBlocks::printmem(){
m_pos.X=60;
m_pos.Y=0;
SetConsoleTextAttribute(m_hd,FOREGROUND_INTENSITY | 0);
for(int i=0;i<m_Down;i++){
SetConsoleCursorPosition(m_hd, m_pos); //设置控制台光标输出的位置
for(int j=0;j<m_Right;j++){
cout<<(int)screenarray[i][j]<<" ";
}
cout<<endl;
m_pos.Y++;
}
}
//消除屏幕上满行方块 并 把对应内存数组置0
void CBlocks::clearblocks(char *fullline,int n){
if(n % 2==0) n--; //必须保证n是奇数
int i=0;
bool m=false;
char p[3]=" ";
SetConsoleTextAttribute(m_hd,FOREGROUND_INTENSITY | 0);
while(i<n){
for(int j=0;j<m_x;j++){
if(fullline[j]==1){ //如果当前行是满行
m_pos.Y=m_scry+j; //把光标移到要消除的行
m_pos.X=m_scr_left+2; //把光标移到当前行列首
SetConsoleCursorPosition(m_hd, m_pos);
//满行
for(int k=0;k<m_Right;k++){
cout<<p; //把屏幕对应行用空格复盖
screenarray[m_memx+j][k]=0; //对应的内存数组清0
}
}
}
m=!m;
if(m){
p[0]=-95;p[1]=-11;p[2]='\0';
}else{
p[0]=' ';p[1]=' ';p[2]='\0';
}
i++;
}
}
//设置当前方块行和列值
void CBlocks::m_setCurBlocksXy(){
m_x=m_blocks[m_curblock][m_curblockstyle][0]-48; //得到当前方块行
m_y=m_blocks[m_curblock][m_curblockstyle][1]-48; //及列
}
//读取当前方块组成内容
int CBlocks::m_getCurBlocksValue(int value){
int start=2+value;
return m_blocks[m_curblock][m_curblockstyle][start]-48;
}
//判断当前方块每行是否有满行要进行消除
bool CBlocks::isfull(char *fullline){
bool flag=false;
for(int i=0;i<m_x;i++){
int n;
//对当前行每格值进行判断
for(int j=0;j<m_Right;j++){
n=screenarray[m_memx+i][j];
if(n==0) break; //如果n值为0表示当前行不是满行
}
if(n>0){ //n>0 表示当前行是满行
fullline[i]=1; //标记
flag=true;
}else
fullline[i]=0;
}
return flag;
}
//判断是否是空行
bool CBlocks::isNullLine(int const col){
int sum=0;
for(int j=0;j<m_Right;j++){
int n=screenarray[col][j];
sum=sum+n;
}
if(sum>0) //当前行不是空行
return false;
else
return true; //当前行是空行
}
void CBlocks::fflush(){
while(kbhit()!=0){
getch();
}
}
class CGameface{
public:
//全局变量 可以外部引用
int m_top;
int m_left;
int m_right;
int m_down;
enum dirdir{
ESC=27,UP=72,RIGHT=77,DOWN=80,LEFT=75
};
void m_playGame();
public:
CGameface();
~CGameface();
private:
CBlocks *myblocks; //方块类对象
CPressKey *mykey; //键盘类对象
HANDLE m_hd;
COORD m_pos;
private:
void m_printface(); //画游戏界面
void m_hidecursor(HANDLE);
};
CGameface::CGameface(){
m_top=5;m_down=20;
m_left=10;m_right=36;
m_hd=GetStdHandle(STD_OUTPUT_HANDLE); //获取cmd窗口句柄 同时设置pos 光标位置
m_printface(); //画游戏界面
myblocks=new CBlocks(m_top,m_down,m_left,m_right,m_hd,m_pos); //创建方块
mykey=new CPressKey; //创建键盘
m_playGame();
}
CGameface::~CGameface(){
delete myblocks;
delete mykey;
}
void CGameface::m_printface(){ //画游戏界面
m_hidecursor(m_hd);
m_pos.X=m_left;
m_pos.Y=m_top;
for(int i=m_top;i<m_down;i++){
SetConsoleCursorPosition(m_hd, m_pos);
cout<<"■";
m_pos.X=m_right;
SetConsoleCursorPosition(m_hd, m_pos);
cout<<"■";
cout<<endl;
m_pos.Y++;
m_pos.X=m_left;
}
m_pos.X=m_left+2;
m_pos.Y=m_down-1;
for(int i=m_left;i<=m_right-2;i=i+2){
SetConsoleCursorPosition(m_hd, m_pos);
cout<<"■";
m_pos.X=m_pos.X+2;
}
cout<<endl;
}
void CGameface::m_playGame(){
mykey->m_waitpresskey(); //等待玩家按下方向键
int presskey=mykey->m_getDirKey();
while(presskey !=27 ){
if(presskey !=1){ //如果玩家按入是方向键
//玩家按下方向键后开始移动当前方块
myblocks->m_moveBlocks(presskey);
if(myblocks->m_getgamestat()==true){
mykey->m_waitpresskey();
if(presskey !=0) return; //等待玩家任意键结束
}
}
mykey->m_waitpresskey(); //等待玩家按下方向键
presskey=mykey->m_getDirKey();//判断玩家按键
}
}
void CGameface::m_hidecursor(HANDLE hd){ //隐藏光标
//隐藏光标
CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(hd,&cci);
cci.bVisible=0; //赋1为显示,赋0为隐藏
SetConsoleCursorInfo(hd,&cci);
}
int main(){
CGameface startagame;
return 0;
}
|
上一篇: VS2013单步调试问题下一篇: 没有最新XTP库 怎么才能在VS2019上编译ForShare呢
|