一、预备知识
1.使用EasyX必须要知道的一些基础函数
2.选择结构 if , switch
3.循环结构 for, while
4.多维数组 arr1[N], arr2[N][N] , arr3[N][N][N](不喜欢玩拼图,就写一关)
5.函数封装
二、游戏逻辑
首先明确拼图游戏元素和规则
1.拼图元素
一张完整的图片被切成若干个小图片,下面直接按老程序员命名方式走起
有人会问了,“为什么拼锐雯呢,二次元它不香嘛?”
当然是“断剑重铸之日,骑士归来之时”,都的都懂
但是,这还不够,还需要一张纯黑或者纯白的图片,当然啥颜色看你心情了
素材都有了,这不赶紧写个初始化函数
#define SIZE_X 245
#define SIZE_Y 125
//小图片的长宽
IMAGE img[40];//存放小图片
IMAGE nothing;//存放空图
void Loadimg()
{//素材初始化函数
loadimage(¬hing,L"./images/-1.jpg",SIZE_X,SIZE_Y);
loadimage(&img[0] ,L"./images/00.jpg",SIZE_X,SIZE_Y);
loadimage(&img[1] ,L"./images/01.jpg",SIZE_X,SIZE_Y);
loadimage(&img[2] ,L"./images/02.jpg",SIZE_X,SIZE_Y);
loadimage(&img[3] ,L"./images/03.jpg",SIZE_X,SIZE_Y);
loadimage(&img[10],L"./images/10.jpg",SIZE_X,SIZE_Y);
loadimage(&img[11],L"./images/11.jpg",SIZE_X,SIZE_Y);
loadimage(&img[12],L"./images/12.jpg",SIZE_X,SIZE_Y);
loadimage(&img[13],L"./images/13.jpg",SIZE_X,SIZE_Y);
loadimage(&img[20],L"./images/20.jpg",SIZE_X,SIZE_Y);
loadimage(&img[21],L"./images/21.jpg",SIZE_X,SIZE_Y);
loadimage(&img[22],L"./images/22.jpg",SIZE_X,SIZE_Y);
loadimage(&img[23],L"./images/23.jpg",SIZE_X,SIZE_Y);
loadimage(&img[30],L"./images/30.jpg",SIZE_X,SIZE_Y);
loadimage(&img[31],L"./images/31.jpg",SIZE_X,SIZE_Y);
loadimage(&img[32],L"./images/32.jpg",SIZE_X,SIZE_Y);
loadimage(&img[33],L"./images/33.jpg",SIZE_X,SIZE_Y);
}
2.拼图规则
拼图就是少一小块图片,然后点击空图附近的图,空图附近的图就是移动到空图的位置上去,我们可以理解为,空图和它附近的图片交换位置,这个很重要,划重点了
其次就是,所有图移动到它本来的位置,游戏获胜
三、游戏设计
知道游戏元素和规则就可以开始设计游戏了
1.地图设计
看过我推箱子博客的都知道,地图就是个二维数组,定义一个全局变量map数组存放地图,先写简单一点,想复杂可以自己再调位置
int map[4][4]=
{//数字是图片编号,-1表示没图片
1, 2, 3,13,
20,10,11,12,
21,22,23,33,
-1,30,31,32,
};
有地图了就开始画图吧,写一个Drawmap()函数
void Drawmap()
{
int i,j,x,y;
for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
{
x=0,y=0;
x+=j*SIZE_X;y+=i*SIZE_Y;//确定图片位置
switch(map[i][j])
{
case -1:
putimage(x,y,¬hing);
break;
case 0:
putimage(x,y,&img[0]);
break;
case 1:
putimage(x,y,&img[1]);
break;
case 2:
putimage(x,y,&img[2]);
break;
case 3:
putimage(x,y,&img[3]);
break;
case 10:
putimage(x,y,&img[10]);
break;
case 11:
putimage(x,y,&img[11]);
break;
case 12:
putimage(x,y,&img[12]);
break;
case 13:
putimage(x,y,&img[13]);
break;
case 20:
putimage(x,y,&img[20]);
break;
case 21:
putimage(x,y,&img[21]);
break;
case 22:
putimage(x,y,&img[22]);
break;
case 23:
putimage(x,y,&img[23]);
break;
case 30:
putimage(x,y,&img[30]);
break;
case 31:
putimage(x,y,&img[31]);
break;
case 32:
putimage(x,y,&img[32]);
break;
case 33:
putimage(x,y,&img[33]);
break;
}
}
printf("\n");
}
system("cls");
}
2.移动设计
上文提到,移动就是交换图片的位置
空图和它附近的图片交换位置
这次不用键盘控制移动了,换一换,改用鼠标操作
想交换位置,首先要定义一些全局变量保存空图和鼠标的位置
int ipos,jpos;//由鼠标确定的图片位置
int bipos=3,bjpos=0;//当前黑图位置
注意空图位置是上面地图里-1的位置
然后就要得到鼠标点击图片区域对应的数组坐标位置,写一个Getimgxy()函数
ExMessage m;//消息全局变量
void Getimgxy()
{
m = getmessage(EM_MOUSE);//得到鼠标消息
if (m.message == WM_LBUTTONDOWN)
{//如果鼠标左键按下
if(m.y>0 && m.y<SIZE_Y)
{//如果鼠标点第一行
if(m.x>0 && m.x<SIZE_X)
{//第一行第一个
ipos=0;jpos=0;//确定鼠标点击的图片位置
}
else if(m.x>SIZE_X && m.x<SIZE_X*2)
{//第一行第二个
ipos=0;jpos=1;
}
else if(m.x>SIZE_X*2 && m.x<SIZE_X*3)
{//第一行第三个
ipos=0;jpos=2;
}
else if(m.x>SIZE_X*3 && m.x<SIZE_X*4)
{//第一行第四个
ipos=0;jpos=3;
}
}
else if(m.y>SIZE_Y && m.y<SIZE_Y*2)
{
if(m.x>0 && m.x<SIZE_X)
{//第二行第一个
ipos=1;jpos=0;
}
else if(m.x>SIZE_X && m.x<SIZE_X*2)
{//第二行第二个
ipos=1;jpos=1;
}
else if(m.x>SIZE_X*2 && m.x<SIZE_X*3)
{//第二行第三个
ipos=1;jpos=2;
}
else if(m.x>SIZE_X*3 && m.x<SIZE_X*4)
{//第二行第四个
ipos=1;jpos=3;
}
}
else if(m.y>SIZE_Y*2 && m.y<SIZE_Y*3)
{
if(m.x>0 && m.x<SIZE_X)
{//第三行第一个
ipos=2;jpos=0;
}
else if(m.x>SIZE_X && m.x<SIZE_X*2)
{//第三行第二个
ipos=2;jpos=1;
}
else if(m.x>SIZE_X*2 && m.x<SIZE_X*3)
{//第三行第三个
ipos=2;jpos=2;
}
else if(m.x>SIZE_X*3 && m.x<SIZE_X*4)
{//第三行第四个
ipos=2;jpos=3;
}
}
else if(m.y>SIZE_Y*3 && m.y<SIZE_Y*4)
{
if(m.x>0 && m.x<SIZE_X)
{//第四行第一个
ipos=3;jpos=0;
}
else if(m.x>SIZE_X && m.x<SIZE_X*2)
{//第四行第二个
ipos=3;jpos=1;
}
else if(m.x>SIZE_X*2 && m.x<SIZE_X*3)
{//第四行第三个
ipos=3;jpos=2;
}
else if(m.x>SIZE_X*3 && m.x<SIZE_X*4)
{//第四行第四个
ipos=3;jpos=3;
}
}
}
}
有了由鼠标点击确定的图片位置和空图的位置,我们就可以写交换图片的函数了,Exchangeimg()
void Exchangeimg()
{
int x=0,y=0,bx=0,by=0;
if(ipos==bipos)
{//如果点击的图片和空图在一行
if(jpos== bjpos-1 || jpos== bjpos+1)
{//如果点击的图片在空图左边或右边
x+=jpos*SIZE_X;y+=ipos*SIZE_Y;//计算点击图片的坐标
putimage(x,y,¬hing);//放空图
bx+=bjpos*SIZE_X;by+=bipos*SIZE_Y;//计算空图坐标
putimage(bx,by,&img[ map[ipos][jpos] ]);//放点击的图
map[bipos][bjpos]=map[ipos][jpos];
map[ipos][jpos]=-1;//交换地图上的数据
bipos=ipos; //记录空图的新坐标
bjpos=jpos;
}
}
else if(jpos==bjpos)
{//如果点击的图片和空图在一列
if(ipos== bipos-1 || ipos== bipos+1)
{//如果点击的图片在空图上边或下边
x+=jpos*SIZE_X;y+=ipos*SIZE_Y;//计算点击图片的坐标
putimage(x,y,¬hing);//放空图
bx+=bjpos*SIZE_X;by+=bipos*SIZE_Y;//计算空图坐标
putimage(bx,by,&img[ map[ipos][jpos] ]);//放点击的图
map[bipos][bjpos]=map[ipos][jpos];
map[ipos][jpos]=-1;//交换地图上的数据
bipos=ipos; //记录空图的新坐标
bjpos=jpos;
}
}
}
3.结束设计
上文提到
所有图移动到它本来的位置,游戏获胜
写一个Gameover()函数判断游戏是否结束
int Gameover()
{
if(map[0][1]==1)
if(map[0][2]==2)
if(map[0][3]==3)
if(map[1][0]==10)
if(map[1][1]==11)
if(map[1][2]==12)
if(map[1][3]==13)
if(map[2][0]==20)
if(map[2][1]==21)
if(map[2][2]==22)
if(map[2][3]==23)
if(map[3][0]==30)
if(map[3][1]==31)
if(map[3][2]==32)
if(map[3][3]==33)
return 1;
return 0;
}
4.整体设计
下面把上面的函数组合起来就是拼图游戏,再来点音乐
#include<stdio.h>
#include<graphics.h>//图形库
#include<mmsystem.h>//音乐
#pragma comment(lib,"winmm.lib")//库文件
int main()
{
initgraph(980,500);//创建窗口
Loadimg(); //加载图片资源
mciSendString(L"open ./images/bg.mp3",0,0,0);//打开音乐
mciSendString(_T("play ./images/bg.mp3 repeat"), 0, 0, 0);//循环播放
BeginBatchDraw();//这个函数用于开始批量绘图。执行后,任何绘图操作都将暂时不输出到屏幕上,
Drawmap();
EndBatchDraw();//直到执行 EndBatchDraw 才将之前的绘图输出。
while(true){
Getimgxy();//获取鼠标点击的图片位置
Exchangeimg();//交换图片位置
if(Gameover())
{//如果游戏结束
mciSendString(L"close ./images/bg.mp3",0,0,0);//关闭背景音乐
PlaySound(L"./images/success.wav", nullptr, SND_FILENAME | SND_ASYNC);//播放胜利音乐
putimage(0,0,&img[0]);//把少的那一块图片贴上去
break;
}
}
setbkmode(0);//设置文字背景透明
settextcolor(RED);//设置字体颜色 红色
settextstyle(64, 0, _T("宋体"));//64是字体大小 0是自适应 双引号里可以填字体名字
outtextxy(230, 150, _T("游戏结束,你真棒!"));
system("pause");
return 0;
}
把上面所有代码复制到一个cpp文件里,再把素材改好命名放到images文件夹里,images文件夹在cpp文件旁边,就可以编译运行了!