C语言 推箱子游戏 图形界面版EGE

使用C语言和EGE图形库实现简单好看的推箱子小游戏

如果您喜欢我的文章可以点赞支持一下。
如果您对我的程序有什么意见和建议欢迎在评论区发表评论。

这是我目前为止写的,无论从时间上还是从工程量上来说都是最长的一个小程序。
从初学EGE到攻克所有的技术难点,总共历时五天。
全代码一千一百多行。

在此向帮助过我解决问题的陌生人和老师表示由衷的感谢。

首先展示几张程序的运行效果:

主菜单界面
C语言 推箱子游戏 图形界面版EGE_第1张图片
游戏进行中界面:
C语言 推箱子游戏 图形界面版EGE_第2张图片
C语言 推箱子游戏 图形界面版EGE_第3张图片
C语言 推箱子游戏 图形界面版EGE_第4张图片
关卡选择界面
C语言 推箱子游戏 图形界面版EGE_第5张图片
游戏商城界面
C语言 推箱子游戏 图形界面版EGE_第6张图片
地图编辑器界面
C语言 推箱子游戏 图形界面版EGE_第7张图片
程序中除操控人物移动外所有操作均使用鼠标操作

下面进入技术环节:

C语言 EGE实现图形界面版推箱子

编译环境: Windows VS2019
其他编译器,可通过查看下文的“注意事项”将代码更正为其他平台可正常版本

需求:
界面之间互相跳转、选择关卡、控制人物移动推箱子、游戏商城、地图编辑器

思路:
使用EGE库函数在图形界面上画出图片。使用三维数组存储所有关卡。
使用wasd键控制地图数组中的值,根据值的改变实现界面的改变。
地图编辑器根据鼠标移动到的像素坐标判断他属于哪一个格子,鼠标点击则再改格子的位置画出相应图片。

做法:
游戏部分:双重循环找出二维数组中人物的位置,并根据按键和条件改变数组中的值,然后根据数组中的值画出这个数组所对应的地图。
编辑器部分:以格子宽度,通过将循环初始值0加格子边框距离边界的像素值,小于这个像素值加总的格子的宽度,最后内外层循环各循环16次,再根据这个像素值区分鼠标当前坐标所在的格子区域的xy坐标,当鼠标点击左键时在对应xy处画出对应图片。

具体详细做法清查看代码。

使用到知识点:
三维二维数组,写入文件,函数之间相互调用,EGE相关操作

难点:
EGE各种函数的使用
函数之间相互调用
根据按键改变数组中的值
编辑器循环确定鼠标对应网格坐标(不在左上角的情况)

说明:
在写程序的过程中,因为比较赶进度,所以在ege函数的用法和代码规范上可能存在较多缺陷,还请大家多多包涵。

如需要程序需要的图片素材请对我留言。

注意:

因为该程序需要用到ege库函数,所以在编译使用该代码之前需要配置ege图形库
并使用c++编译器进行性编译。

如果要在VS之外的编译环境编译,需要将
文件打开函数fopen_s(&fp, buff, “w”);更改为fp = fopen(buff, “w”);
fprintf_s更改为fprintf fprintf_s()更改为fprintf

代码实现:
#include        //ege
#include    //输入
#include   //文件操作

#define LEUPINX 0      //地图最左上角X坐标
#define LEUPINY 0      //地图最左上角Y坐标
#define N 16           //地图的宽高

//设置图像对象图像大小函数:setimage();
//参数:图片对象名称,需要修改的图片对象中图片的宽高
void setimage(PIMAGE img, int pwidth, int pheight);
//自定义图片大小指定位置输出函数:toddback();
//参数:图片路径,图片宽高, 图片位置xy
void toddback(const char charr[100], int bawidth, int baheight, int backx, int backy);
//创建图片对象,锁定图片大小为全屏
PIMAGE tdoimage(const char charr[100]);     //背景图片数组

mouse_msg msg;  //主菜单选择鼠标信息
mouse_msg msg2; //选择游戏模式鼠标信息
mouse_msg msg3; //游戏中返回主菜单鼠标信息
mouse_msg msg4; //游戏中下一关鼠标信息
mouse_msg msg5; //商城界面鼠标信息

int clonum;     //初始从第几关开始游戏

