学完C语言后,C语言大作业或者是大项目基本是每个学校都会要求学生做一份出来的。于是各路神仙开始展现自己的功力:这个图书管理系统,那个车票售卖管理系统,这个做个贪吃蛇,那个做个推箱子,更甚有大佬做的什么植物大战僵尸,节奏大师,消消乐orz......大佬终归是大佬,那我们身为一个有梦想的菜鸡,为了不挂科期末作业总是要交的呀!怎么来个容易上手还能让老师感觉有那么一点技术的项目呢?推箱子是你的不二首选。
本人现在是一名大二本科生,这是我去年元旦这个时候交的C语言项目。但遗憾那时刚上大学懵懂无知,没听过还有博客这种东西,现在拿来与大家分享。
先说说我的项目能实现的功能
1.推箱子的基本功能,什么上下左右啦,推动箱子啦,过关啦,总共有5关!
2.按R可以重置本关
3.按Q可以跳过本关
4.有音乐效果,有界面
先来几张运行的截图
虽然有点粗糙,但是还可以(其实是因为做游戏因为能插图片看着逼格比做系统高一点hhh)
图片来源于百度图片
接下来上代码
//以下是制作游戏用到的头文件
#include
#include
#include
#include
#include
#pragma warning(disable:4996)
#pragma comment(lib,"Winmm.lib")
const int rows = 12, cols = 12;
int level = 0, step = 0, highnum;//关卡,目前已经走的步数,
IMAGE goodbox, box, man, blank, destination, wall, sky, startpage, background, button,button2;//声明一下要加载的图片,和变量的声明类似
//以下是地图的存储,采用三维数组,第一维代表关卡,二三维存储地图
/*
0代表空地
1代表墙壁
3代表目的地
4代表箱子
5代表人
7代表已经推到目的地的箱子
8代表人和目的地重叠的点,其实还是人
这个数字不是随便定义的,是有原因的,详情请细看函数movereason(移动原理)
*/
int map[5][12][12] =
{
{
{0,0,0,1,1,1,0,0,0,0,0,0},
{0,0,0,1,3,1,0,0,0,0,0,0},
{0,0,0,1,0,1,1,1,1,1,1,1},
{1,1,1,1,4,0,4,0,0,0,3,1},
{1,3,0,0,4,5,1,1,1,1,1,1},
{1,1,1,1,1,4,1,0,0,0,0,0},
{0,0,0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,3,1,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,1,1,1,1,0,0,0},
{0,0,0,1,1,1,0,5,1,0,0,0},
{0,0,0,1,0,0,4,0,1,0,0,0},
{0,0,1,1,0,1,0,1,1,1,0,0},
{0,0,1,0,0,1,0,1,3,1,0,0},
{0,0,1,0,1,0,0,4,3,1,0,0},
{0,0,1,0,4,0,0,0,3,1,0,0},
{0,0,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,1,1,1,1,1,0,0,0,0,0,0},
{0,1,0,0,0,1,0,0,0,0,0,0},
{0,1,0,4,4,1,0,1,1,1,0,0},
{0,1,5,4,0,1,0,1,3,1,0,0},
{0,1,1,1,0,1,1,1,3,1,0,0},
{0,0,1,1,0,0,0,0,3,1,0,0},
{0,0,1,0,0,0,1,0,0,1,0,0},
{0,0,1,0,0,0,1,1,1,1,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,1,1,1,1,1,1,1,1,1,0,0},
{0,1,0,0,0,1,0,0,0,1,0,0},
{0,1,0,4,4,4,4,4,0,1,0,0},
{0,1,0,4,0,4,0,4,0,1,1,0},
{0,1,0,0,0,0,0,0,4,0,1,0},
{1,1,0,1,1,1,1,0,4,0,1,0},
{1,0,8,3,3,3,3,1,0,0,1,0},
{1,0,3,3,3,3,3,0,0,1,1,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,0,0,1,1,1,1,1,1,1,0,0},
{0,0,0,1,3,0,3,0,3,1,0,0},
{0,0,0,1,0,4,4,4,0,1,0,0},
{0,0,0,1,3,4,5,4,3,1,0,0},
{0,0,0,1,0,4,4,4,0,1,0,0},
{0,0,0,1,3,0,3,0,3,1,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},
}
};
int map2[5][12][12] =//为了实现按R重置本关卡,定义了两个一模一样的map,map2
{
{
{0,0,0,1,1,1,0,0,0,0,0,0},
{0,0,0,1,3,1,0,0,0,0,0,0},
{0,0,0,1,0,1,1,1,1,1,1,1},
{1,1,1,1,4,0,4,0,0,0,3,1},
{1,3,0,0,4,5,1,1,1,1,1,1},
{1,1,1,1,1,4,1,0,0,0,0,0},
{0,0,0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,3,1,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,1,1,1,1,0,0,0},
{0,0,0,1,1,1,0,5,1,0,0,0},
{0,0,0,1,0,0,4,0,1,0,0,0},
{0,0,1,1,0,1,0,1,1,1,0,0},
{0,0,1,0,0,1,0,1,3,1,0,0},
{0,0,1,0,1,0,0,4,3,1,0,0},
{0,0,1,0,4,0,0,0,3,1,0,0},
{0,0,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,1,1,1,1,1,0,0,0,0,0,0},
{0,1,0,0,0,1,0,0,0,0,0,0},
{0,1,0,4,4,1,0,1,1,1,0,0},
{0,1,5,4,0,1,0,1,3,1,0,0},
{0,1,1,1,0,1,1,1,3,1,0,0},
{0,0,1,1,0,0,0,0,3,1,0,0},
{0,0,1,0,0,0,1,0,0,1,0,0},
{0,0,1,0,0,0,1,1,1,1,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,1,1,1,1,1,1,1,1,1,0,0},
{0,1,0,0,0,1,0,0,0,1,0,0},
{0,1,0,4,4,4,4,4,0,1,0,0},
{0,1,0,4,0,4,0,4,0,1,1,0},
{0,1,0,0,0,0,0,0,4,0,1,0},
{1,1,0,1,1,1,1,0,4,0,1,0},
{1,0,8,3,3,3,3,1,0,0,1,0},
{1,0,3,3,3,3,3,0,0,1,1,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,0,0,1,1,1,1,1,1,1,0,0},
{0,0,0,1,3,0,3,0,3,1,0,0},
{0,0,0,1,0,4,4,4,0,1,0,0},
{0,0,0,1,3,4,5,4,3,1,0,0},
{0,0,0,1,0,4,4,4,0,1,0,0},
{0,0,0,1,3,0,3,0,3,1,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},
}
};
void InitGame()//显示地图
{
int x, y;
for (int i = 0; i < 12; i++)
{
for (int j = 0; j < 12; j++)
{
x = j * 64;
y = i * 64;
switch (map[level][i][j])
{
case 0:
{
putimage(x, y, &blank);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 1:
{
putimage(x, y, &wall);
putimage(200, 700, &button);
putimage(500,700, &button2);
break;
}
case 3:
{
putimage(x, y, &destination);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 4:
{
putimage(x, y, &box);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 5:
{
putimage(x, y, &man);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 7:
{
putimage(x, y, &goodbox);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 8:
{
putimage(x, y, &man);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
}
}
}
}
void drawstratpage()//显示开始界面
{
initgraph(rows * 64, cols * 64);
loadimage(&startpage, "F:/kungehaoshuai/Debug./startpage.jpg", rows * 64, cols * 64);
putimage(0, 0, &startpage);
setbkmode(TRANSPARENT);
settextstyle(50, 0, _T("宋体"));
outtextxy(120, 500, _T("欢迎来到推箱子小游戏!"));
outtextxy(120, 600, _T("按任意键开始游戏!"));
system("pause");
}
void movereason()//移动的原理
{
InitGame();
int r1, r2;
for (int i = 0; i < 12; i++)
for (int j = 0; j < 12; j++)
if (map[level][i][j] == 5 || map[level][i][j] == 8)
{
r1 = i; r2 = j;
}
if (kbhit())
{
char c = getch();
switch (c)
{
case 'w':
case 72:
case 'W':
{
/*
0代表空地
1代表墙壁
3代表目的地
4代表箱子
5代表人
7代表已经推到目的地的箱子
8代表人和目的地重叠的点,其实还是人
这个数字不是随便定义的,是有原因的,详情请细看函数movereason(移动原理)
*/
if (map[level][r1 - 1][r2] == 0 || map[level][r1 - 1][r2] == 3)//如果要前进的格子是空格或者是目的地,那么肯定能走吧
{
map[level][r1 - 1][r2] += 5;//+5表示人站上去了
map[level][r1][r2] -= 5;//-5表示人走了
}
else if (map[level][r1 - 1][r2] == 4 || map[level][r1 - 1][r2] == 7)//如果要前进的格子是箱子或者是已经推到目的地的箱子呢么肯定是要实现 “推动” 的功能
{
if (map[level][r1 - 2][r2] == 3 || map[level][r1 - 2][r2] == 0)//如果箱子的下一个位置是目的地或者是空地,那就可以推动
{
map[level][r1 - 2][r2] += 4;//该格子推来了个箱子
map[level][r1 - 1][r2] += 1;//4变成了5,代表人原来在这里的箱子变成了人
map[level][r1][r2] -= 5;//原位置-5,恢复成初始的格子
}
}
break;
}
case 's':
case 'S':
case 80:
{
{
if (map[level][r1 + 1][r2] == 0 || map[level][r1 + 1][r2] == 3)//同上,一毛一样,就是ctrl+c/v ^_^
{
map[level][r1 + 1][r2] += 5;
map[level][r1][r2] -= 5;
}
else if (map[level][r1 + 1][r2] == 4 || map[level][r1 + 1][r2] == 7)
{
if (map[level][r1 + 2][r2] == 3 || map[level][r1 + 2][r2] == 0)
{
map[level][r1 + 2][r2] += 4;
map[level][r1 + 1][r2] += 1;
map[level][r1][r2] -= 5;
}
}
}
break;
}
case 'd':
case 'D':
case 77:
{
{
if (map[level][r1][r2 + 1] == 0 || map[level][r1][r2 + 1] == 3)
{
map[level][r1][r2 + 1] += 5;
map[level][r1][r2] -= 5;
}
else if (map[level][r1][r2 + 1] == 4 || map[level][r1][r2 + 1] == 7)
{
if (map[level][r1][r2 + 2] == 3 || map[level][r1][r2 + 2] == 0)
{
map[level][r1][r2 + 2] += 4;
map[level][r1][r2 + 1] += 1;
map[level][r1][r2] -= 5;
}
}
}
break;
}
case 'a':
case 'A':
case 75:
{
{
if (map[level][r1][r2 - 1] == 0 || map[level][r1][r2 - 1] == 3)
{
map[level][r1][r2 - 1] += 5;
map[level][r1][r2] -= 5;
}
else if (map[level][r1][r2 - 1] == 4 || map[level][r1][r2 - 1] == 7)
{
if (map[level][r1][r2 - 2] == 3 || map[level][r1][r2 - 2] == 0)
{
map[level][r1][r2 - 2] += 4;
map[level][r1][r2 - 1] += 1;
map[level][r1][r2] -= 5;
}
}
}
break;
}
case 'r':
case 'R':
{
for (int i = 0; i < 12; i++)
for (int j = 0; j < 12; j++)
map[level][i][j] = map2[level][i][j];
break;
}
case 'q':
case 'Q':
{
level++;
break;
}
case 27:
{
exit(0);
}
}
}
}
void gameintroduce()//显示游戏玩法介绍界面
{
initgraph(rows * 64, cols * 64);
loadimage(&startpage, "F:/kungehaoshuai/Debug./background.jpg", rows * 64, cols * 64);
putimage(0, 0, &startpage);
setbkmode(TRANSPARENT);
settextstyle(50, 0, _T("宋体"));
outtextxy(120, 200, _T("玩法介绍:"));
outtextxy(120, 300, _T("基本玩法与经典推箱子一致"));
outtextxy(120, 400, _T("按r键重新开始本关"));
outtextxy(120, 500, _T("你能帮助Father Christman"));
outtextxy(120, 550, _T("把礼物送到每个孩子手中吗?"));
system("pause");
}
void boxnum()//计算还没有被推到目的地的箱子的数量
{
int i, j, boxnum = 0;
for (i = 0; i < 10; i++)
for (j = 0; j < 12; j++)
if (map[level][i][j] == 4)
boxnum++;
if (boxnum == 0)//所有的箱子都被推到目的地了
{
loadimage(&sky, "F:/kungehaoshuai/Debug./sky.jpg", rows * 64, cols * 64);
cleardevice();
putimage(0, 0, &sky);
settextstyle(50, 0, _T("黑体"));
outtextxy(200, 200, _T("恭喜你!过关了!"));
outtextxy(220, 260, _T("你真棒!"));
outtextxy(200, 320, _T("按任意键进入下一关!"));
system("pause");
level++;
}
}
int flag = 0;
void finish()//显示游戏结束提示界面
{
if (level > 4)//满共4关,都过了意思就是通关了
{
loadimage(&background, "F:/kungehaoshuai/Debug./background.jpg", rows * 64, cols * 64);
putimage(0, 0, &background);
settextstyle(50, 0, _T("黑体"));
outtextxy(100, 200, _T("恭喜你!通过了全部关卡!"));
outtextxy(100, 350, _T("按任意键退出小游戏"));
outtextxy(100, 500, _T("HAPPY NEW YEAR!"));
flag = 1;
system("pause");
}
}
int main()
{
mciSendString("open F:/kungehaoshuai/Debug//game_music3.mp3 alias bkmusic", NULL, 0, NULL);
mciSendString("play bkmusic", NULL, 0, NULL);
drawstratpage();//显示开始界面
gameintroduce();//显示游戏介绍界面
loadimage(&background, "F:/kungehaoshuai/Debug./background.jpg", rows * 64, cols * 64);
putimage(0, 0, &background);
loadimage(&box, "F:/kungehaoshuai/Debug./box.jpg", 64, 64);
loadimage(&goodbox, "F:/kungehaoshuai/Debug./goodbox.jpg", 64, 64);
loadimage(&blank, "F:/kungehaoshuai/Debug./blank.jpg", 64, 64);
loadimage(&wall, "F:/kungehaoshuai/Debug./wall.jpg", 64, 64);
loadimage(&man, "F:/kungehaoshuai/Debug./man.jpg", 64, 64);
loadimage(&destination, "F:/kungehaoshuai/Debug./destination4.jpg", 64, 64);
loadimage(&button, "F:/kungehaoshuai/Debug./button.jpg", 200, 70);
loadimage(&button2, "F:/kungehaoshuai/Debug./q_副本.jpg", 200, 70);
while (1)
{
movereason();//游戏进行的原理
boxnum();//每次移动完了以后计算一下是不是把所有的箱子都推动到目的地了
finish();//通关提示
if (flag == 1)//flag=1标志着通过了所有关卡
break;
}
closegraph();
return 0;
}
代码500行左右,但用数组存地图就200行,加上各种重复的操作ctrl+c/v和注释,最后核心东西不到200行。emm不过毕竟要答辩嘛,细细~
这是我们当时统一买的参考书,虽然上面没有推箱子,但是有很多关于别的游戏如飞机大战,贪吃蛇的开发的讲解。我就是从中学到的怎么加载音乐,怎么在图片上显示字体,怎么清空画布等基本知识。
想弄懂这推箱子怎么做的,就两个:1.怎么样实现推箱子的这个移动过程 和 2.怎么加载图片
1.推箱子过程的实现
这个很简单,用到了C语言中的数组,选择结构if else来编写。大家看见那两个长长的全是数字的数组了吧,那就是地图,数字的含义如下:
0代表空地
1代表墙壁
3代表目的地
4代表箱子
5代表人
7代表已经推到目的地的箱子
8代表人和目的地重叠的点,其实还是人
移动的原理如下,以上移为例
char c = getch();
switch (c)
{
case 'w':
case 72:
case 'W':
{
if (map[level][r1 - 1][r2] == 0 || map[level][r1 - 1][r2] == 3)//如果要前进的格子是空格或者是目的地,那么肯定能走吧
{
map[level][r1 - 1][r2] += 5;//+5表示人站上去了
map[level][r1][r2] -= 5;//-5表示人走了
}
else if (map[level][r1 - 1][r2] == 4 || map[level][r1 - 1][r2] == 7)//如果要前进的格子是箱子或者是已经推到目的地的箱子呢么肯定是要实现 “推动” 的功能
{
if (map[level][r1 - 2][r2] == 3 || map[level][r1 - 2][r2] == 0)//如果箱子的下一个位置是目的地或者是空地,那就可以推动
{
map[level][r1 - 2][r2] += 4;//该格子推来了个箱子
map[level][r1 - 1][r2] += 1;//4变成了5,代表人原来在这里的箱子变成了人
map[level][r1][r2] -= 5;//原位置-5,恢复成初始的格子
}
}
break;
}
思路都注释在代码中了,大家仔细每种情况走一下应该不难理解。那么其他三个方向是一摸一样的,就是[i-1][j],[i][j+1],[i][j-1]这些的差别。
2.怎么加载图片
其实这一部分是当时最让我困扰的,因为不用加载图片,用黑框框运行一样能行。但我当时听说我们班五十个人,有五个人都做得推箱子,那总得搞出点花样儿来啊,不然老师凭什么给你高分。于是我没日没夜刻苦钻研(上网百度)终于加上了图片,最后那五个推箱子里,只有我得了19分,其他人都是17分^ ^(满分20分,虽然只高了2分,但当时别提有多开心了)。
loadimage(&background, "F:/kungehaoshuai/Debug./background.jpg", rows * 64, cols * 64);
putimage(0, 0, &background);
当时试过的各种图片和音乐,大家注意看路径和图片名
这是我自己摸索出来的可以让屏幕上覆盖上图片的方法,当时上网查了无数版本,但好像都有点问题。大概就是这样的:你想显示哪张图片,得先loadimage它,然后使用putimage函数把他显示出来。这是我自己通俗的理解,可能不到位或者根本不对,欢迎大家给我批评指正。
结语:这篇文章希望能够帮到那些需要做大作业的同学,和对C语言做推箱子游戏感兴趣的同学,我的理解可能还很浅显,如果有一些大佬对我的想法存在疑问,欢迎大家给我批评指正。祝大家学习上都有个好成绩,都玩的开心,写代码写的有成就感~