• 【C语言版推箱子】已用双缓冲技术解决闪屏问题


    课设做的推箱子,经典老游戏,网上代码也非常多。

    老师检查的时候让我解决闪屏问题,虽然知道需要使用双缓冲技术解决,但网上没有找到能直接帮助解决闪屏的博客文章之类的。

    最后通过阅读微软API文档并结合搜索,终于解决。

    完成的过程中参照了不少他人文章或者其他资料,但因为时间有点久,而当时又没注意保存参考链接,所以此处没办法给出参考链接了,抱歉。

    因为IDE是Dev-Cpp_5.11,且加入了播放背景音乐的功能,所以需要对dev文件进行编译才可运行程序。

    地图文件和背景音乐文件略

    下面直接贴代码:

    /*推箱子-infocodez*/
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <Windows.h>
    #include <mmsystem.h>
    
    #define WIDTH 9
    #define HEIGHT 9
    #define MAX_LEVEL 5
    
    /*
        0    表示空地"  " 
        1    表示墙壁"■"
        2    表示人物"♀"
        3    表示箱子"◆"
        4    表示目的地"●"
        5    表示到达目的地的箱子"★" 
        6   表示人物和目的地重合"♂"
    */
    
    //用于存储地图 
    int map[HEIGHT][WIDTH]={
        {0, 0, 1, 1, 1, 0, 0, 0, 0},
        {0, 0, 1, 4, 1, 0, 0, 0, 0},
        {0, 0, 1, 0, 1, 1, 1, 1, 0},
        {1, 1, 1, 3, 0, 3, 4, 1, 0},
        {1, 4, 0, 3, 2, 1, 1, 1, 0},
        {1, 1, 1, 1, 3, 1, 0, 0, 0},
        {0, 0, 0, 1, 4, 1, 0, 0, 0},
        {0, 0, 0, 1, 1, 1, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0} 
    };
    
    //临时存储map数组,用于重置地图 
    int tmap[HEIGHT][WIDTH];
    
    //x、y表示人物位置,level表示关卡数,steps为已走步数,boxes箱子的个数
    int x, y,level=1,steps,boxes;
    
    //定义句柄,默认显示缓冲区和后台显示缓冲区的句柄
    HANDLE hOutput,hOutBuf;
    
    //定义光标的坐标 
    COORD coord={0,0};
    
    //双缓冲处理显示
    DWORD bytes=0;//DWORD相当于unsigned long类型。bytes为两个显示缓冲区之间传输的字符数 
    char data[2700];//两个显示缓冲区之间传输的字符
    
    /*函数声明*/ 
    
    //游戏说明 
    void instructions(); 
    
    //菜单界面 
    void menu();
    
    //开始游戏
    void startGame();
    
    //初始化数据
    void initData();
    
    //绘制地图
    void drawMap();
    
    //选择关卡
    void chooseLevel();
    
    //选择地图
    void chooseMap();
    
    //过关判定
    void isPass();
    
    //通关界面 
    void win(); 
    
    //人物向上移动
    void moveUp();
    
    //人物向左移动
    void moveLeft();
    
    //人物向下移动
    void moveDown();
    
    //人物向右移动
    void moveRight();
    
    //主函数
    int main(){    
        system("mode con cols=90 lines=30");//设置窗口大小 
        system("title 推箱子小游戏"); //设置窗口标题 
        system("color F0");//设置窗体颜色。背景亮白色、字体黑色     
        
        PlaySound(TEXT("music.wav"),NULL,SND_FILENAME|SND_ASYNC|SND_LOOP);//异步方式循环播放背景音乐 
        
        //获取默认标准显示缓冲区句柄
        hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
        
        //创建新的缓冲区,作为后台显示缓冲区 
        hOutBuf=CreateConsoleScreenBuffer(
            GENERIC_READ | GENERIC_WRITE, //控制台屏幕缓冲区的读写权限
            FILE_SHARE_READ | FILE_SHARE_WRITE, //共享缓冲区的读写权限 
            NULL, //安全属性,默认值 
            CONSOLE_TEXTMODE_BUFFER, //缓冲区类型,唯一可选
            NULL//保留的属性,默认值 
        );
            
        //隐藏两个缓冲区的光标
        CONSOLE_CURSOR_INFO cci;
        cci.bVisible=0;//光标不可见 
        cci.dwSize=1;//光标填充的字符单元格的百分比 
        SetConsoleCursorInfo(hOutput, &cci);//设置指定控制台屏幕缓冲区的光标的大小和可见性
        SetConsoleCursorInfo(hOutBuf, &cci);
        
        instructions();
        menu();
        startGame();    
        return 0;
    }
    
    //游戏说明 
    void instructions(){
        int s; 
        printf("\n\n\n\n\n\t\t\t\t     游戏说明\n\n");
        printf("\t\t本游戏共5关。将所有箱子全部堆到目的地则通过关卡。\n");
        printf("\t\t通过w、s、a、d键或方向键可上下左右移动人物。按r键可重置地图。\n");
        printf("\t\t♀代表人物、◆代表箱子、●代表目的地。\n");
        printf("\t\t阅读完毕请按任意键:\n\n");
        s=getch();
        return;
    }
    
    //菜单界面 
    void menu(){
        int option,x=1;//option选项,x控制while循环并初始为1 
        system("cls");//清屏 
        printf("\n\n\n\n\n\t\t\t\t     菜 单\n\n");
        printf("\t\t\t\t1.直 接 开 始\n");
        printf("\t\t\t\t2.选 择 关 卡\n");
        printf("\n\n请输入你的选择:");
        while(x){
            scanf("%d",&option);
            fflush(stdin);//清空输入缓冲区
            switch(option){
                case 1:
                    x=0;
                    chooseMap();//将第1关的地图数据从文件中导入 
                    startGame();
                    break;
                case 2:
                    x=0;
                    chooseLevel();
                    break;
                default:
                    printf("输入错误,请重新输入:");
                    break;
            }    
        }
    }
    
    //选择关卡
    void chooseLevel(){
        int option1,x=1;//option1存储按键信息,x控制while循环并初始为1
        system("cls");//清屏 
        printf("\n\n\n\n\n\t\t\t\t     关卡说明\n\n");
        printf("\t\t     游戏共5关,第1关到第5关由易到难。\n");
        printf("\t\t     选择完关卡后,将直接开始游戏。\n\n\n\n");
        printf("请输入你选择的关卡:");
        while(x){
            scanf("%d",&option1);
            fflush(stdin);
            switch(option1){
                case 1:
                    x=0;
                    level=1;
                    chooseMap();
                    break;
                case 2:
                    x=0;
                    level=2;
                    chooseMap();
                    break;
                case 3:
                    x=0;
                    level=3;
                    chooseMap();
                    break;
                case 4:
                    x=0;
                    level=4;
                    chooseMap();
                    break;
                case 5:
                    x=0;
                    level=5;
                    chooseMap();
                    break;
                default:
                    printf("输入错误,请重新输入:");
                    break;                
            }
        }    
        startGame();
    }
    
    //过关判定
    void isPass(){    
        if(!boxes){//当剩余箱子数为0,说明所有箱子均被推入目的地。则关卡数加1 
            int op,x=1;
            char str1[200];//用于合成字符串        
            level++;
            if(level>MAX_LEVEL){//当关卡数大于最大关卡数,说明已通关,进入通关界面 
                win();
                return;
            }
            
            //设置默认标准显示缓冲区光标的坐标        
            coord.X=0;
            coord.Y=18;
            SetConsoleCursorPosition(hOutput,coord);
            
            printf("\n输入1为继续闯关,输入0为结束游戏:");            
            while(1){            
                scanf("%d",&op);
                fflush(stdin);        
                if(op==1){//进入下一关游戏
                    x=0;    
                    chooseMap();
                    startGame();
                }
                else if(op==0){
                    x=0;
                    printf("\n感谢您对本游戏的支持!\n");        
                    exit(0);//退出游戏,结束程序 
                }
                else{
                    printf("输入错误,请重新输入:");
                }             
            }         
        }
    } 
    
    //通关界面
    void win(){
        system("cls");    
        printf(
        "\n\n\t\t\t  ★\'☆°★ *°∴°°☆☆★ *°☆☆\n"
        "\t\t\t  ★ °∴°°☆°★*°☆*°°∴*☆\n"
        "\t\t\t  ★*°∴°☆★∴°★*°°*°☆\n"
        "\t\t\t    ★°★☆°∴* ★*∴*°☆\n"
        "\t\t\t     ★  *★*°°∴°☆\n"
        "\t\t\t      ﹨  ☆*  ☆\n"
        "\t\t\t       ﹨   ☆\n"
        "\t\t\t        ﹨ /\n"
        "\n\n\n\n\n\n\n\t\t\t\t※恭喜你通关!太棒了!※\n\n\n\n");
        exit(0);//正常状态退出程序 
    }
    
    //选择地图
    void chooseMap(){
        int i,j;
        FILE *fp;
        switch(level){
            case 1:
                fp=fopen("map1.txt","rt");//rt,打开已存在的文本文件并读取数据            
                break;
            case 2:
                fp=fopen("map2.txt","rt");
                break;
            case 3:
                fp=fopen("map3.txt","rt");
                break;
            case 4:
                fp=fopen("map4.txt","rt");
                break;
            case 5:
                fp=fopen("map5.txt","rt");
                break;
            default:
                printf("地图选择失败\n");
                exit(-1);//异常状态结束程序 
        }
        if(fp==NULL){
                printf("文件打开失败\n");
                exit(-1);//异常状态结束程序 
            }
        //将地图文件的数据复制到map数组 
        for(i=0;i<HEIGHT;i++){
            for(j=0;j<WIDTH;j++){
                fscanf(fp,"%d",&map[i][j]);
            }
        }
        
        fclose(fp);//关闭文件 
    }
    
    //开始游戏
    void startGame(){
        int i,j;
        char direction;//存储按键动作    
        //记忆关卡初始时的地图,便于重置地图 
        for(i=0;i<HEIGHT;i++){
            for(j=0;j<WIDTH;j++){
                tmap[i][j]=map[i][j];
            }
        }
        
        initData();    
        while(1){        
            drawMap();                
            isPass();
            direction=getch();
            switch(direction){
                case 'w':
                case 'W':
                case 72://向上的方向键 
                    moveUp();                
                    break;
                case 'a':
                case 'A':
                case 75://向左的方向键 
                    moveLeft(); 
                    break;
                case 's':
                case 'S':
                case 80://向下的方向键 
                    moveDown();
                    break;
                case 'd':
                case 'D':
                case 77://向右的方向键 
                    moveRight();
                    break; 
                case 'R':
                case 'r'://重置地图 
                    for(i=0;i<HEIGHT;i++)
                    {
                        for(j=0;j<WIDTH;j++)
                        {
                            map[i][j]=tmap[i][j];
                        }
                    }                
                    startGame();
                    break;
                default:
                    break;    
            }
        } 
    }
    
    //初始化数据
    void initData(){
        int i,j;
        boxes=0;//箱子数目初始化为零 
        steps=0;//步数初始化为零 
        
        //获取箱子数目和人物位置 
        for(i=0;i<HEIGHT;i++){
            for(j=0;j<WIDTH;j++){            
                //遍历到3时,箱子的数目增加 
                if(map[i][j]==3){
                    boxes++;
                }
                //遍历到2或6时,记录人物位置 
                else if(map[i][j]==2||map[i][j]==6){
                    x=j;
                    y=i;
                } 
            }
        } 
    }
    
    //绘制地图
    void drawMap(){    
        int i,j;
        char str[200];//用于合成字符串    
        
        //设置后台显示缓冲区光标的坐标    
        coord.X=29;
        coord.Y=4;
        SetConsoleCursorPosition(hOutBuf,coord); 
           
        sprintf(str,"第%d关\n",level);//合成字符串 
        WriteConsole(hOutBuf,str,strlen(str),NULL,NULL);//将字符串写入后台显示缓冲区 
        for(i=0;i<HEIGHT;i++){
            coord.X=24;
            coord.Y=i+6;
            SetConsoleCursorPosition(hOutBuf,coord);
            for(j=0;j<WIDTH;j++){
                switch(map[i][j]){
                    case 0:
                        WriteConsole(hOutBuf, "  ", strlen("  "), NULL, NULL);;//空地 
                        break;
                    case 1:
                        WriteConsole(hOutBuf, "", strlen(""), NULL, NULL);;//墙壁 
                        break;
                    case 2:
                        WriteConsole(hOutBuf, "", strlen(""), NULL, NULL);//人物 
                        break;
                    case 3:
                        WriteConsole(hOutBuf, "", strlen(""), NULL, NULL);//箱子 
                        break;
                    case 4:
                        WriteConsole(hOutBuf, "", strlen(""), NULL, NULL);//目的地 
                        break;
                    case 5:
                        WriteConsole(hOutBuf, "", strlen(""), NULL, NULL);//到达目的地的箱子
                        break; 
                    case 6:
                        WriteConsole(hOutBuf, "", strlen(""), NULL, NULL);//人物和目的地重合
                        break;
                    default:
                        WriteConsole(hOutBuf,"地图载入错误",strlen("地图载入错误"),NULL,NULL);
                        break;
                }        
            }
        }
        sprintf(str,"\n\n本关剩余目标数:%-4d\n本关已走步数:%-4d\n",boxes,steps); 
        WriteConsole(hOutBuf,str,strlen(str),NULL,NULL); 
        //设置两个缓冲区传输字符的起始坐标 
        coord.X=0;
        coord.Y=0;
        //将后台缓冲区的字符信息传输到默认标准显示缓冲区 
        ReadConsoleOutputCharacter(hOutBuf,data,2700,coord,&bytes);//读取后台缓冲区的字符信息 
        WriteConsoleOutputCharacter(hOutput,data,2700,coord,&bytes);//将字符信息写入默认标准显示缓冲区   
    }
    
    //人物向上移动
    void moveUp(){    
        int ux, uy;//存放人物上方的坐标     
        
        //记录人物上方坐标
        ux=x;
        uy=y-1; 
        
        //人物上方没有元素或人物上方为墙壁
        if(y==0||map[uy][ux] == 1){
            return;
        }    
    
        //人物上方为目的地
        else if(map[uy][ux]==4){         
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;           
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[uy][ux]=6;    
        }
        
        //人物上方为已到达目的地的箱子 
        else if(map[uy][ux]==5){            
            //已到达目的地的箱子上方为墙壁、箱子、已到达目的地的箱子
            if(map[uy-1][ux]==1||map[uy-1][ux]==3||map[uy-1][ux]==5){
                return;
            }        
            //已到达目的地的箱子上方为目的地           
            else if(map[uy-1][ux]==4){  
                map[uy-1][ux]=5;            
            }        
            //已到达目的地的箱子上方为空地
            else if(map[uy-1][ux]==0){             
                map[uy-1][ux]=3;
                boxes++;//箱子的数目加1    
            }
            
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[uy][ux]=6;
        } 
        
        //人物上方为箱子
        else if(map[uy][ux]==3){
            //箱子上方为墙壁、箱子、已到达目的地的箱子 
            if(map[uy-1][ux]==1||map[uy-1][ux]==3||map[uy-1][ux]==5){
                return;
            }        
            //箱子上方为目的地
            if(map[uy-1][ux]==4){
                map[uy-1][ux]=5;            
                boxes--;//箱子的数目减1     
                
            }
            //箱子上方为空地
            else if(map[uy-1][ux]==0){            
                map[uy-1][ux]=3;
            }
            
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
                
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[uy][ux]=2;
        }
        
        //人物上方为空地
        else if(map[uy][ux]==0){        
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;                
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[uy][ux]=2;
        }
        
        y=uy;//更新人物坐标 
        steps++;//已走步数加1 
        return;
    } 
    
    //人物向左移动
    void moveLeft(){
        int lx,ly;//存放人物左方的坐标  
        
        //记录人物左方坐标
        lx=x-1;
        ly=y; 
    
        //人物左方没有元素或人物左方为墙壁
        if(x==0||map[ly][lx]==1){
            return;
        }    
        
        //人物左方为目的地
        else if(map[ly][lx]==4){        
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;            
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[ly][lx]=6;
        }
        
        //人物左方为已到达目的地的箱子 
        else if(map[ly][lx]==5){
            //已到达目的地的箱子左方为墙壁、箱子、已到达目的地的箱子
            if(map[ly][lx-1]==1||map[ly][lx-1]==3||map[ly][lx-1]==5){
                return;
            }
            //已到达目的地的箱子左方为目的地,
            else if(map[ly][lx-1]==4){
                map[ly][lx-1]=5;
            }
            //已到达目的地的箱子左方为空地,
            else if(map[ly][lx-1]==0){
                map[ly][lx-1]=3;
                boxes++;//箱子的数目加1    
            }
            
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[ly][lx]=6;
        } 
        
        //人物左方为箱子
        else if(map[ly][lx]==3){
            //箱子左方为墙壁、箱子、已到达目的地的箱子 
            if(map[ly][lx-1]==1||map[ly][lx-1]==3||map[ly][lx-1]==5){
                return;
            }        
            //箱子左方为目的地
            if(map[ly][lx-1]==4){
                map[ly][lx-1]=5;
                boxes--;//箱子的数目减1  
            }
            //箱子左方为空地
            else if(map[ly][lx-1]==0){            
                map[ly][lx-1]=3;
            }        
            
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[ly][lx]=2;
        }
        //人物左方为空地
        else if(map[ly][lx]==0){
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[ly][lx]=2;
        }
            
        x=lx;//更新人物坐标 
        steps++;//已走步数加1 
        return;
    }
    
    //人物向下移动
    void moveDown(){    
        int dx,dy;//存放人物下方的坐标 
        
        //记录人物下方坐标
        dx=x;
        dy=y+1; 
        
        //人物下方没有元素或人物下方为墙壁    
        if(y==HEIGHT-1||map[dy][dx]==1){
            return;
        }    
        
        //人物下方为目的地
        else if(map[dy][dx]==4){
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[dy][dx]=6;    
        }
        
        //人物下方为已到达目的地的箱子 
        else if(map[dy][dx]==5){
            //已到达目的地的箱子下方为墙壁、箱子、已到达目的地的箱子
            if(map[dy+1][dx]==1||map[dy+1][dx]==3||map[dy+1][dx]==5){
                return;
            }
            //已到达目的地的箱子下方为目的地       
            else if(map[dy+1][dx]==4){            
                map[dy+1][dx]=5;
            }
            //已到达目的地的箱子下方为空地
            else if(map[dy+1][dx]==0){
                map[dy+1][dx]=3;
                boxes++;//箱子的数目加1    
            }
            
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;                
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[dy][dx]=6;
        } 
        
        //人物下方为箱子
        else if(map[dy][dx]==3){
            //箱子下方为墙壁、箱子、已到达目的地的箱子
            if(map[dy+1][dx]==1||map[dy+1][dx]==3||map[dy+1][dx]==5){
                return;
            }        
            //箱子下方为目的地
            if(map[dy+1][dx]==4){
                map[dy+1][dx]=5;
                boxes--;//箱子的数目减1  
            }
            //箱子下方为空地
            else if(map[dy+1][dx]==0){
                map[dy+1][dx]=3; 
            }
            
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;                
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[dy][dx]=2;
        }
        
        //人物下方为空地
        else if(map[dy][dx]==0){        
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;                
            }
            else if(map[y][x]==2){
                map[y][x]=0;
            }
            
            map[dy][dx]=2;
        }
        
        y=dy;//更新人物坐标 
        steps++;//已走步数加1 
        return;    
    }
    
    //人物向右移动
    void moveRight(){    
        int rx, ry;//存放人物右方的坐标 
        
        //记录人物右方坐标
        rx = x + 1;
        ry = y; 
        
        //人物右方没有元素或人物右方为墙壁
        if(x==WIDTH-1||map[ry][rx]==1){
            return;
        }
        
        //人物右方为目的地
        else if(map[ry][rx]==4){        
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){            
                map[y][x]=0;
            }
            
            map[ry][rx]=6;
        }
    
        //人物右方为已到达目的地的箱子 
        else if(map[ry][rx]==5){
            //已到达目的地的箱子右方为墙壁、箱子、已到达目的地的箱子
            if(map[ry][rx+1]==1||map[ry][rx+1]==3||map[ry][rx+1]==5){
                return;
            }
            //已到达目的地的箱子右方为目的地       
            else if(map[ry][rx+1]==4){
                map[ry][rx+1]=5;            
            }
            //已到达目的地的箱子右方为空地,
            else if(map[ry][rx+1]==0){
                map[ry][rx+1]=3;
                boxes++;//箱子的数目加1    
            }
            
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){            
                map[y][x]=0;
            }
            
            map[ry][rx]=6;
        } 
        
        //人物右方为箱子
        else if(map[ry][rx]==3){
            //箱子右方为墙壁、箱子、已到达目的地的箱子  
            if(map[ry][rx+1]==1||map[ry][rx+1]==3||map[ry][rx+1]==5){
                return;
            }        
            //箱子右方为目的地
            if(map[ry][rx+1]==4){
                map[ry][rx + 1] = 5;
                boxes--;//箱子的数目减1  
            }
            //箱子右方为空地
            else if(map[ry][rx+1]==0){
                map[ry][rx+1]=3; 
            }
            
             if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){            
                map[y][x]=0;
            }
                
            map[ry][rx]=2;
        }
        
        //人物右方为空地
        else if(map[ry][rx]==0){
            if(map[y][x]==6){//人物原位置与目的地重合
                map[y][x]=4;
            }
            else if(map[y][x]==2){            
                map[y][x]=0;
            }
            
            map[ry][rx]=2;
        }
        
        x=rx;//更新人物坐标 
        steps++;//已走步数加1 
        return;    
    }
     
  • 相关阅读:

    bzoj3052: [wc2013]糖果公园
    莫队算法心得
    bzoj1104: [POI2007]洪水pow
    bzoj1102: [POI2007]山峰和山谷Grz
    bzoj1121: [POI2008]激光发射器SZK
    bzoj1113: [Poi2008]海报PLA
    bzoj1103: [POI2007]大都市meg
    bzoj1396: 识别子串
    bzoj3756: Pty的字符串
  • 原文地址:https://www.cnblogs.com/infocodez/p/15872479.html
Copyright © 2020-2023  润新知