//关卡选择界面
void chcheck()              //关卡选择界面
{
    setbkmode(TRANSPARENT);
    setcolor(WHITE);
    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\开始游戏选择背景图.jpg", getwidth(), getheight(), 0, 0);
    //背景图与模式选择界面相同

    setfont(36, 0, "黑体");   //设置字体和字号
    PIMAGE backstep = newimage();         //为文本背景图片创建新的对象
    getimage(backstep, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\用于关卡选择界面.png");//步数信息文本背景图片
    setimage(backstep, 200, 55);       //设置背景图片大小
    putimage(50, 70, backstep);        //在指定位置画出图片
    //一行四个按钮                     //一个按钮宽度200,高度55,上边距70,左边距50,标准
    outtextxy(95, 80, "第一关");

    putimage(100 + 200, 70, backstep); outtextxy(95 + 250, 80, "第二关");    //第二关
    putimage(150 + 400, 70, backstep); outtextxy(95 + 500, 80, "第三关");
    putimage(200 + 600, 70, backstep); outtextxy(95 + 750, 80, "第四关");   
    putimage(50, 210, backstep); outtextxy(95, 80 + 140, "第五关");
    putimage(100 + 200, 210, backstep); outtextxy(95 + 250, 80 + 140, "第六关");
    putimage(150 + 400, 210, backstep); outtextxy(95 + 500, 80 + 140, "第七关");
    putimage(200 + 600, 210, backstep); outtextxy(95 + 750, 80 + 140, "第八关");
    putimage(50, 350, backstep); outtextxy(95, 80 + 280, "第九关");
    putimage(100 + 200, 350, backstep); outtextxy(95 + 250, 80 + 280, "第十关");
    putimage(150 + 400, 350, backstep); outtextxy(95 + 482, 80 + 280, "第十一关");

    delimage(backstep);               //用完图片对象需要释放

    while (true)
    {
        msg2 = getmouse();

        if (msg2.is_move())          //第二条鼠标点击并移动后break,继续
            break;
        delay_fps(59);
    }

    while (true)                    //鼠标点击开始游戏按钮时进入下一步
    {
        msg2 = getmouse();

        if (msg2.x >= 50 && msg2.y >= 70 && msg2.x <= 250 && msg2.y <= 125)
            if (msg2.is_left()) { clonum = 1; break; }  //第一关开始

        if (msg2.x >= 300 && msg2.y >= 70 && msg2.x <= 500 && msg2.y <= 125)
            if (msg2.is_left()) { clonum = 2; break; }

        if (msg2.x >= 550 && msg2.y >= 70 && msg2.x <= 750 && msg2.y <= 125)
            if (msg2.is_left()) { clonum = 3; break; }

        if (msg2.x >= 800 && msg2.y >= 70 && msg2.x <= 1000 && msg2.y <= 125)
            if (msg2.is_left()) { clonum = 4; break; }

        if (msg2.x >= 50 && msg2.y >= 210 && msg2.x <= 250 && msg2.y <= 265)
            if (msg2.is_left()) { clonum = 5; break; }

        if (msg2.x >= 300 && msg2.y >= 210 && msg2.x <= 500 && msg2.y <= 265)
            if (msg2.is_left()) { clonum = 6; break; }

        if (msg2.x >= 550 && msg2.y >= 210 && msg2.x <= 750 && msg2.y <= 265)
            if (msg2.is_left()) { clonum = 7; break; }

        if (msg2.x >= 800 && msg2.y >= 210 && msg2.x <= 1000 && msg2.y <= 265)
            if (msg2.is_left()) { clonum = 8; break; }

        if (msg2.x >= 50 && msg2.y >= 350 && msg2.x <= 250 && msg2.y <= 405)
            if (msg2.is_left()) { clonum = 9; break; }

        if (msg2.x >= 300 && msg2.y >= 350 && msg2.x <= 500 && msg2.y <= 405)
            if (msg2.is_left()) { clonum = 10; break; }

        if (msg2.x >= 550 && msg2.y >= 350 && msg2.x <= 750 && msg2.y <= 405)
            if (msg2.is_left()) { clonum = 11; break; }
        //循环?
        delay_fps(100);
    }
}
//关卡选择界面

//选择模式界面
void selsch()
{
    setbkmode(TRANSPARENT);
    setcolor(WHITE);
    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\开始游戏选择背景图.jpg", getwidth(), getheight(), 0, 0);

    //新游戏文字
    PIMAGE degnewbuttback = newimage();//为背景图片创建新的对象
    getimage(degnewbuttback, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\选择界面标准.png");            //步数信息文本背景图片
    setimage(degnewbuttback, 250, 70); //设置背景图片大小
    putimage_withalpha(NULL, degnewbuttback, 404, 90);//输出位置
    delimage(degnewbuttback);                //用完图片对象需要释放


    setcolor(WHITE);                //文字颜色白色
    setfont(36, 0, "黑体");         //文字自号和字体,第二个参数默认0
    outtextxy(454, 106, "新 游 戏");//输出“开始游戏”文字
    //新游戏文字

    //选择关卡文字
    PIMAGE degifbuttback = newimage();//为背景图片创建新的对象
    getimage(degifbuttback, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\选择界面标准.png");
    setimage(degifbuttback, 250, 70); //设置背景图片大小
    putimage_withalpha(NULL, degifbuttback, 404, 270);//输出位置
    delimage(degifbuttback);        //用完图片对象需要释放

    setcolor(WHITE);                //文字颜色白色
    setfont(36, 0, "黑体");         //文字自号和字体,第二个参数默认
    outtextxy(454, 286, "选择关卡");//输出“开始游戏”文字
    //选择关卡文字

    while (true)
    {
        msg = getmouse();

        if (msg.is_move())          //鼠标移动时move为1,所以必须在鼠标移动后再次点击按钮才能代表第二条鼠标消息
            break;
        delay_fps(60);
    }

    while (true)                    //鼠标点击开始游戏按钮时进入下一步
    {
        msg2 = getmouse();

        if (msg2.x >= 404 && msg2.y >= 90 && msg2.x <= 404 + 250 && msg2.y <= 90 + 70)//鼠标移动到指定范围时
                                    //这个范围是背景图片xy到背景图片xy+背景图片的宽高                      
            if (msg2.is_left())     //鼠标左键点击时
            {
                clonum = 1;         //选择新游戏从第一关开始
                break;
            }

        if (msg2.x >= 404 && msg2.y >= 270 && msg2.x <= 404 + 250 && msg2.y <= 270 + 70)
            if (msg2.is_left())
            {
                chcheck();          //关卡选择

                break;
            }

        delay_fps(60);     //平均延迟1000/fps毫秒,用于稳定帧率控制
    }

}
//选择模式界面结束

//游戏商城界面
int figure;         //人物形象变量
double money = 5.0; //游戏资金变量

void gamall()       //游戏商城界面
{
    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\商城背景4.jpg", 1543, getheight(), 0, 0);

    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\派大星2.png", 283, 280, 50, 105);
    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\喜洋洋1.jpg", 250, 280, 420, 105);
    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\源氏商城图.jpg", 220, 280, 740, 105);

    setfont(32, 0, "微软雅黑");
    setfontbkcolor(BLACK);
    setbkmode(OPAQUE);      //文字背景不透明
    setcolor(YELLOW);
    outtextxy(120, 410, "价格:免费");
    outtextxy(470, 410, "价格:免费");
    outtextxy(770, 410, "价格:200元");

    outtextxy(120, 480, "正在使用");
    outtextxy(470, 480, "使用          ");
    outtextxy(770, 480, "购买          ");


    xyprintf(0, 60, "剩余资产:%.2lf 元", money);

    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\返回按钮.png", 50, 50, 0, 0);

    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\用于关卡选择界面.png", 200, 50, 850, 0);
    setbkmode(1);      //文字背景透明
    setcolor(WHITE);
    outtextxy(900, 10, "开始游戏");

    int op = 0;         //没有1购买过高级形象op为0

    setbkmode(OPAQUE);      //文字背景不透明
    setcolor(YELLOW);
    while (true)                    //鼠标点击开始游戏按钮时进入下一步
    {
        msg5 = getmouse();

        if (msg5.x >= 0 && msg5.y >= 0 && msg5.x <= 50 && msg5.y <= 50) 
            if (msg5.is_left())     //鼠标左键点击时
            {
                void gamegoint();
                gamegoint();        //主菜单界面
                break;
            }
        
        if (msg5.x >= 470 && msg5.y >= 0 && msg5.x <= 1050 && msg5.y <= 50)
            if (msg5.is_left())     //鼠标左键点击时
            {
                selsch();           //选择模式界面
                break;
            }

        if (msg5.x >= 120 && msg5.y >= 480 && msg5.x <= 120 + 130 && msg5.y <= 480 + 32)
            if (msg5.is_left())     //鼠标左键点击时
            {
                op = 0;
                figure = 0;
                outtextxy(120, 480, "              "); outtextxy(120, 480, "正在使用");
                outtextxy(470, 480, "使用          "); outtextxy(770, 480, "使用          ");
            }
        if (msg5.x >= 470 && msg5.y >= 480 && msg5.x <= 470 + 130 && msg5.y <= 480 + 32)
            if (msg5.is_left())     //鼠标左键点击时
            {
                op = 0;     //op控制是否可以点击源氏形象下方使用按钮
                figure = 1;
                outtextxy(470, 480, "              "); outtextxy(120, 480, "使用          ");
                outtextxy(470, 480, "正在使用"); outtextxy(770, 480, "使用          ");
            }
        if (msg5.x >= 770 && msg5.y >= 480 && msg5.x <= 770 + 130 && msg5.y <= 480 + 32)
            if (msg5.is_left())     //鼠标左键点击时
            {
                if (money < 200)
                {
                    outtextxy(770, 550, "资金不足");
                    continue;               //点击购买按钮,如果游戏资金小于200则重新循环
                }
                
                if (op) continue;           //如果已经购买过一次则直接进入下一次循环
                outtextxy(770, 550, "已购买    ");
                money -= 200;               //购买形象后,资金-200
                xyprintf(0, 60, "剩余资产:%.2lf 元", money);

                figure = 2;
                outtextxy(770, 480, "              "); outtextxy(120, 480, "使用          ");
                outtextxy(470, 480, "使用          "); outtextxy(770, 480, "正在使用");
                op = 1;                     //购买过op为1
            }

        delay_fps(90);
    }
    
}

#define MAXP 11      //最大关数
int mapbox[MAXP][N][N] =                       //三维数组
{
    {       //第一关
      {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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 1, 9, 1, 1, 1, 1, 0, 0, 0, 0},
      {0, 0, 0, 0, 1, 1, 1, 3, 9, 3, 2, 1, 0, 0, 0, 0},
      {0, 0, 0, 0, 1, 2, 9, 3, 4, 1, 1, 1, 0, 0, 0, 0},
      {0, 0, 0, 0, 1, 1, 1, 1, 3, 1, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 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, 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},
      {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 1, 1, 1, 1, 3, 1, 1, 0, 0, 0, 0, 0},
      {0, 0, 0, 1, 9, 9, 9, 9, 9, 9, 9, 1, 0, 0, 0, 0},
      {0, 0, 1, 9, 9, 1, 1, 1, 9, 9, 9, 9, 1, 0, 0, 0},
      {0, 0, 1, 9, 2, 9, 9, 9, 2, 9, 9, 9, 1, 0, 0, 0},
      {0, 0, 1, 9, 9, 9, 9, 9, 9, 9, 1, 9, 1, 0, 0, 0},
      {0, 0, 1, 9, 1, 3, 1, 9, 9, 3, 9, 9, 1, 0, 0, 0},
      {0, 0, 1, 9, 1, 9, 1, 9, 1, 1, 1, 9, 1, 0, 0, 0},
      {0, 0, 1, 4, 1, 9, 9, 9, 9, 9, 9, 9, 1 ,0, 0 ,0},
      {0, 0, 1, 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},
    },
    {
        {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, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 1, 9, 9, 9, 1, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 1, 2, 9, 9, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 1, 9, 4, 1, 1, 9, 3, 2, 1, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 9, 9, 1, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 1, 1, 9, 9, 1, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 1, 9, 3, 2, 1, 1, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 1, 9, 9, 9, 3, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 1, 9, 9, 1, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 1, 9, 9, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 9, 9, 9, 1, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 2, 3, 9, 1, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 2, 1, 3, 3, 9, 9, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 9, 1, 9, 2, 1, 9, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 9, 4, 3, 9, 1, 2, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 1, 1, 1, 9, 9, 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, 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, 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, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 9, 1, 1, 1, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 2, 3, 2, 1, 9, 9, 9, 1, 0, 0, 0},
        {0, 0, 1, 9, 9, 3, 1, 1, 9, 3, 9, 1, 0, 0, 0, 0},
        {0, 1, 1, 9, 3, 9, 9, 9, 9, 4, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 1, 2, 1, 1, 2, 1, 1, 1, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 1, 0, 0, 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, 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, 0},
        {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0},
        {0, 0, 1, 9, 3, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0},
        {0, 0, 1, 9, 3, 9, 1, 9, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 1, 9, 3, 9, 9, 9, 0, 0, 1, 1, 0, 0, 0, 0},
        {0, 1, 2, 3, 2, 1, 4, 2, 2, 2, 9, 1, 0, 0, 0, 0},
        {0, 0, 1, 9, 3, 9, 1, 9, 0, 0, 1, 1, 0, 0, 0, 0},
        {0, 0, 1, 9, 3, 9, 1, 9, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 1, 9, 3, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    },
    //第7关
    {
        {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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 1, 2, 3, 9, 2, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 1, 9, 9, 9, 1, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 1, 1, 9, 9, 9, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 1, 3, 1, 4, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 1, 9, 9, 9, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 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, 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},
    },
    //8
    {
        {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, 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, 9, 9, 9, 1, 9, 9, 9, 2, 1, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 3, 2, 1, 9, 9, 9, 1, 0, 0, 0},
        {0, 0, 0, 1, 9, 1, 9, 1, 9, 9, 9, 9, 1, 0, 0, 0},
        {0, 0, 0, 1, 4, 9, 9, 1, 3, 9, 9, 9, 1, 0, 0, 0},
        {0, 0, 0, 1, 1, 9, 9, 1, 9, 9, 1, 1, 1, 0, 0, 0},
        {0, 0, 0, 0, 1, 9, 9, 9, 9, 9, 1, 1, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 1, 9, 9, 9, 9, 1, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 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, 0, 0, 0},
    },
    //9
    {
        {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, 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, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 9, 9, 9, 9, 2, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 9, 9, 9, 9, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 9, 9, 9, 9, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 9, 9, 9, 9, 9, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 1, 1, 1, 1, 1, 9, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 3, 9, 9, 9, 9, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 4, 9, 9, 9, 9, 9, 9, 1, 0, 0, 0, 0},
        {0, 0, 0, 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},
    },
    //10

    {
        {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, 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, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 2, 1, 2, 9, 9, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 3, 1, 3, 1, 9, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 1, 9, 1, 9, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 9, 1, 9, 1, 3, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 4, 9, 9, 1, 2, 1, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 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, 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},
        {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},
        {0, 0, 1, 9, 9, 2, 1, 2, 1, 9, 9, 9, 2, 1, 0, 0},
        {0, 0, 1, 1, 1, 9, 9, 3, 1, 1, 9, 1, 9, 1, 0, 0},
        {0, 0, 1, 9, 9, 9, 1, 9, 9, 9, 3, 9, 9, 1, 0, 0},
        {0, 0, 1, 9, 3, 9, 1, 1, 9, 1, 1, 1, 9, 1, 0, 0},
        {0, 0, 1, 2, 1, 3, 9, 3, 2, 9, 9, 9, 9, 1, 0, 0},
        {0, 0, 1, 1, 1, 9, 1, 9, 1, 1, 9, 9, 1, 1, 0, 0},
        {0, 0, 1, 9, 9, 9, 1, 3, 9, 9, 1, 9, 9, 1, 0, 0},
        {0, 0, 1, 9, 1, 1, 9, 2, 1, 9, 1, 9, 9, 1, 0, 0},
        {0, 0, 1, 9, 9, 1, 9, 1, 1, 9, 9, 1, 9, 1, 0, 0},
        {0, 0, 1, 4, 9, 3, 9, 9, 3, 2, 9, 9, 2, 1, 0, 0},
        {0, 0, 1, 1, 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},
    },
};

int maptemp[N][N];          //一个临时的地图数组,最后将这个数组中的值保存进文件中
//编辑器界面
void editmap()              //地图编辑器函数
{
    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\OW背景2.jpg", getwidth()+70, getheight(), 0, 0);//背景
    //获取地图需要用到的图片
    PIMAGE floimg2 = newimage(); PIMAGE walimg2 = newimage(); PIMAGE perimg2 = newimage(); PIMAGE boximg2 = newimage(); PIMAGE tarimg2 = newimage();
    getimage(floimg2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\石质地板.png");
    getimage(walimg2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\手绘树木_3.png");
    switch (figure) { case 0: getimage(perimg2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\派大星实心.png"); break;
    case 1: getimage(perimg2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\喜洋洋2.png");    break;
    case 2: getimage(perimg2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\源氏皮肤.png");   break; } 
    getimage(boximg2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\单个箱子.png"); 
    getimage(tarimg2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\五角星实心.png"); 
    setimage(walimg2, 34, 34);
    setimage(floimg2, 34, 34); setimage(perimg2, 34, 34); setimage(boximg2, 34, 34); setimage(tarimg2, 34, 34);
    PIMAGE bkzkd2 = newimage();
    getimage(bkzkd2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\不可走空地代替.png"); setimage(bkzkd2, 34, 34);

    //rectangle(0, 590, 34, 624);       //空白矩形
    //用于输出屏幕下方工具图片
    putimage(0, 590, bkzkd2);
    putimage(50, 590,walimg2); putimage(100, 590, tarimg2); putimage(150, 590, boximg2);
    putimage(200, 590, perimg2); putimage(250, 590, floimg2);

    //文字说明
    setfont(20, 0, "楷体");
    xyprintf(5,480,"第一个工具代表不可走空地,");
    xyprintf(5, 500, "在游戏中的显示效果为不显示,");
    xyprintf(5, 520, "编辑器中可省略不写,");
    xyprintf(5, 540, "但可走空地工具:第六个工具");
    xyprintf(5, 560, "在设计地图时不可以遗忘!");

    //返回按钮
    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\返回按钮.png", 50, 50, 0, 60);


    int cinst = 0;                  //控制使用工具变量,用于控制allimg等于不同的图片对象

    PIMAGE allimg = perimg2;        //图片对象变量

    //用于输出网格
    for (int i = 1; i < 18; i++)
        for (int j = 10; j < 26; j++)
            rectangle(9 * 34, 3 * 34, j * 34, i * 34);


    mouse_msg msg6;
    setfillcolor(WHITE);
    setbkmode(1);

    setfont(40, 0, "楷体");
    setcolor(WHITE);
    int x = 0, y = 0;

    setfillcolor(BLACK);

    while (true)                //在主界面点击结束鼠标又移动后,编辑器界面鼠标点击世界才会生效
    {
        msg5 = getmouse();
        if (msg5.is_move())
            break;
        delay_fps(60);
    }

    sys_edit editBox;           //创建输入框 类

    editBox.create(true);       //设置输入框是否为多行,true表示是多行

    editBox.move(325, 195);     //输入框位置
    editBox.size(400, 250);     //输入框大小
    editBox.setfont(24, 0, "宋体");//输入框内文本属性
    editBox.visable(true);      //设置输入框可见,默认不可见
    editBox.setfocus();         //设置输入框获取焦点
    const int buffSize = 100;   //文本输入框需要的格式
    char buff[100] = {0};       //从文本框输入的内容会被保存到这个字符数组里
    editBox.settext("请清空该段文字后,输入正确的需要将地图保存到的文件路径,否则将导致程序出错并无法保存创建的地图!");

    bool flag_press = false;      //标记是否按下
    int bufflen = 0;                //记录字符数组的长度

    for (; is_run(); delay_fps(60)) {       //获取字符数组中的数
        if (keystate(key_enter)) {
            if (!flag_press) {
                //获取输入框内容字符串
                editBox.gettext(buffSize, buff);

                bufflen = strlen(buff);

                editBox.settext("");
                break;
            }
            flag_press = true;
        }
        else {
            flag_press = false;
        }
    }
    editBox.visable(false);     //设置输入框不可见

    buff[bufflen - 2] = '\0';   //三条\0缺一不可,因为字符数组长度的最后使错误的转义字符,
    buff[bufflen - 1] = '\0';   //会导致寻找文件路径错误!!
    buff[bufflen] = '\0';

    while (true)
    {
        msg6 = getmouse();      //获取编辑器界面内的鼠标信息

        if (msg6.x >= 0 && msg6.y >= 590 && msg6.x <= 34 && msg6.y <= 624)          //根据点击的不同的区域来改变控制
        { if (msg.is_left()) { cinst = 0; } }
        if (msg6.x >= 50 && msg6.y >= 590 && msg6.x <= 34+50 && msg6.y <= 624)      //对象的变量以控制输出的对象
        { if (msg.is_left()) { cinst = 1; } }
        if (msg6.x >= 100 && msg6.y >= 590 && msg6.x <= 34+100 && msg6.y <= 624)
        { if (msg.is_left()) { cinst = 2; } }
        if (msg6.x >= 150 && msg6.y >= 590 && msg6.x <= 34+150 && msg6.y <= 624)    
        { if (msg.is_left()) { cinst = 3; } }
        if (msg6.x >= 200 && msg6.y >= 590 && msg6.x <= 34+200 && msg6.y <= 624)    
        { if (msg.is_left()) { cinst = 4; } }
        if (msg6.x >= 250 && msg6.y >= 590 && msg6.x <= 34+250 && msg6.y <= 624)    
        { if (msg.is_left()) { cinst = 9; } }

        if (cinst == 0) allimg = bkzkd2;        //根据变量不同的值来改变cinst引用的对象
        if (cinst == 1) allimg = walimg2;        
        if (cinst == 2) allimg = tarimg2;
        if (cinst == 3) allimg = boximg2;
        if (cinst == 4) allimg = perimg2;
        if (cinst == 9) allimg = floimg2;


        int i, j;                               //用于循环和确定格子的变量
        //实现像素和网格意义对应的效果,544为网格总宽度
        if (msg6.x >= 9 * 34 && msg6.y >= 34 && msg6.x <= 544 + 9 * 34 && msg6.y <= 544 + 34)
        {
            for (i = 34; i < 544 + 34; i += 34)
                for (j = 9 * 34; j < 544 + 9 * 34; j += 34) //确定像素对应的网格
                    if (msg6.x >= j && msg6.y >= i && msg6.x <= j + 34 && msg6.y <= i + 34)
                    {
                        x = j - 9 * 34;     //减去距离边界的像素
                        y = i - 34;
                    }
            if (msg6.is_left())
            {
                putimage(x + 9 * 34, y + 34, allimg);
                maptemp[y / 34][x / 34] = cinst;        //将工具对应的属性值赋给对应下标的地图数组内容
            }
        }

        bar(20, 0, 140, 50);        //清除痕迹
        xyprintf(20, 10, "%d  %d", x / 34, y / 34);

        if (msg6.x >= 0 && msg6.y >= 60 && msg6.x <= 50 && msg6.y <= 110)
            if(msg6.is_left())                  //点击返回按钮break
                break;

        delay_fps(100);
    }


    FILE* fp = NULL;       //定义文件指针
    
    fopen_s(&fp, buff, "a+");

    fprintf(fp, "{\n");

    for (int i = 0; i < N; i++)     //将地图信息以数组专属格式写入文件
    {
        fprintf(fp, "{");
        for (int j = 0; j < N; j++)
        {
            if (j == N - 1)
                fprintf(fp, "%d", maptemp[i][j]);
            else
                fprintf(fp, "%d, ", maptemp[i][j]);
        }
        fprintf(fp, "},\n");
    }
    fprintf(fp, "},\n");

    fclose(fp);                     //操作完文件之后关闭文件
    //创建完新的地图后需要手动将保存到文件中的地图数据复制到代码的三维数组中

    //释放对象
    delimage(floimg2);      //释放图片对象
    delimage(walimg2); 
    delimage(perimg2); 
    delimage(boximg2); 
    delimage(tarimg2); 
    //delimage(allimg);     //释放这个的对象会导致程序错误原因不明

    void gamegoint();
    gamegoint();            //完成编辑后返回主菜单界面

}
//编辑器界面


//主菜单界面
void gamegoint()        //游戏开始界面
{
    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\开局背景1_1.png", getwidth(), getheight(), 0, 0);
    //背景图片宽高为获取窗口界面宽高

    //推箱子文字
    setbkmode(TRANSPARENT);
    setcolor(YELLOW);
    setfont(120, 0, "微软雅黑");
    outtextxy(320, 80, " 推  箱  子");
    //推箱子文字


    //开始游戏文字
    PIMAGE gobuttback = newimage(); //为背景图片创建新的对象
    getimage(gobuttback, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\圆角2科技文本框.png");//步数信息文本背景图片
    setimage(gobuttback, 200, 50);  //设置背景图片大小
    putimage_withalpha(NULL, gobuttback, 432, 280);//输出位置

    setbkmode(TRANSPARENT);         //设置文字背景混合模式,一般使用文字背景色前都会加上
    setcolor(WHITE);                //文字颜色白色
    setfont(40, 0, "楷体");         //文字自号和字体,第二个参数默认0
    outtextxy(456, 286, "开始游戏");//输出“开始游戏”文字
    //开始游戏文字

    //游戏商城文字
    putimage_withalpha(NULL, gobuttback, 432, 380);
    outtextxy(456, 386, "游戏商城");  //输出“游戏商城”文字
    //游戏商城文字

    //地图编辑器文字
    putimage_withalpha(NULL, gobuttback, 432, 480);
    outtextxy(436, 486, "地图编辑器");  //输出“地图编辑器”文字
    //地图编辑器文字

    delimage(gobuttback);   //释放

    while (true)                       //鼠标点击开始游戏按钮时进入下一步
    {
        msg = getmouse();

        if (msg.x >= 432 && msg.y >= 280 && msg.x <= 432 + 200 && msg.y <= 280 + 50)//鼠标移动到指定范围时
                                                         //这个范围是背景图片xy到背景图片xy+背景图片的宽高                      
            if (msg.is_left())         //鼠标左键点击时  
            {
                selsch();               //选择模式界面
                break;
            }
        if (msg.x >= 432 && msg.y >= 380 && msg.x <= 432 + 200 && msg.y <= 380 + 50)                  
            if (msg.is_left())
            {
                gamall();               //游戏商城界面
                break;
            }
        if (msg.x >= 432 && msg.y >= 480 && msg.x <= 432 + 200 && msg.y <= 480 + 50)
            if (msg.is_left())
            {
                clonum = 1;
                editmap();               //地图编辑器界面
                break;
            }
        delay_fps(100);     //平均延迟1000/fps毫秒,用于稳定帧率控制
    }

    //cleardevice();          //清除屏幕

}
//游戏主菜单界面结束



void graminit()        //初始化
{
    //setinitmode(INIT_NOBORDER, 0, 0);     //完全全屏
    //initgraph(-1, -1);

    initgraph(1050, 640);         //初始化图形界面
    setcaption("ege推箱子");      //初始化标题

    gamegoint();        //主菜单界面
}


//流程部分

//0代表不可走空地,1代表墙,2代表未达成的目标,3代表箱子,4代表玩家,5代表已放箱子的目标,
//6代表人暂时所在的未达成的目标,7代表人暂时所在的已达成的目标,8代表箱子暂时所在的已达成的目标
//9代表可走空地

//增添一个关卡应该做的事情:
/*
在关卡选择函数中将添加一个按钮并添加一个按钮点击事件:if-范围;
将宏MAXP的值增加1,将关卡数组的值以恰当形式粘贴到三维数组最后一位
将每关卡背景图片数组里面的图片对象加1
*/




int i, j;                   //循环使用变量

void tranmap(PIMAGE floimg, PIMAGE walimg, PIMAGE perimg, PIMAGE boximg, PIMAGE tarimg,
    PIMAGE tarimg_2,int map[N][N])//翻译并输出地图
{//五个图片对象,空地,墙壁,箱子,目标
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
        {
            const int j_2 = LEUPINX + j * 40;
            const int i_2 = i * 40;
            switch (map[i][j])
            {
            case 1:
                putimage_withalpha(NULL, walimg, j_2, i_2); //墙壁//坐标全部待定
                break;
            case 2:
                putimage_withalpha(NULL, tarimg, j_2, i_2); //目标
                break;
            case 3:
            case 8:
                putimage_withalpha(NULL, boximg, j_2, i_2); //箱子
                break;
            case 4:
            case 6:
            case 7:
                putimage(j_2, i_2,  perimg); //人物
                break;
            case 5:
                putimage_withalpha(NULL, tarimg_2, j_2, i_2); //已达成目标
                break;
            case 9:
                putimage_withalpha(NULL, floimg, j_2, i_2);   //9代表可走空地输出空白地板
                break;
            default:
                //putimage_withalpha(NULL, floimg, j_2, i_2); //0代表不可走空地,不输出
                break;
            }

        }

}

int ststeabox(int map[][N])             //统计一张地图中未完成目标的个数
{
    int boxternum = 0;
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
            if (map[i][j] == 2)
                boxternum++;

    return boxternum;       //返回每一关目标的个数
}

int box_x, box_y;           //箱子的坐标
int boxnext_x, boxnext_y;   //箱子前面的坐标
int peonext_x, peonext_y;   //人前面的坐标
int concustom;              //控制关卡变量
int Boxternum;              //统计每一关目标个数变量
int stepnum;                //统计每一关已经行走的步数
int map_temp_last[N][N];    //用于储存人物的上一步的地图
int degfir;                 //为0表示该关卡是第一次移动人物

//0代表不可走空地,1代表墙,2代表未达成的目标,3代表箱子,4代表玩家,5代表已放箱子的目标,
//6代表人暂时所在的未达成的目标,7代表人暂时所在的已达成的目标,8代表箱子暂时所在的已达成的目标
//9代表可走空地
void modarrva(int kch, int map[N][N])       //修改数组值函数,键盘值,地图
{                                           //map地图为形参

    for (i = 0; i < N; i++)     //存储人物移动前上一步的地图信息
        for (j = 0; j < N; j++)
            map_temp_last[i][j] = map[i][j];

    int i, j;
    for (i = 0; i < N; i++)                 //遍历   i是y轴,j是x轴
        for (j = 0; j < N; j++)
        {
            if (map[i][j] != 4 && map[i][j] != 6 && map[i][j] != 7)    //找到人的位置
                continue;

            switch (kch)        // 用于确定不同方向上 箱子、箱子前面、人前面的坐标
            {
            case 'w':
                box_y = i - 1; box_x = j;           //箱子当前
                boxnext_y = i - 2; boxnext_x = j;   //箱子前面
                peonext_y = i - 1; peonext_x = j;   //人前面
                break;
            case 'a':
                box_y = i; box_x = j - 1;
                boxnext_y = i; boxnext_x = j - 2;
                peonext_y = i; peonext_x = j - 1;
                break;
            case 's':
                box_y = i + 1; box_x = j;
                boxnext_y = i + 2; boxnext_x = j;
                peonext_y = i + 1; peonext_x = j;
                break;
            case 'd':
                box_y = i; box_x = j + 1;
                boxnext_y = i; boxnext_x = j + 2;
                peonext_y = i; peonext_x = j + 1;
                break;
            }

            //解决数组越界
            if (box_x < 0) box_x = 0; if (box_y < 0) box_y = 0; if (boxnext_x < 0) boxnext_x = 0; if (boxnext_y < 0) boxnext_y = 0;
            if (peonext_x < 0) peonext_x = 0; if (peonext_y < 0) peonext_y = 0; if (box_x > N) box_x = N; if (box_y > N) box_y = N;
            if (boxnext_x > N) boxnext_x = N; if (boxnext_y > N) boxnext_y = N; if (peonext_x > N) peonext_x = N; if (peonext_y > N) peonext_y = N;

            //排除大的错误
            if (map[box_y][box_x] == 3 || map[box_y][box_x] == 8)    //如果人的前边是箱子
                if (map[boxnext_y][boxnext_x] == 1 || map[boxnext_y][boxnext_x] == 3 || map[boxnext_y][boxnext_x] == 8 || \
                    map[boxnext_y][boxnext_x] == 0)     //如果人的前边是不可行走的空地当做墙处理
                    return;     //如果箱子前边是墙或者是箱子直接结束修改函数

            if (map[box_y][box_x] == 1 || map[box_y][box_x] == 0)       //人的前边不能是墙
                return;         //如果人的前边是墙直接结束修改


            stepnum++;          //人物成功移动一次则步数+1
            degfir = 1;         //成功移动后,为1,表示这一步已经不是这个关卡的第一步了
            

            //箱子改变
            if (map[box_y][box_x] == 3 || map[box_y][box_x] == 8)    //如果人的前边是箱子
            {
                //箱子原地改变:
                if (map[box_y][box_x] == 3)       //如果箱子所在的位置是 一般箱子 
                    map[box_y][box_x] = 9;        //改变为空地
                else if (map[box_y][box_x] == 8)  //如果箱子所在的位置是 箱子暂时所在已达成的目标
                    map[box_y][box_x] = 5;        //改变为已达成的目标

                //箱子的下一步改变:

                if (map[boxnext_y][boxnext_x] == 2)         //如果箱子前面的格子是未放箱子的目标
                {
                    map[boxnext_y][boxnext_x] = 5;          //则该格子变为已放箱子的目标

                    Boxternum--;                            //箱子移动到目标上则目标数自减

                    if (Boxternum == 0)                     //当当前关目标数为0的时候,concustom为1,则进入下一关
                        concustom = 1;

                }
                else if (map[boxnext_y][boxnext_x] == 9)  //如果箱子前面是空地                        
                    map[boxnext_y][boxnext_x] = 3;        //则变为普通箱子
                else if (map[boxnext_y][boxnext_x] == 5)  //如果箱子前面是已经放过箱子的目标
                    map[boxnext_y][boxnext_x] = 8;        //则变为 箱子暂时所在已达成的目标
            }

            //原地改变
            if (map[i][j] == 6)
                map[i][j] = 2;    //走出去之后原地又变回2 未达成的目标
            if (map[i][j] == 7)
                map[i][j] = 5;    //走出去之后原地又变回5 已达成的目标
            if (map[i][j] == 4)
                map[i][j] = 9;    //走出去之后原地变回4 人的原型

            
            //人下一步改变
            if (map[peonext_y][peonext_x] == 9 || map[peonext_y][peonext_y] == 3)//如果他的下一步是普通箱子或者空地
                map[peonext_y][peonext_x] = 4;        //人是 普通的人

            if (map[peonext_y][peonext_x] == 2)       //如果他的下一步还是未达成的目标
                map[peonext_y][peonext_x] = 6;        //人是 人暂时所在未达成的目标

            if (map[peonext_y][peonext_x] == 5)       //如果人的下一步是已经达成的目标
                map[peonext_y][peonext_x] = 7;        //人是 人暂时所在已经达成的目标

            goto L1;    //修改完成后不需要遍历后面的数组,直接跳出所有循环
        }
            
        
        L1:;                        //用于跳出的标签

}       //修改数组值函数

int map_temp[N][N];     //每关的初始临时地图

void peomove(PIMAGE floimg, PIMAGE walimg, PIMAGE perimg, PIMAGE boximg, PIMAGE tarimg,         \
    PIMAGE tarimg_2, int map[N][N])       //接收键盘值控制人物移动
{
    int i, j;
    
    char k = 0;         //键盘值
    k = getch();        //等待从键盘输入一个值

    switch (k)
    {
    case 'w':
    case 'a':
    case 's':
    case 'd':
        modarrva(k, map); break;    //将键盘值、地图、每关未完成目标的个数传进操控函数中
    case 'r':

        for (i = 0; i < N; i++)     //按下r键重启本关
            for (j = 0; j < N; j++)
                map[i][j] = map_temp[i][j];
        stepnum = 0;                //本关步数清0
        Boxternum = ststeabox(map); //重新刷新本关目标个数
        break;
    case 'z':

        if (degfir != 1)            //撤回时,如果这一步是这一关卡的第一步则无法撤回
            break;
        for (i = 0; i < N; i++)
            for (j = 0; j < N; j++)
                map[i][j] = map_temp_last[i][j];
        
        break;
    }

    tranmap(floimg, walimg, perimg, boximg, tarimg, tarimg_2, map);    //输出地图

}

int clesta[MAXP] = { 0 };

void display(int thpass)         //用于输出游戏内的显示信息
{
    setfont(40, 0, "微软雅黑");  //设置文本属性
    setcolor(WHITE);             //设置文本颜色

    PIMAGE mestextback = newimage();        
    getimage(mestextback, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\科技文本框2.jpg");
    setimage(mestextback, 240, 60);
    putimage(720, 80, mestextback); 

    xyprintf(780, 90, "第 %d 关", thpass + 1);         //第几关thpass,因为读关卡是从0开始所以+1

    putimage(720, 180, mestextback);                 
    xyprintf(780, 190, "步数:%d",stepnum);            //在指定位置输出格式化字符串,步数

    putimage(720, 280, mestextback);
    xyprintf(740, 290, "目标数量:%d", Boxternum);     //本关剩余目标个数

    if (!Boxternum)
        clesta[thpass] = 1;            //如果本关目标数为0则,本关状态为已通过

    putimage(720, 380, mestextback);
    if(!clesta[thpass]) xyprintf(740, 390, "状态:未通过");//本关通关状态
    else        xyprintf(740, 390, "状态:已通过");    

    putimage(720, 480, mestextback);
    xyprintf(740, 490, "    下一关 ->");     //本关剩余目标个数

    delimage(mestextback);

    toddback("C:\\Users\\ASUS\\Desktop\\推箱子素材\\返回按钮.png", 50, 50, 0, 0);
    //返回主菜单按钮
}

void process()                      //流程函数
{
    PIMAGE floimg = newimage();     //创建地板的图片对象
    PIMAGE walimg = newimage();     //创建墙壁的图片对象
    PIMAGE perimg = newimage();     //创建人物的图片对象
    PIMAGE boximg = newimage();     //创建箱子的图片对象
    PIMAGE tarimg = newimage();     //创建目标的图片对象
    PIMAGE tarimg_2 = newimage();   //目标已达成状态对象

    getimage(floimg, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\石质地板.png");     //从文件中获取图片
    getimage(walimg, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\手绘树木_3.png");   //墙壁,图片待定

    switch (figure)         //根据figure获取不同的人物形象
    {
    case 0:
        getimage(perimg, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\派大星实心.png"); break;   //人物
    case 1:
        getimage(perimg, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\喜洋洋2.png");    break;
    case 2:
        getimage(perimg, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\源氏皮肤.png");   break;
    }

    getimage(boximg, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\单个箱子.png");     //箱子
    getimage(tarimg, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\五角星实心.png");   //目标
    getimage(tarimg_2, "C:\\Users\\ASUS\\Desktop\\推箱子素材\\单个箱子已达成状态.png");   //目标

    setimage(floimg, 40, 40);           //设置图像对象的大小
    setimage(walimg, 40, 40);       
    setimage(perimg, 40, 40);       
    setimage(boximg, 40, 40);       
    setimage(tarimg, 40, 40);       
    setimage(tarimg_2, 40, 40);     


    PIMAGE backbox[MAXP] = {            //背景图片数组,将每一关背景放到数组中
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\游戏背景1_1.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景1.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景2.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景3.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景4.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景5.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景6.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景7.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景8.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\SC背景9.jpg"),
    tdoimage("C:\\Users\\ASUS\\Desktop\\推箱子素材\\OW背景1.jpg"),
    };

    //这里的i必须为局部变量
    for (int i = clonum - 1; i < MAXP; i++)     //小于当前最大关数
    {//从初始关数-1关开始游戏,因为从三维数组里读数据是从下标0开始读的
        putimage(0, 0, backbox[i]); //输出每关背景图片
    
        tranmap(floimg, walimg, perimg, boximg, tarimg, tarimg_2, mapbox[i]); //输出每关初始地图 

        concustom = 0;              //没到下一关关卡,控制关卡变量为0    控制关卡变量为全局
        Boxternum = ststeabox(mapbox[i]);//统计每一关未完成的目标个数
        stepnum = 0;                //下一关卡步数清0
        degfir = 0;                 //为0表示这是该关卡第一步
        
        for (int j = 0; j < N; j++) //在开始i关卡前存储前,存储i初始关卡,用于重启本关
            for (int k = 0; k < N; k++)
                map_temp[j][k] = mapbox[i][j][k]; 

        money += 100;               //关卡开始前给100资金

        while (true)
        {
            flushmouse();           //清空鼠标缓冲区
            delay_fps(70);          //延时、帧率

            display(i);             //输出游戏信息

            if(kbhit())
                peomove(floimg, walimg, perimg, boximg, tarimg, tarimg_2, mapbox[i]);    //人物移动主要函数
            //参数:所有图片对象、控制关卡变量、三维地图数组     

            if (mousemsg())
            {
                msg3 = getmouse();      //返回主界面鼠标信息
                msg4 = getmouse();      //进入下一关鼠标信息
                if (msg3.x >= 0 && msg3.y >= 0 && msg3.x <= 50 && msg3.y <= 50)
                    if (msg3.is_left())
                        goto F1;
                
                if (msg4.x >= 720 && msg4.y >= 480 && msg4.x <= 720 + 240 && msg4.y <= 480 + 60)
                {
                    while (!msg4.is_move()) break;
                    if (msg4.is_left())
                        break;
                }
            }
            if (concustom)          //控制进入下一关卡变量
                break;              //当这个变量被改变为1时break,进入下一关卡

            money -= 0.125;           //多走一步-0.125的资金
            delay_fps(70);          
        }
        money += 60;                //过一关资金加60
        delay_fps(70);
    }

F1:

    delimage(floimg);
    delimage(walimg);
    delimage(perimg);
    delimage(boximg);
    delimage(tarimg);
    delimage(tarimg_2);

    
}

int main()          //主函数
{
    
    graminit();     //程序初始化

    while (true)
    {
        process();   //流程函数
        gamegoint(); //主菜单界面
    }

    closegraph();           //关闭图形界面
    return 0;
}

PIMAGE tdoimage(const char charr[100])
{
    PIMAGE txtbackstepnum = newimage();         //为文本背景图片创建新的对象
    getimage(txtbackstepnum, charr);            //步数信息文本背景图片
    setimage(txtbackstepnum, getwidth(), getheight());//设置背景图片大小
    return txtbackstepnum;
}


//自定义图片大小指定位置输出函数:toddback();
//参数:背景图片路径,背景图片宽高, 背景图片位置xy
void toddback(const char charr[100], int bawidth, int baheight, int backx, int backy)
{
    PIMAGE txtbackstepnum = newimage();         //为文本背景图片创建新的对象
    getimage(txtbackstepnum, charr);            //步数信息文本背景图片
    setimage(txtbackstepnum, bawidth, baheight);//设置背景图片大小
    putimage(backx, backy, txtbackstepnum);     //在指定位置画出图片
    delimage(txtbackstepnum);
}


//设置图像对象图像大小函数:setimage();
//参数:图片对象名称,需要修改的图片对象中图片的宽高
void setimage(PIMAGE img, int pwidth, int pheight)      
{
    int whidth = getwidth(img), height = getheight(img);//获取当前img图像对象的宽高

    PIMAGE img_2 = newimage(pwidth, pheight);           //创建一个新的图像对象,这个新的图像对象的宽高为
                                                        //要重新设置的图像的宽高

    putimage(img_2, 0, 0, pwidth, pheight, img, 0, 0, whidth, height);//将原本img中的图像拉伸绘制到img_2中

    getimage(img, img_2, 0, 0, pwidth, pheight);        //img再获取img_2中的图像

    delimage(img_2);  //使用完毕将释放掉
    //该函数可以直接修改图片对象,因为这是引用
}

不足之处和反思:
关于内存管理方面的知识我掌握的并不是很好,因此在写程序的过程中还闹出过几次笑话。
我在写程序的过程中遇到的两个我曾经认为比较大的问题,后来被证明全都是其他地方、并且我早已想到只是没有去解决的问题。

虽然我解决了程序中大部分的问题,但是该程序在内存管理和其他方面还肯定存在着很多其他问题。

ege的库函数大部分是使用c++写的,如:类、对象、函数重载等。这些概念在c语言中并不存在。
在完成这么一个稍显复杂的小程序后,我发现用c面向过程的方法去写稍大的程序会非常复杂,且代码复用率低。且通过写这个程序又暴露了部分我在一些方面的不足。
所以综上考虑,在巩固完基础知识和学习完面向对象程序设计方法之前,我不会再用C语言写一些其他花里胡哨的程序了。

欢迎大家在评论区对我的程序提出意见和建议。


感谢您阅读了这篇文章。

最后,感谢所有愿意对我提出批评和建议的人,您的支持是我坚持下去的强大动力。

你可能感兴趣的:(C语言)