使用C语言和EGE图形库实现简单好看的推箱子小游戏
如果您喜欢我的文章可以点赞支持一下。
如果您对我的程序有什么意见和建议欢迎在评论区发表评论。
这是我目前为止写的,无论从时间上还是从工程量上来说都是最长的一个小程序。
从初学EGE到攻克所有的技术难点,总共历时五天。
全代码一千一百多行。
在此向帮助过我解决问题的陌生人和老师表示由衷的感谢。
首先展示几张程序的运行效果:
主菜单界面
游戏进行中界面:
关卡选择界面
游戏商城界面
地图编辑器界面
程序中除操控人物移动外所有操作均使用鼠标操作
下面进入技术环节:
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 <graphics.h> //ege
#include <ege/sys_edit.h> //输入
#include <stdio.h> //文件操作
#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