一、预备知识
1.使用EasyX必须要知道的一些基础函数
2.选择结构 if , switch
3.循环结构 for, while
4.多维数组 arr1[N], arr2[N][N] , arr3[N][N][N]
5.函数封装
二、游戏逻辑
想要写出推箱子,首先要知道推箱子游戏都有哪些元素和规则
1.推箱子元素
1.小人 2.箱子 3.空地 4.墙壁 5.目的地 6.到达目的地的箱子
2.推箱子规则
1.“小人”只能在“空地”和“目的地”上行走
2.“小人”可以推“箱子后面是 ‘空地’和‘目的地’ ”的“箱子”
3.所有“箱子”到达“目的地”游戏获胜
三、游戏设计
知道游戏元素和规则就可以开始设计游戏了
1.地图设计
首选需要知道的是,游戏地图是一个二维数组
根据玩推箱子的经验可以知道,游戏中会出现哪些组合情况
- “小人”在“目的地”
- “箱子”在“目的地”
下面我们给这些情况定义一些变量,数字大家可以随意,对应就好
enum material
{
space=0,//空地
wall=1,//墙壁
box=3, //箱子
person=4,//小人
aim=5,//目的地
box_aim=8,//箱子在目的地 3+5=8
person_aim=9,//小人在目的地 4+5=9
};
元素和数字已经对应起来了,先写一个初始化加载图片函数Loadimg(),素材就放在源文件旁边
#define SIZE 35//每个元素的大小
IMAGE img[10];//存放小图片
void Loadimg()//加载资源
{
loadimage(&img[0],L"./images/0.jpg",SIZE,SIZE);//空地
loadimage(&img[1],L"./images/1.jpg",SIZE,SIZE);//墙壁
loadimage(&img[3],L"./images/3.jpg",SIZE,SIZE);//箱子
loadimage(&img[4],L"./images/4.jpg",SIZE,SIZE);//小人
loadimage(&img[5],L"./images/5.jpg",SIZE,SIZE);//目的地
loadimage(&img[8],L"./images/8.jpg",SIZE,SIZE);//箱子在目的地
}
然后你就知道为什么游戏地图是一个二维数组
是不是switch,case根据数字贴图就完事了,就是这么easy,一关怎么能行呢,当然要很多关,二维不够三维来凑,定义一个全局变量map三维数组,和全局变量number存放关卡数
int number=0;//存放关卡数,当前第一关
int map[3][7][7]=
{ //第一关
1,1,1,1,1,1,1,
1,5,0,0,0,5,1,
1,0,3,0,3,0,1,
1,0,0,4,0,0,1,
1,0,3,0,3,0,1,
1,5,0,0,0,5,1,
1,1,1,1,1,1,1,
//第二关
1,1,1,1,1,1,1,
1,5,5,5,5,5,1,
1,0,0,3,0,0,1,
1,0,3,3,3,0,1,
1,0,0,3,0,0,1,
1,0,0,4,0,0,1,
1,1,1,1,1,1,1,
//第三关
1,1,1,1,1,1,1,
1,0,0,0,0,0,1,
1,5,1,5,1,3,1,
1,0,3,0,5,0,1,
1,3,1,3,1,0,1,
1,4,0,0,0,5,1,
1,1,1,1,1,1,1,
};
地图定义好了,那就贴图吧,写一个Drawmap()函数画地图
#define ROWS 7//行数
#define COLS 7//列数
void Drawmap()
{
int i,j,x,y;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
x=0;y=0;
x += j * SIZE, y += i * SIZE;//确定每个图片的坐标
switch (map[number][i][j])
{
case space://空地
putimage(x, y, &img[space]);
break;
case wall://墙壁
putimage(x, y, &img[wall]);
break;
case box://箱子
putimage(x, y, &img[box]);
break;
case person://小人
putimage(x, y, &img[person]);
break;
case aim://目的地
putimage(x, y, &img[aim]);
break;
case box_aim://箱子在目的地
putimage(x, y, &img[box_aim]);
break;
case person_aim://人在目的地还是人
putimage(x, y, &img[person]);
break;
}
}
printf("\n");
}
system("cls");//清屏
}
2.移动设计
上文提到
1.“小人”只能在“空地”和“目的地”上行走
2.“小人”可以推“箱子后面是 ‘空地’和‘目的地’ ”的“箱子”
那么每次移动完都需要确定小人的位置,再判断能否移动
先定义全局变量记录小人位置,再写一个获取小人位置的函数Getpersonxy()
int ipos,jpos;//记录小人位置
void Getpersonxy()
{
int i,j;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
if(map[number][i][j]==person || map[number][i][j]==person_aim)
{//找到小人 或者 小人在目的地
ipos=i;
jpos=j;//找到小人位置
break;
}
}
}
}
找到小人位置后判断小人是否能移动,写一个Moveperson()函数,这个就要划重点了
void Moveperson()
{
PlaySound(L"./images/move.wav", nullptr, SND_FILENAME | SND_ASYNC);//播放移动音乐
char ch = _getch();
switch (ch)//控制小人走
{
case 'w':
case 'W':
case 72://小键盘键值
if (map[number][ipos-1][jpos] == space || map[number][ipos-1][jpos] == aim)
{//人前面是空地或者目的地
map[number][ipos-1][jpos]+=person;//人来了
map[number][ipos][jpos]-=person;//人走了
}
else if (map[number][ipos-1][jpos] == box || map[number][ipos-1][jpos] == box_aim)
{//人前面是箱子或者箱子在目的地
//判断箱子是否能走
if (map[number][ipos-2][jpos] == space || map[number][ipos-2][jpos] == aim)//箱子前面是空地或者目的地 才能走
{
map[number][ipos-2][jpos]+=box ; //箱子来了
map[number][ipos-1][jpos]+=person-box ;//人来了 箱子走了
map[number][ipos][jpos]-=person;//人走了
}
}
break;
case 's':
case 'S':
case 80://小键盘键值
if (map[number][ipos+1][jpos] == space || map[number][ipos+1][jpos] == aim)
{//人前面是空地或者目的地
map[number][ipos+1][jpos]+=person;//人来了
map[number][ipos][jpos]-=person;//人走了
}
else if (map[number][ipos+1][jpos] == box || map[number][ipos+1][jpos] == box_aim)
{//人前面是箱子或者箱子在目的地
//判断箱子是否能走
if (map[number][ipos+2][jpos] == space || map[number][ipos+2][jpos] == aim)//箱子前面是空地或者目的地 才能走
{
map[number][ipos+2][jpos]+=box ; //箱子来了
map[number][ipos+1][jpos]+=person-box;//人来了 箱子走了
map[number][ipos][jpos]-=person;//人走了
}
}
break;
case 'a':
case 'A':
case 75://小键盘键值
if (map[number][ipos][jpos-1] == space || map[number][ipos][jpos-1] == aim)
{//人前面是空地或者目的地
map[number][ipos][jpos-1]+= person;//人来了
map[number][ipos][jpos]-= person;//人走了
}
else if (map[number][ipos][jpos-1] == box || map[number][ipos][jpos-1] == box_aim)
{//人前面是箱子或者箱子在目的地
//判断箱子是否能走
if (map[number][ipos][jpos-2] == space || map[number][ipos][jpos-2] == aim)//箱子前面是空地或者目的地 才能走
{
map[number][ipos][jpos-2]+=box ; //箱子来了
map[number][ipos][jpos-1]+=person-box ;//人来了 箱子走了
map[number][ipos][jpos]-=person;//人走了
}
}
break;
case 'd':
case 'D':
case 77://小键盘键值
if (map[number][ipos][jpos+1] == space || map[number][ipos][jpos+1] == aim)
{//人前面是空地或者目的地
map[number][ipos][jpos+1]+=person;//人来了
map[number][ipos][jpos]-=person;//人走了
}
else if (map[number][ipos][jpos+1] == box || map[number][ipos][jpos+1] == box_aim)
{//人前面是箱子或者箱子在目的地
//判断箱子是否能走
if (map[number][ipos][jpos+2] == space || map[number][ipos][jpos+2] == aim)//箱子前面是空地或者目的地 才能走
{
map[number][ipos][jpos+2]+=box ; //箱子来了
map[number][ipos][jpos+1]+=person-box ;//人来了 箱子走了
map[number][ipos][jpos]-=person;//人走了
}
}
break;
}
}
3.结束设计
上文提到
3.所有“箱子”到达“目的地”游戏获胜**
也就是判断地图上是否有box,如果有,游戏继续,没有box,进入下一关,写一个Gameover()函数
int Gameover()
{
int i, j;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
//遍历地图是否有箱子
if (map[number][i][j] == box)
{
return 0;//有箱子退出循环 继续
}
}
}
return 1;
}
4.整体设计
下面把上面的函数组合起来就是推箱子游戏,再来点音乐
#include<stdio.h>
#include<easyx.h>//图形库
#include<conio.h>//按键
#include<mmsystem.h>//音乐
#pragma comment(lib,"winmm.lib")//库文件
int main()
{
initgraph(SIZE*7,SIZE*7);//创建地图大小的窗口
Loadimg();//加载资源
//循环播放背景音乐
mciSendString(L"open ./images/bg.mp3 ", 0, 0, 0);
mciSendString(_T("play ./images/bg.mp3 repeat"), 0, 0, 0);
while(true){
//绘制界面
BeginBatchDraw();
Drawmap();//贴地图
EndBatchDraw();
if (Gameover())
{
PlaySound(L"./images/success.wav", nullptr, SND_FILENAME | SND_ASYNC);//播放胜利音乐
number++;//进入下一关
Sleep(500);
if (number>= 3)
break;//第三关玩完结束
}
if (_kbhit())//获取按键消息
{
Getpersonxy();//找到小人位置
Moveperson();//小人移动
}
}
mciSendString(L"close ./images/bg.mp3",0,0,0);//关闭背景音乐
setbkmode(0);//设置文字背景透明
settextcolor(RGB(167, 48, 48));//设置字体颜色
settextstyle(30, 0, _T("宋体"));//64是字体大小 0是自适应 双引号里可以填字体名字
outtextxy(0, 110, _T("游戏结束,你真棒!"));
Sleep(5000);
closegraph();//关闭窗口
return 0;
}
把上面所有代码复制到一个cpp文件里,再把素材改好命名放到images文件夹里,images文件夹在cpp文件旁边,就可以编译运行了!