美文网首页C语言学习
C语言推箱子小游戏教程

C语言推箱子小游戏教程

作者: dawsonlee1790 | 来源:发表于2018-07-03 06:14 被阅读0次

    作者GitHub-Pages个人主页
    本教程GitHub-Pages链接
    本教程百度云下载地址
    本教程编写于2016/11/22
    Dawson Lee edited this page on Beijing, 2018/7/3


    设计思路

    将整个画面分成13*16的矩阵,每个元素对应者一个小矩形。然后用0代表黑格,1代表白墙,2代表蓝格……,这样就有了整幅图像的信息。然后移动就可以想象成,改变矩阵的坐标信息,形成一个新的矩阵,再重绘。这就是推箱子的基本原理。

    #define BLACK         0 //黑格:用于填充图像
    #define WHITEWALL     1 //白墙:用于阻挡主角移动
    #define BLUEWALL      2 //蓝格:用于表示可移动空间
    #define BALL          3 //点:用于指定箱子放置位置
    #define BOX       4 //箱子:用于表示初始箱子位置
    #define REDBOX        5 //变色的箱子:用于表示这个点位置已经放置箱子
    #define MAN       6 //主角:用于表示主角位置
    #define MANBALL       7 //主角站在点上
    
    设计思路

    步骤

    1.编译环境:Visual Studio Community 2015

    Tip:

    • 您也可以自行百度或者google Visual Studio Community 2015的其它下载途径
    • 您也可以尝试使用Visual Studio其它版本,但有可能会出现未知问题

    2.要给VS2015安装EGE图形库

    3.要参考EGE图形库帮助文档

    Tip:如果链接失效,请自行百度或者google"EGE图形库帮助文档"

    4.刚才我们说到,图像的背后对应着一个矩阵,我们可以考虑将矩阵的信息存储在一个MAP.txt文件中,在程序运行时读取。下面我来介绍,在文件中读取一个矩阵。

    首先我们得有一个TXT文件:MAP.txt(一定确保路径放置正确),它存储着这样一个13*16的矩阵

    0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
    0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
    0   0   0   1   1   1   1   1   1   1   1   1   1   0   0   0   
    0   0   0   1   2   2   2   2   2   2   2   2   1   0   0   0   
    0   0   0   1   2   2   2   2   2   2   2   2   1   0   0   0   
    0   0   0   1   2   6   2   4   2   3   2   2   1   0   0   0   
    0   0   0   1   2   2   2   2   2   2   2   2   1   0   0   0   
    0   0   0   1   2   2   2   2   2   2   2   2   1   0   0   0   
    0   0   0   1   2   2   2   2   2   2   2   2   1   0   0   0   
    0   0   0   1   1   1   1   1   1   1   1   1   1   0   0   0   
    0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
    0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
    0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
    

    文本文件中的矩阵读取操作源代码:

    #include<stdio.h>
    #include<stdlib.h>
    
    int main()
    {
    
        FILE *fp = fopen("MAP.txt", "rb");//以只读形式打开文件MAP.txt
        if (!fp) {
            printf("打开文件失败,文件位置你可能放错了\n");
            exit(1);
        }
        int map[13][16];//创建一个map数组用来存储矩阵信息
        int i, j;
        char str[10];//用于存储从文本文件读取的字符串信息
        for (i = 0; i<13; i++)
            for (j = 0; j < 16; j++) {
                fscanf(fp, "%s", str); //从文件中读取一个字符串,遇到空格结束
                map[i][j] = atoi(str);//将字符串转变为整型,并赋值给map[i][j]
            }
        for (i = 0; i < 13; i++) {
            for (j = 0; j < 16; j++) {
                printf("%d", map[i][j]);//遍历输出矩阵信息
            }
            printf("\n");
        }
        return 0;
    }
    

    接下我们要把TXT文件的读取矩阵的功能,写在一个readmap()函数中,实现功能的模块化。并加多一项功能,就是查找出,MAN的位置。

    //map[13][16]是形参,其中&man_i表是的是man_i的引用,引用传递的意义在于:传入参数会因函数中的操作而改变
    int readmap(int map[13][16], int &man_i, int &man_j)
    {
        FILE *fp = fopen("MAP.txt", "rb");//以只读形式打开文件MAP.txt
        if (!fp) {
            printf("打开文件失败,文件位置你可能放错了\n");
            exit(1);//打开失败则结束程序
        }
        int i, j;
        char str[10];//用于存储从文本文件读取的字符串信息
        for (i = 0; i<13; i++)
            for (j = 0; j < 16; j++) 
            {
                fscanf(fp, "%s", str); //从文件中读取一个字符串,遇到空格结束
                map[i][j] = atoi(str);//将字符串转变为整型,并赋值给map[i][j]
                if (map[i][j] == MAN) //如果找到了MAN的位置,则返回坐标在 man_i和 man_j 中
                {
                    man_i = i;
                    man_j = j;
                }
            }
        return 0;
    }
    

    5.接下来我们实现在窗口中,把矩阵信息转变成图像。原理就是,将图片贴在窗口上。

    • 图片资源pic.zip(把当中的pic文件夹解压,存放在正确路径中,如果路径不正确会导致窗口中没有图像)

    实现的效果如图所示:

    效果图-无背景

    绘制操作的源代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<graphics.h>
    
    #define BLACK         0 //黑格:用于填充图像
    #define WHITEWALL     1 //白墙:用于阻挡主角移动
    #define BLUEWALL      2 //蓝格:用于表示可移动空间
    #define BALL          3 //点:用于指定箱子放置位置
    #define BOX       4 //箱子:用于表示初始箱子位置
    #define REDBOX        5 //变色的箱子:用于表示这个点位置已经放置箱子
    #define MAN       6 //主角:用于表示主角位置
    #define MANBALL       7 //主角站在点上
    
    int main()
    {
        int map[13][16];//创建一个map数组用来存储矩阵信息
        int man_i, man_j;//用来存储人物的位置信息
        int readmap(int map[13][16], int &man_x, int &man_y);//声明
        void darm(int a, int ii, int jj);//声明
    
        readmap(map, man_i, man_j);//调用readmap()函数。
        initgraph(800, 650);//生成一个宽800,高650的窗口。假设矩阵元素对应的每个小矩形是50*50的,那个窗口就应该是这么大。
        for (int i = 0; i<13; i++)
            for (int j = 0; j < 16; j++)
            {
                darm(map[i][j], i, j);//遍历绘制每个小矩形
            }
    
        getch();//等待一个键盘输入,如果没有的话,窗口会看不到,因为程序运行结束就会关闭窗口。生成关闭就转瞬即逝
        closegraph();//关闭窗口
        return 0;
    }
    
    //map[13][16]是形参,其中&man_i表是的是man_i的引用,引用传递的意义在于:传入参数会因函数中的操作而改变
    int readmap(int map[13][16], int &man_i, int &man_j)
    {
        FILE *fp = fopen("MAP.txt", "rb");//以只读形式打开文件MAP.txt
        if (!fp) {
            printf("打开文件失败,文件位置你可能放错了\n");
            exit(1);//打开失败则结束程序
        }
        int i, j;
        char str[10];//用于存储从文本文件读取的字符串信息
        for (i = 0; i<13; i++)
            for (j = 0; j < 16; j++) 
            {
                fscanf(fp, "%s", str); //从文件中读取一个字符串,遇到空格结束
                map[i][j] = atoi(str);//将字符串转变为整型,并赋值给map[i][j]
                if (map[i][j] == MAN) //如果找到了MAN的位置,则返回坐标在 man_i和 man_j 中
                {
                    man_i = i;
                    man_j = j;
                }
            }
        return 0;
    }
    
    
    /*
    下面是在图形库绘制小矩形中图像的功能模块
    这部分涉及C++中的知识,C语言新手不必过多纠结不懂的东西。但要知道,创建的源文件要是cpp格式的
    */
    void darm(int a, int ii, int jj)
    {
        PIMAGE img;//声明
        img = newimage();//实例化
        switch (a)
        {
        case BLACK:break;
        case WHITEWALL:
        {
            getimage(img, "pic/墙.jpg");//getimage()和putimage请翻阅ege图形库帮助手册。
            putimage(jj * 50, ii * 50, img);//在ii行jj列绘制一个50*50像素的矩形图像
            break;
        }
        case BLUEWALL:
        {
            getimage(img, "pic/路.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case BALL:
        {
            getimage(img, "pic/目的地.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case BOX:
        {
            getimage(img, "pic/箱子.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case MAN:
        {
            getimage(img, "pic/人.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case MANBALL:
        {
            getimage(img, "pic/人.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case REDBOX:
        {
            getimage(img, "pic/红箱子.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        }
        ege::delimage(img);
    }
    

    6.实现图像的绘制以后那我们就应该实现图像的移动了。

    向右移动

    首先将所有的情况列举出来并存入数组table[i][0]中,将改变后的第一个格子信息存在table[i][1]中,第二个格子信息存在table[i][2]中,第三个格子信息存在table[i][3]中。

    数组table

    然后就可以开始写move()函数,实现MAN的移动。move()函数源代码

    /*
    传入函数的参数 get表示用户输入的键盘按键,传入实时数组map的信息,传入实时MAN的位置信息man_i,man_j。
    */
    void move(char get, int map[13][16], int &man_i, int &man_j)
    {
        //首先把情况的变化存在table[12][4]数组中
        int table[12][4];
    
        table[0][0] = MAN * BLUEWALL;
        table[1][0] = MAN * BALL;
        table[2][0] = MAN * BOX * BLUEWALL;
        table[3][0] = MAN * BOX * BALL;
        table[4][0] = MAN * REDBOX * BLUEWALL;
        table[5][0] = MAN * REDBOX * BALL;
        table[6][0] = MANBALL * BLUEWALL;
        table[7][0] = MANBALL * BALL;
        table[8][0] = MANBALL * BOX * BLUEWALL;
        table[9][0] = MANBALL * BOX * BALL;
        table[10][0] = MANBALL * REDBOX * BLUEWALL;
        table[11][0] = MANBALL * REDBOX * BALL;
    
        table[0][1] = BLUEWALL;
        table[1][1] = BLUEWALL;
        table[2][1] = BLUEWALL;
        table[3][1] = BLUEWALL;
        table[4][1] = BLUEWALL;
        table[5][1] = BLUEWALL;
        table[6][1] = BALL;
        table[7][1] = BALL;
        table[8][1] = BALL;
        table[9][1] = BALL;
        table[10][1] = BALL;
        table[11][1] = BALL;
    
        table[0][2] = MAN;
        table[1][2] = MANBALL;
        table[2][2] = MAN;
        table[3][2] = MAN;
        table[4][2] = MANBALL;
        table[5][2] = MANBALL;
        table[6][2] = MAN;
        table[7][2] = MANBALL;
        table[8][2] = MAN;
        table[9][2] = MAN;
        table[10][2] = MANBALL;
        table[11][2] = MANBALL;
    
        table[0][3] = -1;
        table[1][3] = -1;
        table[2][3] = BOX;
        table[3][3] = REDBOX;
        table[4][3] = BOX;
        table[5][3] = REDBOX;
        table[6][3] = -1;
        table[7][3] = -1;
        table[8][3] = BOX;
        table[9][3] = REDBOX;
        table[10][3] = BOX;
        table[11][3] = REDBOX;
    
    
    
        int i;
        void darm(int a, int ii, int jj);//要用到绘制函数,所以先声明
        switch (get)//switch判断用户输入的键盘信息
        {
        case 37:   //左移,向左键的ASCII码
            //for循环遍历判断MAN向左移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i][man_j - 1] * map[man_i][man_j - 2] == table[i][0] ||
                    map[man_i][man_j] * map[man_i][man_j - 1] == table[i][0])
                {
                    //找到对应的情况后执行以下语句
    
                    map[man_i][man_j] = table[i][1];
                    map[man_i][man_j - 1] = table[i][2];
                    if (table[i][3] != -1)map[man_i][man_j - 2] = table[i][3];//如果不需要改变第三个格子的图像,就不赋值。也就是如果table[i][3]==-1时。
    
                    //遍历绘制对应的三个格子信息
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i][man_j - 1], man_i, man_j - 1);
                    darm(map[man_i][man_j - 2], man_i, man_j - 2);
    
                    man_j--;//绘制结束后,要改变MAN的实时位置信息,并break跳出循环
                    break;
                }
            break;//再次break跳出switch语句
        case 38://上移,向上键的ASCII码
            //for循环遍历判断MAN向上移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i - 1][man_j] * map[man_i - 2][man_j] == table[i][0] ||
                    map[man_i][man_j] * map[man_i - 1][man_j] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i - 1][man_j] = table[i][2];
                    if (table[i][3] != -1)map[man_i - 2][man_j] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i - 1][man_j], man_i - 1, man_j);
                    darm(map[man_i - 2][man_j], man_i - 2, man_j);
                    man_i--;
                    break;
                }
            break;
        case 39://右移,向右键的ASCII码
            //for循环遍历判断MAN向右移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i][man_j + 1] * map[man_i][man_j + 2] == table[i][0] ||
                    map[man_i][man_j] * map[man_i][man_j + 1] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i][man_j + 1] = table[i][2];
                    if (table[i][3] != -1)map[man_i][man_j + 2] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i][man_j + 1], man_i, man_j + 1);
                    darm(map[man_i][man_j + 2], man_i, man_j + 2);
                    man_j++;
                    break;
                }
            break;
        case 40://下移,向下键的ASCII码
            //for循环遍历判断MAN向下移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i + 1][man_j] * map[man_i + 2][man_j] == table[i][0] ||
                    map[man_i][man_j] * map[man_i + 1][man_j] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i + 1][man_j] = table[i][2];
                    if (table[i][3] != -1)map[man_i + 2][man_j] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i + 1][man_j], man_i + 1, man_j);
                    darm(map[man_i + 2][man_j], man_i + 2, man_j);
                    man_i++;
                    break;
                }
            break;
        default:
            break;
        }
    }
    

    写好move()函数后,我们就可以实现推箱子的功能了。整合后的源代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<graphics.h>
    
    #define BLACK         0 //黑格:用于填充图像
    #define WHITEWALL     1 //白墙:用于阻挡主角移动
    #define BLUEWALL      2 //蓝格:用于表示可移动空间
    #define BALL          3 //点:用于指定箱子放置位置
    #define BOX       4 //箱子:用于表示初始箱子位置
    #define REDBOX        5 //变色的箱子:用于表示这个点位置已经放置箱子
    #define MAN       6 //主角:用于表示主角位置
    #define MANBALL       7 //主角站在点上
    
    int main()
    {
        int map[13][16];//创建一个map数组用来存储矩阵信息
        int man_i, man_j;//用来存储人物的位置信息
        int readmap(int map[13][16], int &man_x, int &man_y);
        void darm(int a, int ii, int jj);
    
        readmap(map, man_i, man_j);//调用readmap()函数。
    
    
        initgraph(800, 650);//生成一个宽800,高650的窗口。假设矩阵元素对应的每个小矩形是50*50的,那个窗口就应该是这么大。
        for (int i = 0; i<13; i++)
            for (int j = 0; j < 16; j++)
            {
                darm(map[i][j], i, j);//遍历绘制每个小矩形
            }
    
        void move(char get, int map[13][16], int &man_i, int &man_j);
        char get;
        for (; is_run(); delay_fps(60))//is_run()表示窗口一直显示,delay_fps(60)表示窗口以60帧的频率刷新
        {
            get = getch();//等待用户输入键盘按键
            move(get, map, man_i, man_j);
        }
    
        closegraph();//关闭窗口
        return 0;
    }
    //map[13][16]是形参,其中&man_i表是的是man_i的引用,引用传递的意义在于:传入参数会因函数中的操作而改变
    int readmap(int map[13][16], int &man_i, int &man_j)
    {
        FILE *fp = fopen("MAP.txt", "rb");//以只读形式打开文件MAP.txt
        if (!fp) {
            printf("打开文件失败,文件位置你可能放错了\n");
            exit(1);//打开失败则结束程序
        }
        int i, j;
        char str[10];//用于存储从文本文件读取的字符串信息
        for (i = 0; i<13; i++)
            for (j = 0; j < 16; j++) 
            {
                fscanf(fp, "%s", str); //从文件中读取一个字符串,遇到空格结束
                map[i][j] = atoi(str);//将字符串转变为整型,并赋值给map[i][j]
                if (map[i][j] == MAN) //如果找到了MAN的位置,则返回坐标在 man_i和 man_j 中
                {
                    man_i = i;
                    man_j = j;
                }
            }
        return 0;
    }
    /*
    下面是在图形库绘制小矩形中图像的功能模块
    这部分涉及C++中的知识,C语言新手不必过多纠结不懂的东西。但要知道,创建的源文件要是cpp格式的
    */
    
    void darm(int a, int ii, int jj)
    {
        PIMAGE img;//声明
        img = newimage();//实例化
        switch (a)
        {
        case BLACK:break;
        case WHITEWALL:
        {
            getimage(img, "pic/墙.jpg");//getimage()和putimage请翻阅ege图形库帮助手册。
            putimage(jj * 50, ii * 50, img);//在ii行jj列绘制一个50*50像素的矩形图像
            break;
        }
        case BLUEWALL:
        {
            getimage(img, "pic/路.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case BALL:
        {
            getimage(img, "pic/目的地.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case BOX:
        {
            getimage(img, "pic/箱子.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case MAN:
        {
            getimage(img, "pic/人.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case MANBALL:
        {
            getimage(img, "pic/人.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case REDBOX:
        {
            getimage(img, "pic/红箱子.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        }
        ege::delimage(img);
    }
    
    /*
    传入函数的参数 get表示用户输入的键盘按键,传入实时数组map的信息,传入实时MAN的位置信息man_i,man_j。
    */
    void move(char get, int map[13][16], int &man_i, int &man_j)
    {
        //首先把情况的变化存在table[12][4]数组中
        int table[12][4];
    
        table[0][0] = MAN * BLUEWALL;
        table[1][0] = MAN * BALL;
        table[2][0] = MAN * BOX * BLUEWALL;
        table[3][0] = MAN * BOX * BALL;
        table[4][0] = MAN * REDBOX * BLUEWALL;
        table[5][0] = MAN * REDBOX * BALL;
        table[6][0] = MANBALL * BLUEWALL;
        table[7][0] = MANBALL * BALL;
        table[8][0] = MANBALL * BOX * BLUEWALL;
        table[9][0] = MANBALL * BOX * BALL;
        table[10][0] = MANBALL * REDBOX * BLUEWALL;
        table[11][0] = MANBALL * REDBOX * BALL;
    
        table[0][1] = BLUEWALL;
        table[1][1] = BLUEWALL;
        table[2][1] = BLUEWALL;
        table[3][1] = BLUEWALL;
        table[4][1] = BLUEWALL;
        table[5][1] = BLUEWALL;
        table[6][1] = BALL;
        table[7][1] = BALL;
        table[8][1] = BALL;
        table[9][1] = BALL;
        table[10][1] = BALL;
        table[11][1] = BALL;
    
        table[0][2] = MAN;
        table[1][2] = MANBALL;
        table[2][2] = MAN;
        table[3][2] = MAN;
        table[4][2] = MANBALL;
        table[5][2] = MANBALL;
        table[6][2] = MAN;
        table[7][2] = MANBALL;
        table[8][2] = MAN;
        table[9][2] = MAN;
        table[10][2] = MANBALL;
        table[11][2] = MANBALL;
    
        table[0][3] = -1;
        table[1][3] = -1;
        table[2][3] = BOX;
        table[3][3] = REDBOX;
        table[4][3] = BOX;
        table[5][3] = REDBOX;
        table[6][3] = -1;
        table[7][3] = -1;
        table[8][3] = BOX;
        table[9][3] = REDBOX;
        table[10][3] = BOX;
        table[11][3] = REDBOX;
    
    
    
        int i;
        void darm(int a, int ii, int jj);//要用到绘制函数,所以先声明
        switch (get)//switch判断用户输入的键盘信息
        {
        case 37:   //左移,向左键的ASCII码
            //for循环遍历判断MAN向左移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i][man_j - 1] * map[man_i][man_j - 2] == table[i][0] ||
                    map[man_i][man_j] * map[man_i][man_j - 1] == table[i][0])
                {
                    //找到对应的情况后执行以下语句
    
                    map[man_i][man_j] = table[i][1];
                    map[man_i][man_j - 1] = table[i][2];
                    if (table[i][3] != -1)map[man_i][man_j - 2] = table[i][3];//如果不需要改变第三个格子的图像,就不赋值。也就是如果table[i][3]==-1时。
    
                    //遍历绘制对应的三个格子信息
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i][man_j - 1], man_i, man_j - 1);
                    darm(map[man_i][man_j - 2], man_i, man_j - 2);
    
                    man_j--;//绘制结束后,要改变MAN的实时位置信息,并break跳出循环
                    break;
                }
            break;//再次break跳出switch语句
        case 38://上移,向上键的ASCII码
            //for循环遍历判断MAN向上移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i - 1][man_j] * map[man_i - 2][man_j] == table[i][0] ||
                    map[man_i][man_j] * map[man_i - 1][man_j] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i - 1][man_j] = table[i][2];
                    if (table[i][3] != -1)map[man_i - 2][man_j] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i - 1][man_j], man_i - 1, man_j);
                    darm(map[man_i - 2][man_j], man_i - 2, man_j);
                    man_i--;
                    break;
                }
            break;
        case 39://右移,向右键的ASCII码
            //for循环遍历判断MAN向右移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i][man_j + 1] * map[man_i][man_j + 2] == table[i][0] ||
                    map[man_i][man_j] * map[man_i][man_j + 1] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i][man_j + 1] = table[i][2];
                    if (table[i][3] != -1)map[man_i][man_j + 2] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i][man_j + 1], man_i, man_j + 1);
                    darm(map[man_i][man_j + 2], man_i, man_j + 2);
                    man_j++;
                    break;
                }
            break;
        case 40://下移,向下键的ASCII码
            //for循环遍历判断MAN向下移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i + 1][man_j] * map[man_i + 2][man_j] == table[i][0] ||
                    map[man_i][man_j] * map[man_i + 1][man_j] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i + 1][man_j] = table[i][2];
                    if (table[i][3] != -1)map[man_i + 2][man_j] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i + 1][man_j], man_i + 1, man_j);
                    darm(map[man_i + 2][man_j], man_i + 2, man_j);
                    man_i++;
                    break;
                }
            break;
        default:
            break;
        }
    }
    

    7.最后,我们给窗口载入一张完美的背景图片,至此就可以实现完整推箱子小游戏了。

    实际效果图:


    效果图-背景

    最后整合的代码就是:

    /*
    作者:DawsonLee
    Email:dawsonlee1790@foxmail.com
    */
    
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<graphics.h>
    
    #define BLACK         0 //黑格:用于填充图像
    #define WHITEWALL     1 //白墙:用于阻挡主角移动
    #define BLUEWALL      2 //蓝格:用于表示可移动空间
    #define BALL          3 //点:用于指定箱子放置位置
    #define BOX       4 //箱子:用于表示初始箱子位置
    #define REDBOX        5 //变色的箱子:用于表示这个点位置已经放置箱子
    #define MAN       6 //主角:用于表示主角位置
    #define MANBALL       7 //主角站在点上
    
    
    
    
    int main()
    {
        int map[13][16];//创建一个map数组用来存储矩阵信息
        int man_i, man_j;//用来存储人物的位置信息
        int readmap(int map[13][16], int &man_x, int &man_y);
        void darm(int a, int ii, int jj);
    
        readmap(map, man_i, man_j);//调用readmap()函数。
    
    
        initgraph(800, 650);//生成一个宽800,高650的窗口。假设矩阵元素对应的每个小矩形是50*50的,那个窗口就应该是这么大。
    
        //载入一张漂亮的背景图片
        PIMAGE imgui;
        imgui = newimage();
        getimage(imgui, "pic/背景.jpg");
        putimage(0, 0, imgui);
    
        for (int i = 0; i<13; i++)
            for (int j = 0; j < 16; j++)
            {
                darm(map[i][j], i, j);//遍历绘制每个小矩形
            }
    
        void move(char get, int map[13][16], int &man_i, int &man_j);
        char get;
        for (; is_run(); delay_fps(60))//is_run()表示窗口一直显示,delay_fps(60)表示窗口以60帧的频率刷新
        {
            get = getch();//等待用户输入键盘按键
            move(get, map, man_i, man_j);
        }
    
        closegraph();//关闭窗口
        return 0;
    }
    
    int readmap(int map[13][16], int &man_i, int &man_j)//map[13][16]是形参,其中&man_i表是的是man_i的引用,引用传递的意义在于:传入参数会因函数中的操作而改变
    {
        FILE *fp = fopen("MAP.txt", "rb");//以只读形式打开文件MAP.txt
        if (!fp) {
            printf("打开文件失败,文件位置你可能放错了\n");
            exit(1);//打开失败则结束程序
        }
        int i, j;
        char str[10];//用于存储从文本文件读取的字符串信息
        for (i = 0; i<13; i++)
            for (j = 0; j < 16; j++) 
            {
                fscanf(fp, "%s", str); //从文件中读取一个字符串,遇到空格结束
                map[i][j] = atoi(str);//将字符串转变为整型,并赋值给map[i][j]
                if (map[i][j] == MAN) //如果找到了MAN的位置,则返回坐标在 man_i和 man_j 中
                {
                    man_i = i;
                    man_j = j;
                }
            }
        return 0;
    }
    /*
    下面是在图形库绘制小矩形中图像的功能模块
    这部分涉及C++中的知识,C语言新手不必过多纠结不懂的东西。但要知道,创建的源文件要是cpp格式的
    */
    
    void darm(int a, int ii, int jj)
    {
        PIMAGE img;//声明
        img = newimage();//实例化
        switch (a)
        {
        case BLACK:break;
        case WHITEWALL:
        {
            getimage(img, "pic/墙.jpg");//getimage()和putimage请翻阅ege图形库帮助手册。
            putimage(jj * 50, ii * 50, img);//在ii行jj列绘制一个50*50像素的矩形图像
            break;
        }
        case BLUEWALL:
        {
            getimage(img, "pic/路.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case BALL:
        {
            getimage(img, "pic/目的地.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case BOX:
        {
            getimage(img, "pic/箱子.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case MAN:
        {
            getimage(img, "pic/人.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case MANBALL:
        {
            getimage(img, "pic/人.jpg");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        case REDBOX:
        {
            getimage(img, "pic/红箱子.png");
            putimage(jj * 50, ii * 50, img);
            break;
        }
        }
        ege::delimage(img);
    }
    
    /*
    传入函数的参数 get表示用户输入的键盘按键,传入实时数组map的信息,传入实时MAN的位置信息man_i,man_j。
    */
    void move(char get, int map[13][16], int &man_i, int &man_j)
    {
        //首先把情况的变化存在table[12][4]数组中
        int table[12][4];
    
        table[0][0] = MAN * BLUEWALL;
        table[1][0] = MAN * BALL;
        table[2][0] = MAN * BOX * BLUEWALL;
        table[3][0] = MAN * BOX * BALL;
        table[4][0] = MAN * REDBOX * BLUEWALL;
        table[5][0] = MAN * REDBOX * BALL;
        table[6][0] = MANBALL * BLUEWALL;
        table[7][0] = MANBALL * BALL;
        table[8][0] = MANBALL * BOX * BLUEWALL;
        table[9][0] = MANBALL * BOX * BALL;
        table[10][0] = MANBALL * REDBOX * BLUEWALL;
        table[11][0] = MANBALL * REDBOX * BALL;
    
        table[0][1] = BLUEWALL;
        table[1][1] = BLUEWALL;
        table[2][1] = BLUEWALL;
        table[3][1] = BLUEWALL;
        table[4][1] = BLUEWALL;
        table[5][1] = BLUEWALL;
        table[6][1] = BALL;
        table[7][1] = BALL;
        table[8][1] = BALL;
        table[9][1] = BALL;
        table[10][1] = BALL;
        table[11][1] = BALL;
    
        table[0][2] = MAN;
        table[1][2] = MANBALL;
        table[2][2] = MAN;
        table[3][2] = MAN;
        table[4][2] = MANBALL;
        table[5][2] = MANBALL;
        table[6][2] = MAN;
        table[7][2] = MANBALL;
        table[8][2] = MAN;
        table[9][2] = MAN;
        table[10][2] = MANBALL;
        table[11][2] = MANBALL;
    
        table[0][3] = -1;
        table[1][3] = -1;
        table[2][3] = BOX;
        table[3][3] = REDBOX;
        table[4][3] = BOX;
        table[5][3] = REDBOX;
        table[6][3] = -1;
        table[7][3] = -1;
        table[8][3] = BOX;
        table[9][3] = REDBOX;
        table[10][3] = BOX;
        table[11][3] = REDBOX;
    
    
    
        int i;
        void darm(int a, int ii, int jj);//要用到绘制函数,所以先声明
        switch (get)//switch判断用户输入的键盘信息
        {
        case 37:   //左移,向左键的ASCII码
            //for循环遍历判断MAN向左移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i][man_j - 1] * map[man_i][man_j - 2] == table[i][0] ||
                    map[man_i][man_j] * map[man_i][man_j - 1] == table[i][0])
                {
                    //找到对应的情况后执行以下语句
    
                    map[man_i][man_j] = table[i][1];
                    map[man_i][man_j - 1] = table[i][2];
                    if (table[i][3] != -1)map[man_i][man_j - 2] = table[i][3];//如果不需要改变第三个格子的图像,就不赋值。也就是如果table[i][3]==-1时。
    
                    //遍历绘制对应的三个格子信息
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i][man_j - 1], man_i, man_j - 1);
                    darm(map[man_i][man_j - 2], man_i, man_j - 2);
    
                    man_j--;//绘制结束后,要改变MAN的实时位置信息,并break跳出循环
                    break;
                }
            break;//再次break跳出switch语句
        case 38://上移,向上键的ASCII码
            //for循环遍历判断MAN向上移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i - 1][man_j] * map[man_i - 2][man_j] == table[i][0] ||
                    map[man_i][man_j] * map[man_i - 1][man_j] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i - 1][man_j] = table[i][2];
                    if (table[i][3] != -1)map[man_i - 2][man_j] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i - 1][man_j], man_i - 1, man_j);
                    darm(map[man_i - 2][man_j], man_i - 2, man_j);
                    man_i--;
                    break;
                }
            break;
        case 39://右移,向右键的ASCII码
            //for循环遍历判断MAN向右移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i][man_j + 1] * map[man_i][man_j + 2] == table[i][0] ||
                    map[man_i][man_j] * map[man_i][man_j + 1] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i][man_j + 1] = table[i][2];
                    if (table[i][3] != -1)map[man_i][man_j + 2] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i][man_j + 1], man_i, man_j + 1);
                    darm(map[man_i][man_j + 2], man_i, man_j + 2);
                    man_j++;
                    break;
                }
            break;
        case 40://下移,向下键的ASCII码
            //for循环遍历判断MAN向下移动对应的是哪种情况
            for (i = 0; i < 12; i++)
                if (map[man_i][man_j] * map[man_i + 1][man_j] * map[man_i + 2][man_j] == table[i][0] ||
                    map[man_i][man_j] * map[man_i + 1][man_j] == table[i][0])
                {
                    map[man_i][man_j] = table[i][1];
                    map[man_i + 1][man_j] = table[i][2];
                    if (table[i][3] != -1)map[man_i + 2][man_j] = table[i][3];
                    darm(map[man_i][man_j], man_i, man_j);
                    darm(map[man_i + 1][man_j], man_i + 1, man_j);
                    darm(map[man_i + 2][man_j], man_i + 2, man_j);
                    man_i++;
                    break;
                }
            break;
        default:
            break;
        }
    }
    

    8.附上整个工程项目的文件

    9.通过学习,您还可以加上诸多功能,比如音乐的播放,用户登陆和地图的绘制等。下面给一个有上述功能的推箱子游戏

    源码版本

    Release版本(可执行文件 .exe)

    项目展示视频

    相关文章

      网友评论

        本文标题:C语言推箱子小游戏教程

        本文链接:https://www.haomeiwen.com/subject/fooruftx.html