VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

搜索
查看: 446|回复: 0

如何写个定时下移定时器

[复制链接]
06_avatar_middle
最佳答案
0 
在线会员 发表于 2021-4-15 21:42:53 | 显示全部楼层 |阅读模式
花了好几天模仿了个 控制台俄罗斯方块,键盘操作是操网上的 ,但方块创建,移动,和消除都是自己想的,头发都掉了好几根,现在就差定时器定时下移了,这个定时器该怎么写


#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呢
您需要登录后才可以回帖 登录 | 加入驿站 qq_login

本版积分规则

×【发帖 友情提示】
1、请回复有意义的内容,请勿恶意灌水;
2、纯数字、字母、表情等无意义的内容系统将自动删除;
3、若正常回复后帖子被自动删除,为系统误删的情况,请重新回复其他正常内容或等待管理员审核通过后会自动发布;
4、感谢您对VC驿站一如既往的支持,谢谢合作!

关闭

站长提醒上一条 /2 下一条

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号-4 )|网站地图wx_jqr

GMT+8, 2023-9-29 18:12

Powered by CcTry.CoM

© 2009-2021 cctry.com

快速回复 返回顶部 返回列表