ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-02-08 19:01:55  阅读:248  来源: 互联网

标签:case level void C语言 break 闪屏 printf 缓冲区 已用


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

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

最后通过阅读微软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;    
}

 

 

标签:case,level,void,C语言,break,闪屏,printf,缓冲区,已用
来源: https://www.cnblogs.com/infocodez/p/15872479.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有