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

开发环境: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);

}

}

总结

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

以上是 C++语言实现拼图游戏详解 的全部内容, 来源链接: utcz.com/p/247255.html

回到顶部