推箱子游戏详解

举报
Linux猿 发表于 2021/12/08 22:23:02 2021/12/08
【摘要】 🎈 作者:Linux猿 🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊! 🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬


🎈 作者:Linux猿

🎈 简介:CSDN博客专家🏆,华为云享专家🏆,C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊!


推箱子游戏大家应该非常熟悉,非常经典的一款游戏,本文来详细讲解下推箱子游戏的制作过程,赶紧来看下吧!

首先,看下效果图:

图1 游戏过程

一、界面设计

1.1 主界面

主界面如下所示:

主界面主要包括开始游戏和退出游戏。

1.2 游戏界面

游戏界面是通过读取文件中的数据,然后显示到终端界面,如下所示:

二、设计思路

包括的各个函数有:

void showMap();         //输出地图
void move(char ch);     //移动
void hideCursor();      //隐藏光标
void getCoord(int x, int y);//光标定位
void menu();            //选择界面
void readGameData();    //读取游戏数据
void sokoban();         //游戏主要流程
包含的数据结构有:
struct Location {
    int x, y;
}target[NUM*NUM];
char map[NUM][NUM];
其中,target[NUM*NUM] 用于存储目标位置的坐标,map[NUM][NUM]用于存储地图。

主流程如下所示:

//游戏主流程
void sokoban()
{
    showMap(); //展示地图
    hideCursor(); //隐藏光标
    while (true) {
        if(_kbhit()) {
            char ch = _getch();    //获取用户输入
            switch (ch) {
            case 'w':      //上
                move(ch); break;
            case 's':      //下
                move(ch); break;
            case 'a':     //左
                move(ch); break;
            case 'd':     //右
                move(ch); break;
            }
        }
        Sleep(10);
    }
}
设计思路如下:

1. 读取游戏地图数据;

2. 等待用户输入;

3. 根据用户输入移动人;

4. 判断是否移动成功;

5. 循环 2 ~ 4,一直到游戏成功;

三、源代码

源码如下,可直接运行。

#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <stdbool.h>

#define GAMEDATAPATH "data.txt"

#define NUM 200

int lx = 10, ly = 17;
char map[NUM][NUM];
int numTarget = 0;
int nx, ny, sx, sy, mb, n=1;
struct Location {
    int x, y;
}target[NUM*NUM];


void showMap();         //输出地图
void move(char ch);     //移动
void hideCursor();      //隐藏光标
void getCoord(int x, int y);//光标定位
void menu();            //选择界面
void readGameData();    //读取游戏数据
void sokoban();         //游戏主要流程

//隐藏光标
void hideCursor()
{
    CONSOLE_CURSOR_INFO cursor= { 1, 0 };
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor);
}

//设置颜色
void color(int a)
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), a);
}

