C++语言实现拼图游戏详解

编辑: admin 分类: c#语言 发布时间: 2021-12-12 来源:互联网
目录
  • 开发环境:Visual Studio 2019,easyx图形库。
    • 游戏功能列表:
    • 游戏效果
    • 一.头文件和基本量
    • 二.封面
    • 三.数据初始化
    • 四.封面规则按钮
    • 五.构造拼图
    • 六.绘图函数
    • 七.背景音乐
    • 八.数据更新
    • 九.通关判断
    • 十.完整程序
  • 总结

    开发环境:Visual Studio 2019,easyx图形库。

    easyx下载官网:

    EasyX Graphics Library for C++

    https://easyx.cn/

    easyx使用文档:

    EasyX 文档 - 函数说明

    https://docs.easyx.cn/zh-cn/reference

    游戏功能列表:

    其主要功能描述如下:

    1.图片尺寸自适应

    2.图片动态分割

    3.查看原图

    4.随机切换图片

    5.鼠标拖动拼图<——>交换拼图块

    6.自动判断拼图成功

    拓展功能:

    • 背景音乐(开,关)
    • 游戏中Esc键返回桌面
    • 游戏规则窗口

    游戏效果

    封面(音乐按钮有点拉跨~)

    游戏初始图(我的心是冰冰的)

    通关图

    一.头文件和基本量

    #include<conio.h>
    #include<stdio.h>
    #include<easyx.h>
    #include<time.h>
    #include<Windows.h>   
    #include<mmsystem.h>    //音乐
    #pragma comment(lib,"Winmm.lib")    //静态库,调用音乐
    using namespace std;
    constexpr auto N = 3;    //3*3拼图
    IMAGE img[4], imgs[9];     //img存整张图片,imgs暂存拼图块
    int aim_c, aim_r;          //拼图块坐标
    int map[3][3] = { 0 };     //存拼图块
    int NUM = 0;          //关卡数计数

    二.封面

    //开始界面
    void start()
    {
    	loadimage(NULL, L"cover.jpg");
    	setbkmode(TRANSPARENT);
    	settextcolor(BLACK);
    	settextstyle(60, 0, _T("楷体"),0,0,4,false,false,false);
    	outtextxy(180, 120, L"拼图游戏");                    //游戏名称
    	settextstyle(30, 0, _T("微软雅黑"));
    	setfillcolor(BROWN);
    	setlinestyle(BS_SOLID, 5);
    	setlinecolor(RED);
    	fillroundrect(220, 220, 370, 270, 10, 10);
    	settextstyle(30, 0, _T("宋体"), 0, 0, 6, false, false, false);  //开始按钮
    	outtextxy(270, 230, L"开始");
    	fillroundrect(220, 300, 370, 350, 10, 10);
    	outtextxy(240, 310, L"游戏规则");
    	setfillcolor(BROWN);
    	setlinestyle(BS_SOLID, 5);
    	setlinecolor(BLACK);
    	fillcircle(490, 440, 30);  //音乐控制按钮:开
    	fillcircle(560, 440, 30);  //音乐控制按钮:关
    	outtextxy(380, 430, L"音乐:");
    	setfillcolor(BLACK);
    	POINT pts[] = { {481,425},{481,455},{507,440} };
    	fillpolygon(pts, 3);
    	fillrectangle(546, 425, 554, 455);
    	fillrectangle(566, 425, 574, 455);
    	rules();
    }

    三.数据初始化

    //游戏初始化
    void init()
    {
    	//加载资源图片,4张图4个关卡
    	loadimage(&img[0], L"picture1.jpg",  600, 600);
    	loadimage(&img[1], L"picture2.jpg", 600, 600);
    	loadimage(&img[2], L"picture3.jpg", 600, 600);
    	loadimage(&img[3], L"picture4.jpg", 600, 600);
    	//设置最后一张图片为空白图片,作为目标图片
    	loadimage(&imgs[8], L"white.jpg", 200, 200);
    	//设置随机种子
    	srand((unsigned)time(NULL));
    }

    四.封面规则按钮

    //封面规则函数
    int rules()
    {
    	  ExMessage Mou;    //鼠标消息
    	  while (1)
    	  {
    		  Mou = getmessage(EM_MOUSE);
    		  switch (Mou.message)    //对鼠标信息进行匹配
    		  {
    		  case WM_LBUTTONDOWN:            //按下左键
    			  if (Mou.x >= 220 && Mou.x <= 370 && Mou.y >= 300 && Mou.y <= 350)
    			  {
    				  HWND hwnd = GetHWnd();
    				  MessageBox(NULL, L"1.鼠标左键点击空白图处周围图片交换位置\n2.鼠标右键任意处按下显示参照图片\n3.鼠标中键更换背景图片\n4.按Esc键返回封面", L"游戏规则", MB_OKCANCEL);
    				  break;                     //规则按钮
    			  }
    			  if (Mou.x >= 220 && Mou.x <= 370 && Mou.y >= 220 && Mou.y <= 270)
    			  {
    				  return 0;                  //开始按钮
    			  }
    			  if (Mou.x >= 460 && Mou.x <= 520 && Mou.y >= 410 && Mou.y <= 470)
    			  {
    				  BGM();                     //音乐播放按钮
    				  break;
    			  }
    			  if (Mou.x >= 530 && Mou.x <= 590 && Mou.y >= 410 && Mou.y <= 470)
    			  {
    				  mciSendString(L"close back", 0, 0, 0);     //音乐关闭按钮
    				  break;
    			  }
    		  }
    	  }
     }

    五.构造拼图

    //拼图构造函数
    void GameInit()
    {
    	//把拼图贴上去
    	putimage(0, 0, &img[NUM]);
    	//设置绘图目标为img对象   对拼图图片进行切割
    	SetWorkingImage(&img[NUM]);
    	for (int y = 0, n = 0; y < N; y++)
    	{
    		for (int x = 0; x < N; x++)
    		{
    			if (n == 8)	break;
    			//获取100*100像素图片,存储在img中;
    			getimage(&imgs[n++], x * 200, y * 200, (x + 1) * 200, (y + 1) * 200);
    		}
    	}
    	//设置绘图目标为绘图窗口
    	SetWorkingImage();
    	//初始化地图0~15
    	for (int i = 0, k = 0; i < N; i++)
    	{
    		for (int j = 0; j < N; j++)
    		{
    			map[i][j] = k++;
    		}
    	}
    	//打乱地图
    	for (int k = 0; k <= 1000; k++)
    	{
    		//得到目标所在的行和列
    		for (int i = 0; i < N; i++)
    		{
    			for (int j = 0; j < N; j++)
    			{
    				if (map[i][j] == 8)  //空白图片作为交换目标
    				{
    					aim_r = i;
    					aim_c = j;
    					break;
    				}
    			}
    		}
    		//一千次打乱顺序之后需要将空白图片转移到右下角
    		//可以封装成函数下面这个代码
    		if (k == 1000)
    		{
    			//将空白图片循环转移到右下角
    			while (aim_r < 2)
    			{
    				//保证空白目标在最下
    				map[aim_r][aim_c] = map[aim_r + 1][aim_c];
    				map[aim_r + 1][aim_c] = 8;
    				aim_r++;
    			}
    			while (aim_c < 2)
    			{
    				//保证空白目标在最右
    				map[aim_r][aim_c] = map[aim_r][aim_c + 1];
    				map[aim_r][aim_c + 1] = 8;
    				aim_c++;
    			}
    			return;
    		}
    		int dir = rand() % 4;   //随机一个方向
    		switch (dir)
    		{
    		case 0:  //向上交换
    			if (aim_r >= 1)
    			{
    				//空白图片和空白处上面的图片交换
    				map[aim_r][aim_c] = map[aim_r - 1][aim_c];
    				map[aim_r - 1][aim_c] = 8;
    				break;
    			}
    		case 1:  //向下交换
    			if (aim_r < 2)
    			{
    				//空白图片和空白处下面的图片交换
    				map[aim_r][aim_c] = map[aim_r + 1][aim_c];
    				map[aim_r + 1][aim_c] = 8;
    				break;
    			}
    		case 2:  //向左交换
    			if (aim_c >= 1)
    			{
    				//空白图片和空白处左边的图片交换
    				map[aim_r][aim_c] = map[aim_r][aim_c - 1];
    				map[aim_r][aim_c - 1] = 8;
    				break;
    			}
    		case 3:  //向右交换
    			if (aim_c < 2)
    			{
    				//空白图片和空白处右边的图片交换 
    				map[aim_r][aim_c] = map[aim_r][aim_c + 1];
    				map[aim_r][aim_c + 1] = 8;
    				break;
    			}
    		}
    	}
    }

    六.绘图函数

    //绘图函数
    void DrawMap()
    {
    	FlushBatchDraw();  //开始渲染图片
    	for (int y = 0; y < N; y++)
    	{
    		for (int x = 0; x < N; x++)
    		{
    			putimage(x * 200, y * 200, &imgs[map[y][x]]);
    		}
    	}
    	EndBatchDraw();
    }

    七.背景音乐

    //背景音乐函数
    void BGM()
    {
    	//打开音乐,播放音乐
    	mciSendStringW(L"open ./Thrills.mp3 alias back", NULL, 0, NULL);
    	mciSendStringW(_T("play back repeat"), 0, 0, 0);
    }

    八.数据更新

    //数据更新函数
    void play()
    {
    	int col, row;  //鼠标点击的位置
    	ExMessage msg;    //鼠标消息
    	msg = getmessage(EM_MOUSE|EM_KEY);   //获取鼠标消息
    	switch (msg.message)      //对鼠标消息进行匹配
    	{
    	case WM_LBUTTONDOWN:  //当鼠标消息是左键按下时
    		//获取鼠标按下所在列
    		col = msg.x / 200;
    		if (msg.x == 600)
    			col = 2;
    		//获取鼠标按下所在行
    		row = msg.y / 200;
    		if (msg.y == 600)
    			row = 2;
    		//得到目标所在行和列
    		for (int i = 0; i < N; i++)
    		{
    			for (int j = 0; j < N; j++)
    			{
    				if (map[i][j] == 8)    //空白处为交换目标
    				{
    					aim_r = i;
    					aim_c = j;
    				}
    			}
    		}
    		//判断鼠标点击位置和目标是否相邻,相邻交换数据
    		if (row == aim_r && col == aim_c + 1 ||
    			row == aim_r && col == aim_c - 1 ||
    			row == aim_r + 1 && col == aim_c ||
    			row == aim_r - 1 && col == aim_c)
    		{
    			//鼠标点击图片和空白目标图片交换
    			map[aim_r][aim_c] = map[row][col];
    			map[row][col] = 8;
    		}
    		DrawMap();
    		break;
    	case WM_RBUTTONDOWN: //当鼠标消息是右键按下时
    		putimage(0, 0, &img[NUM]);   //将关卡图片贴到窗口上
    		break;
    	case WM_RBUTTONUP:  //当鼠标消息是右键抬起时
    		DrawMap();
    		break;
    	case WM_MBUTTONDOWN:
    		NUM++;
    		if (NUM == 4)
    			NUM = 0;   //返回第一张图
    		//重新开始游戏
    		GameInit(); //游戏初始化
    		DrawMap();  //渲染地图
    		break;
    	case WM_KEYDOWN:
    		if (msg.vkcode == VK_ESCAPE)    //按Esc键返回封面
    		{
    			start();
    			break;
    		}
    	}	
    }

    九.通关判断

    //通关判断函数
    void Judge()
    {
    	//判断当前每张图片是否在对应位置
    	if (map[0][0] == 0 && map[0][1] == 1 && map[0][2] == 2 &&
    		map[1][0] == 3 && map[1][1] == 4 && map[1][2] == 5 &&
    		map[2][0] == 6 && map[2][1] ==7 && map[2][2] == 8 )
    	{
    		//挑战成功之后将全图贴上
    		putimage(0, 0, &img[NUM++]);
    		//四个关卡都胜利之后退出程序
    		if (NUM == 4)
    		{
    			MessageBox(GetHWnd(), L"挑战成功", L"Vectory", MB_OK);
    			exit(0);
    			return;
    		}
    		//每过一个关卡判断是否进入下一个关卡
    		if (MessageBox(GetHWnd(), L"是否进入下一关", L"Vectory", MB_YESNO) == IDYES)
    		{
    			//重新开始游戏
    			GameInit(); //游戏初始化
    			DrawMap();  //渲染地图
    		}
    		//退出游戏
    		else exit(0);
    	}
    }

    十.完整程序

    #include<conio.h>
    #include<stdio.h>
    #include<easyx.h>
    #include<time.h>
    #include<Windows.h>
    #include<mmsystem.h>
    #pragma comment(lib,"Winmm.lib") 
    using namespace std;
    constexpr auto N = 3;
    IMAGE img[4], imgs[9];
    int aim_c, aim_r;
    int map[3][3] = { 0 };
    int NUM = 0;
    //游戏规则,开始界面设计
    void start();
    //封面按钮
    int rules();
    //加载资源
    void init();
    //游戏数据初始化
    void GameInit();
    //游戏渲染
    void DrawMap();
    //播放音乐
    void BGM();
    //玩家操作
    void play();
    //判断输赢
    void Judge();
    int main()
    {
    	//设置窗口大小
    	initgraph(6 * 100, 6 * 100);
    	//设置图片
    	start();
    	init();
    	GameInit();
    	DrawMap();
    	while (1)
    	{
    		play();
    		Judge();
    	}
    	system("pause");//等待用户按键
    	closegraph();
    	return 0;
    }
    //开始界面
    void start()
    {
    	loadimage(NULL, L"cover.jpg");
    	setbkmode(TRANSPARENT);
    	settextcolor(BLACK);
    	settextstyle(60, 0, _T("楷体"),0,0,4,false,false,false);
    	outtextxy(180, 120, L"拼图游戏");                    //游戏名称
    	settextstyle(30, 0, _T("微软雅黑"));
    	setfillcolor(BROWN);
    	setlinestyle(BS_SOLID, 5);
    	setlinecolor(RED);
    	fillroundrect(220, 220, 370, 270, 10, 10);
    	settextstyle(30, 0, _T("宋体"), 0, 0, 6, false, false, false);  //开始按钮
    	outtextxy(270, 230, L"开始");
    	fillroundrect(220, 300, 370, 350, 10, 10);
    	outtextxy(240, 310, L"游戏规则");
    	setfillcolor(BROWN);
    	setlinestyle(BS_SOLID, 5);
    	setlinecolor(BLACK);
    	fillcircle(490, 440, 30);  //音乐控制按钮:开
    	fillcircle(560, 440, 30);  //音乐控制按钮:关
    	outtextxy(380, 430, L"音乐:");
    	setfillcolor(BLACK);
    	POINT pts[] = { {481,425},{481,455},{507,440} };
    	fillpolygon(pts, 3);
    	fillrectangle(546, 425, 554, 455);
    	fillrectangle(566, 425, 574, 455);
    	rules();
    }
    //游戏初始化
    void init()
    {
    	//加载资源图片,4张图4个关卡
    	loadimage(&img[0], L"picture1.jpg",  600, 600);
    	loadimage(&img[1], L"picture2.jpg", 600, 600);
    	loadimage(&img[2], L"picture3.jpg", 600, 600);
    	loadimage(&img[3], L"picture4.jpg", 600, 600);
    	//设置最后一张图片为空白图片,作为目标图片
    	loadimage(&imgs[8], L"white.jpg", 200, 200);
    	//设置随机种子
    	srand((unsigned)time(NULL));
    }
    //封面选项函数
    int rules()
    {
    	  ExMessage Mou;    //鼠标消息
    	  while (1)
    	  {
    		  Mou = getmessage(EM_MOUSE);
    		  switch (Mou.message)    //对鼠标信息进行匹配
    		  {
    		  case WM_LBUTTONDOWN:            //按下左键
    			  if (Mou.x >= 220 && Mou.x <= 370 && Mou.y >= 300 && Mou.y <= 350)
    			  {
    				  HWND hwnd = GetHWnd();
    				  MessageBox(NULL, L"1.鼠标左键点击空白图处周围图片交换位置\n2.鼠标右键任意处按下显示参照图片\n3.鼠标中键更换背景图片\n4.按Esc键返回封面", L"游戏规则", MB_OKCANCEL);
    				  break;                     //规则按钮
    			  }
    			  if (Mou.x >= 220 && Mou.x <= 370 && Mou.y >= 220 && Mou.y <= 270)
    			  {
    				  return 0;                  //开始按钮
    			  }
    			  if (Mou.x >= 460 && Mou.x <= 520 && Mou.y >= 410 && Mou.y <= 470)
    			  {
    				  BGM();                     //音乐播放按钮
    				  break;
    			  }
    			  if (Mou.x >= 530 && Mou.x <= 590 && Mou.y >= 410 && Mou.y <= 470)
    			  {
    				  mciSendString(L"close back", 0, 0, 0);     //音乐关闭按钮
    				  break;
    			  }
    		  }
    	  }
     }
    //拼图构造函数
    void GameInit()
    {
    	//把拼图贴上去
    	putimage(0, 0, &img[NUM]);
    	//设置绘图目标为img对象   对拼图图片进行切割
    	SetWorkingImage(&img[NUM]);
    	for (int y = 0, n = 0; y < N; y++)
    	{
    		for (int x = 0; x < N; x++)
    		{
    			if (n == 8)	break;
    			//获取100*100像素图片,存储在img中;
    			getimage(&imgs[n++], x * 200, y * 200, (x + 1) * 200, (y + 1) * 200);
    		}
    	}
    	//设置绘图目标为绘图窗口
    	SetWorkingImage();
    	//初始化地图0~15
    	for (int i = 0, k = 0; i < N; i++)
    	{
    		for (int j = 0; j < N; j++)
    		{
    			map[i][j] = k++;
    		}
    	}
    	//打乱地图
    	for (int k = 0; k <= 1000; k++)
    	{
    		//得到目标所在的行和列
    		for (int i = 0; i < N; i++)
    		{
    			for (int j = 0; j < N; j++)
    			{
    				if (map[i][j] == 8)  //空白图片作为交换目标
    				{
    					aim_r = i;
    					aim_c = j;
    					break;
    				}
    			}
    		}
    		//一千次打乱顺序之后需要将空白图片转移到右下角
    		//可以封装成函数下面这个代码
    		if (k == 1000)
    		{
    			//将空白图片循环转移到右下角
    			while (aim_r < 2)
    			{
    				//保证空白目标在最下
    				map[aim_r][aim_c] = map[aim_r + 1][aim_c];
    				map[aim_r + 1][aim_c] = 8;
    				aim_r++;
    			}
    			while (aim_c < 2)
    			{
    				//保证空白目标在最右
    				map[aim_r][aim_c] = map[aim_r][aim_c + 1];
    				map[aim_r][aim_c + 1] = 8;
    				aim_c++;
    			}
    			return;
    		}
    		int dir = rand() % 4;   //随机一个方向
    		switch (dir)
    		{
    		case 0:  //向上交换
    			if (aim_r >= 1)
    			{
    				//空白图片和空白处上面的图片交换
    				map[aim_r][aim_c] = map[aim_r - 1][aim_c];
    				map[aim_r - 1][aim_c] = 8;
    				break;
    			}
    		case 1:  //向下交换
    			if (aim_r < 2)
    			{
    				//空白图片和空白处下面的图片交换
    				map[aim_r][aim_c] = map[aim_r + 1][aim_c];
    				map[aim_r + 1][aim_c] = 8;
    				break;
    			}
    		case 2:  //向左交换
    			if (aim_c >= 1)
    			{
    				//空白图片和空白处左边的图片交换
    				map[aim_r][aim_c] = map[aim_r][aim_c - 1];
    				map[aim_r][aim_c - 1] = 8;
    				break;
    			}
    		case 3:  //向右交换
    			if (aim_c < 2)
    			{
    				//空白图片和空白处右边的图片交换 
    				map[aim_r][aim_c] = map[aim_r][aim_c + 1];
    				map[aim_r][aim_c + 1] = 8;
    				break;
    			}
    		}
    	}
    }
    //绘图函数
    void DrawMap()
    {
    	FlushBatchDraw();  //开始渲染图片
    	for (int y = 0; y < N; y++)
    	{
    		for (int x = 0; x < N; x++)
    		{
    			putimage(x * 200, y * 200, &imgs[map[y][x]]);
    		}
    	}
    	EndBatchDraw();
    }
    //背景音乐函数
    void BGM()
    {
    	//打开音乐,播放音乐
    	mciSendStringW(L"open ./Thrills.mp3 alias back", NULL, 0, NULL);
    	mciSendStringW(_T("play back repeat"), 0, 0, 0);
    }
    //数据更新函数
    void play()
    {
    	int col, row;  //鼠标点击的位置
    	ExMessage msg;    //鼠标消息
    	msg = getmessage(EM_MOUSE|EM_KEY);   //获取鼠标消息
    	switch (msg.message)      //对鼠标消息进行匹配
    	{
    	case WM_LBUTTONDOWN:  //当鼠标消息是左键按下时
    		//获取鼠标按下所在列
    		col = msg.x / 200;
    		if (msg.x == 600)
    			col = 2;
    		//获取鼠标按下所在行
    		row = msg.y / 200;
    		if (msg.y == 600)
    			row = 2;
    		//得到目标所在行和列
    		for (int i = 0; i < N; i++)
    		{
    			for (int j = 0; j < N; j++)
    			{
    				if (map[i][j] == 8)    //空白处为交换目标
    				{
    					aim_r = i;
    					aim_c = j;
    				}
    			}
    		}
    		//判断鼠标点击位置和目标是否相邻,相邻交换数据
    		if (row == aim_r && col == aim_c + 1 ||
    			row == aim_r && col == aim_c - 1 ||
    			row == aim_r + 1 && col == aim_c ||
    			row == aim_r - 1 && col == aim_c)
    		{
    			//鼠标点击图片和空白目标图片交换
    			map[aim_r][aim_c] = map[row][col];
    			map[row][col] = 8;
    		}
    		DrawMap();
    		break;
    	case WM_RBUTTONDOWN: //当鼠标消息是右键按下时
    		putimage(0, 0, &img[NUM]);   //将关卡图片贴到窗口上
    		break;
    	case WM_RBUTTONUP:  //当鼠标消息是右键抬起时
    		DrawMap();
    		break;
    	case WM_MBUTTONDOWN:
    		NUM++;
    		if (NUM == 4)
    			NUM = 0;   //返回第一张图
    		//重新开始游戏
    		GameInit(); //游戏初始化
    		DrawMap();  //渲染地图
    		break;
    	case WM_KEYDOWN:
    		if (msg.vkcode == VK_ESCAPE)    //按Esc键返回封面
    		{
    			start();
    			break;
    		}
    	}	
    }
    //通关判断函数
    void Judge()
    {
    	//判断当前每张图片是否在对应位置
    	if (map[0][0] == 0 && map[0][1] == 1 && map[0][2] == 2 &&
    		map[1][0] == 3 && map[1][1] == 4 && map[1][2] == 5 &&
    		map[2][0] == 6 && map[2][1] ==7 && map[2][2] == 8 )
    	{
    		//挑战成功之后将全图贴上
    		putimage(0, 0, &img[NUM++]);
    		//四个关卡都胜利之后退出程序
    		if (NUM == 4)
    		{
    			MessageBox(GetHWnd(), L"挑战成功", L"Vectory", MB_OK);
    			exit(0);
    			return;
    		}
    		//每过一个关卡判断是否进入下一个关卡
    		if (MessageBox(GetHWnd(), L"是否进入下一关", L"Vectory", MB_YESNO) == IDYES)
    		{
    			//重新开始游戏
    			GameInit(); //游戏初始化
    			DrawMap();  //渲染地图
    		}
    		//退出游戏
    		else exit(0);
    	}
    }

    总结

    本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注海外IDC网的更多内容!

    【本文由:日本cn2服务器 提供 转载请保留URL】