//光标重定位,用于在光标处输出
void getCoord(int x, int y)
{
    COORD pos = { x,y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

void checkTargetGraph() {
    int i;
    for(i = 0; i < numTarget; ++i) {
        int x = target[i].x;
        int y = target[i].y;
        if(map[x][y] == '2') {
            getCoord((y+ly)*2, x+lx); color(4); printf("★"); color(7);
        }
    }
}

bool checkSuccess() {
    int i;
    for(i = 0; i < numTarget; ++i) {
        int x = target[i].x;
        int y = target[i].y;
        if(map[x][y] != 'B') {
            return false;
        }
    }
    return true;
}

//从文件中读取游戏地图数据
void readGameData()
{
    int i, j;
    FILE* fp = fopen(GAMEDATAPATH, "r");

    if (fp == NULL) {
        printf("Error: Failed to read data!");
        return;
    }
    //读取行和列
    fscanf(fp, "%d %d %d %d", &nx, &ny, &sx, &sy);
    //读取数据
    for(i = 0; i < nx; ++i) {
       fscanf(fp, "%s", map[i]);
    }
    for(i = 0; i < nx; ++i) {
        for(j = 0; j < ny; ++j) {
            if(map[i][j] == 'C') {
                target[numTarget].x = i;
                target[numTarget++].y = j;
            }
        }
    }
    fclose(fp);
}

//输出游戏地图
void showMap()
{
    system("cls"); //清屏
    int i, j;
    for(i = 0; i < nx; ++i) {
        for(j = 0; j < ny; ++j){
            getCoord((j+ly)*2, i+lx);
            if(map[i][j] == '1') {
                color(52); printf("■");
            } else if(map[i][j] == '2') {
                printf("  ");
            } else if(map[i][j] == 'A') {
                color(1); printf("♀");
            } else if(map[i][j] == 'B') {
                color(6); printf("■");
            } else if(map[i][j] == 'C') {
                color(4); printf("★");
            }
            color(7);
        }
    }
}

//主界面
int main()
{
    system("chcp  65001"); //语言支持
    system("mode con cols=90 lines=30");//设置终端大小
    system("cls");
    menu();//菜单
    sokoban();          //主要流程
    return 0;
}

void menu()//主流程
{
    system("cls"); //清屏
    getCoord(39, 8); color(9); printf("推箱子游戏"); color(7);
    getCoord(38, 12); color(9); printf("1. 开始游戏"); color(7);
    getCoord(38, 14); color(9); printf("2. 退出游戏"); color(7);
    hideCursor();
    while(true) {
        char chn=_getch();//选择
        if(chn == '1') {
            readGameData();  //读取游戏数据
            break;
        } else if (chn == '2') { //退出游戏
            system("cls");
            getCoord(39, 8); printf("推箱子游戏");
            getCoord(38, 15); printf("欢迎下次光临");
            Sleep(3000);
            exit(1);
        } else {
            getCoord(39, 18); printf("输入错误!");
            Sleep(1000);
            getCoord(39, 18); printf("           ");
        }
    }
}

void move(char ch)
{
    if(ch == 'd') {
        if(sy + 1 < ny && map[sx][sy+1] != '1') {// 人右边没有超出方框,且右边不是墙
            if(map[sx][sy+1] == '2' || map[sx][sy+1] == 'C') {//是空格或目标直接走过去
                getCoord((sy+ly)*2, sx+lx);  printf("  ");
                getCoord((sy+ly)*2+2, sx+lx); color(1); printf("♀"); color(7);

                map[sx][sy] = '2';
                map[sx][sy+1] = 'A';
                sy = sy + 1;
            } else if(map[sx][sy+1] == 'B' && sy + 2 < ny && (map[sx][sy+2] == '2' || map[sx][sy+2] == 'C')) {//人 箱子 空格
                getCoord((sy+ly)*2+4, sx+lx); printf("  ");
                getCoord((sy+ly)*2+4, sx+lx); color(6); printf("■"); color(7);
                getCoord((sy+ly)*2+2, sx+lx); color(1); printf("♀"); color(7);
                getCoord((sy+ly)*2, sx+lx);  printf("  ");
                map[sx][sy+2] = 'B';
                map[sx][sy+1] = 'A';
                map[sx][sy] = '2';
                sy = sy + 1;
            }
        }
    } else if(ch == 'a') {
        if(sy - 1 >= 0 && map[sx][sy-1] != '1') {// 人左边没有超出方框,且左边不是墙
            if(map[sx][sy-1] == '2' || map[sx][sy-1] == 'C') {//是空格直接走过去
                getCoord((sy+ly)*2, sx+lx);  printf("  ");
                getCoord((sy+ly)*2-2, sx+lx); color(1); printf("♀"); color(7);

                map[sx][sy] = '2';
                map[sx][sy-1] = 'A';
                sy = sy - 1;
            } else if(map[sx][sy-1] == 'B' && sy - 2 >= 0 && (map[sx][sy-2] == '2' || map[sx][sy-2] == 'C')) {//空格 箱子 人
                getCoord((sy+ly)*2-4, sx+lx); printf("  ");
                getCoord((sy+ly)*2-4, sx+lx); color(6); printf("■"); color(7);
                getCoord((sy+ly)*2-2, sx+lx); color(1); printf("♀"); color(7);
                getCoord((sy+ly)*2, sx+lx);  printf("  ");
                map[sx][sy-2] = 'B';
                map[sx][sy-1] = 'A';
                map[sx][sy] = '2';
                sy = sy - 1;
            }
        }
    } else if(ch =='w') {
        if(sx - 1 >= 0 && map[sx-1][sy] != '1') {// 人上面没有超出方框,且上面不是墙
            if(map[sx-1][sy] == '2' || map[sx-1][sy] == 'C') {//是空格直接走过去
                getCoord((sy+ly)*2, sx+lx);  printf("  ");
                getCoord((sy+ly)*2, sx+lx-1); color(1); printf("♀"); color(7);

                map[sx][sy] = '2';
                map[sx-1][sy] = 'A';
                sx = sx - 1;
            } else if(map[sx-1][sy] == 'B' && sx - 2 >= 0 && (map[sx-2][sy] == '2' || map[sx-2][sy] == 'C')) {//空格 箱子 人
                getCoord((sy+ly)*2, sx+lx-2); printf("  ");
                getCoord((sy+ly)*2, sx+lx-2); color(6); printf("■"); color(7);
                getCoord((sy+ly)*2, sx+lx-1); color(1); printf("♀"); color(7);
                getCoord((sy+ly)*2, sx+lx);  printf("  ");
                map[sx-2][sy] = 'B';
                map[sx-1][sy] = 'A';
                map[sx][sy] = '2';
                sx = sx - 1;
            }
        }
    } else if(ch =='s') {
        if(sx + 1 < nx && map[sx+1][sy] != '1') {// 人上面没有超出方框,且上面不是墙
            if(map[sx+1][sy] == '2' || map[sx+1][sy] == 'C') {//是空格直接走过去
                getCoord((sy+ly)*2, sx+lx);  printf("  ");
                getCoord((sy+ly)*2, sx+lx+1); color(1); printf("♀"); color(7);

                map[sx][sy] = '2';
                map[sx+1][sy] = 'A';
                sx = sx + 1;
            } else if(map[sx+1][sy] == 'B' && sx + 2 >= 0 && (map[sx+2][sy] == '2' || map[sx+2][sy] == 'C')) {//空格 箱子 人
                getCoord((sy+ly)*2, sx+lx+2); printf("  ");
                getCoord((sy+ly)*2, sx+lx+2); color(6); printf("■"); color(7);
                getCoord((sy+ly)*2, sx+lx+1); color(1); printf("♀"); color(7);
                getCoord((sy+ly)*2, sx+lx);  printf("  ");
                map[sx+2][sy] = 'B';
                map[sx+1][sy] = 'A';
                map[sx][sy] = '2';
                sx = sx + 1;
            }
        }
    }
    checkTargetGraph();  //显示目标,防止覆盖
    bool flag = checkSuccess(); //检查是否成功
    if(flag) {
       getCoord(38, 19); color(4); printf("Success\n"); color(7);
    }
}

//游戏主流程
void sokoban()
{
    showMap(); //展示地图
    hideCursor(); //隐藏光标
    while (true) {
        if(_kbhit()) {
            char ch = _getch();    //获取用户输入
            switch (ch) {
            case 'w':      //上
                move(ch); break;
            case 's':      //下
                move(ch); break;
            case 'a':     //左
                move(ch); break;
            case 'd':     //右
                move(ch); break;
            }
        }
        Sleep(10);
    }
}
data.txt文件游戏数据如下所示,放到和源文件同目录下。
7 10 3 2
1111111111
1122222111
11B1112221
12A2B22B21
12CC12B211
11CC122211
1111111111

四、总结

推箱子游戏的重点在于人的移动,人可以向上下左右四个方向移动,需要判断人的前面的物体。游戏界面设计主要是根据用户的输入移动人,每次移动后判断是否成功。


🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。