C++語(yǔ)言實(shí)現(xiàn)拼圖游戲詳解
開發(fā)環(huán)境:Visual Studio 2019,easyx圖形庫(kù)。
easyx下載官網(wǎng):
EasyX Graphics Library for C++
easyx使用文檔:
EasyX 文檔 - 函數(shù)說(shuō)明
游戲功能列表:
其主要功能描述如下:
1.圖片尺寸自適應(yīng)
2.圖片動(dòng)態(tài)分割
3.查看原圖
4.隨機(jī)切換圖片
5.鼠標(biāo)拖動(dòng)拼圖<——>交換拼圖塊
6.自動(dòng)判斷拼圖成功
拓展功能:
- 背景音樂(lè)(開,關(guān))
- 游戲中Esc鍵返回桌面
- 游戲規(guī)則窗口
游戲效果

封面(音樂(lè)按鈕有點(diǎn)拉跨~)

游戲初始圖(我的心是冰冰的)

通關(guān)圖
一.頭文件和基本量
#include<conio.h>
#include<stdio.h>
#include<easyx.h>
#include<time.h>
#include<Windows.h>
#include<mmsystem.h> //音樂(lè)
#pragma comment(lib,"Winmm.lib") //靜態(tài)庫(kù),調(diào)用音樂(lè)
using namespace std;
constexpr auto N = 3; //3*3拼圖
IMAGE img[4], imgs[9]; //img存整張圖片,imgs暫存拼圖塊
int aim_c, aim_r; //拼圖塊坐標(biāo)
int map[3][3] = { 0 }; //存拼圖塊
int NUM = 0; //關(guān)卡數(shù)計(jì)數(shù)
二.封面
//開始界面
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"游戲規(guī)則");
setfillcolor(BROWN);
setlinestyle(BS_SOLID, 5);
setlinecolor(BLACK);
fillcircle(490, 440, 30); //音樂(lè)控制按鈕:開
fillcircle(560, 440, 30); //音樂(lè)控制按鈕:關(guān)
outtextxy(380, 430, L"音樂(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();
}
三.數(shù)據(jù)初始化
//游戲初始化
void init()
{
//加載資源圖片,4張圖4個(gè)關(guān)卡
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);
//設(shè)置最后一張圖片為空白圖片,作為目標(biāo)圖片
loadimage(&imgs[8], L"white.jpg", 200, 200);
//設(shè)置隨機(jī)種子
srand((unsigned)time(NULL));
}
四.封面規(guī)則按鈕
//封面規(guī)則函數(shù)
int rules()
{
ExMessage Mou; //鼠標(biāo)消息
while (1)
{
Mou = getmessage(EM_MOUSE);
switch (Mou.message) //對(duì)鼠標(biāo)信息進(jìn)行匹配
{
case WM_LBUTTONDOWN: //按下左鍵
if (Mou.x >= 220 && Mou.x <= 370 && Mou.y >= 300 && Mou.y <= 350)
{
HWND hwnd = GetHWnd();
MessageBox(NULL, L"1.鼠標(biāo)左鍵點(diǎn)擊空白圖處周圍圖片交換位置\n2.鼠標(biāo)右鍵任意處按下顯示參照?qǐng)D片\n3.鼠標(biāo)中鍵更換背景圖片\n4.按Esc鍵返回封面", L"游戲規(guī)則", MB_OKCANCEL);
break; //規(guī)則按鈕
}
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(); //音樂(lè)播放按鈕
break;
}
if (Mou.x >= 530 && Mou.x <= 590 && Mou.y >= 410 && Mou.y <= 470)
{
mciSendString(L"close back", 0, 0, 0); //音樂(lè)關(guān)閉按鈕
break;
}
}
}
}
五.構(gòu)造拼圖
//拼圖構(gòu)造函數(shù)
void GameInit()
{
//把拼圖貼上去
putimage(0, 0, &img[NUM]);
//設(shè)置繪圖目標(biāo)為img對(duì)象 對(duì)拼圖圖片進(jìn)行切割
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像素圖片,存儲(chǔ)在img中;
getimage(&imgs[n++], x * 200, y * 200, (x + 1) * 200, (y + 1) * 200);
}
}
//設(shè)置繪圖目標(biāo)為繪圖窗口
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++)
{
//得到目標(biāo)所在的行和列
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (map[i][j] == 8) //空白圖片作為交換目標(biāo)
{
aim_r = i;
aim_c = j;
break;
}
}
}
//一千次打亂順序之后需要將空白圖片轉(zhuǎn)移到右下角
//可以封裝成函數(shù)下面這個(gè)代碼
if (k == 1000)
{
//將空白圖片循環(huán)轉(zhuǎn)移到右下角
while (aim_r < 2)
{
//保證空白目標(biāo)在最下
map[aim_r][aim_c] = map[aim_r + 1][aim_c];
map[aim_r + 1][aim_c] = 8;
aim_r++;
}
while (aim_c < 2)
{
//保證空白目標(biāo)在最右
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; //隨機(jī)一個(gè)方向
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;
}
}
}
}
六.繪圖函數(shù)
//繪圖函數(shù)
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();
}
七.背景音樂(lè)
//背景音樂(lè)函數(shù)
void BGM()
{
//打開音樂(lè),播放音樂(lè)
mciSendStringW(L"open ./Thrills.mp3 alias back", NULL, 0, NULL);
mciSendStringW(_T("play back repeat"), 0, 0, 0);
}
八.數(shù)據(jù)更新
//數(shù)據(jù)更新函數(shù)
void play()
{
int col, row; //鼠標(biāo)點(diǎn)擊的位置
ExMessage msg; //鼠標(biāo)消息
msg = getmessage(EM_MOUSE|EM_KEY); //獲取鼠標(biāo)消息
switch (msg.message) //對(duì)鼠標(biāo)消息進(jìn)行匹配
{
case WM_LBUTTONDOWN: //當(dāng)鼠標(biāo)消息是左鍵按下時(shí)
//獲取鼠標(biāo)按下所在列
col = msg.x / 200;
if (msg.x == 600)
col = 2;
//獲取鼠標(biāo)按下所在行
row = msg.y / 200;
if (msg.y == 600)
row = 2;
//得到目標(biāo)所在行和列
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (map[i][j] == 8) //空白處為交換目標(biāo)
{
aim_r = i;
aim_c = j;
}
}
}
//判斷鼠標(biāo)點(diǎn)擊位置和目標(biāo)是否相鄰,相鄰交換數(shù)據(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)
{
//鼠標(biāo)點(diǎn)擊圖片和空白目標(biāo)圖片交換
map[aim_r][aim_c] = map[row][col];
map[row][col] = 8;
}
DrawMap();
break;
case WM_RBUTTONDOWN: //當(dāng)鼠標(biāo)消息是右鍵按下時(shí)
putimage(0, 0, &img[NUM]); //將關(guān)卡圖片貼到窗口上
break;
case WM_RBUTTONUP: //當(dāng)鼠標(biāo)消息是右鍵抬起時(shí)
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;
}
}
}
九.通關(guān)判斷
//通關(guān)判斷函數(shù)
void Judge()
{
//判斷當(dāng)前每張圖片是否在對(duì)應(yīng)位置
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 )
{
//挑戰(zhàn)成功之后將全圖貼上
putimage(0, 0, &img[NUM++]);
//四個(gè)關(guān)卡都勝利之后退出程序
if (NUM == 4)
{
MessageBox(GetHWnd(), L"挑戰(zhàn)成功", L"Vectory", MB_OK);
exit(0);
return;
}
//每過(guò)一個(gè)關(guān)卡判斷是否進(jìn)入下一個(gè)關(guān)卡
if (MessageBox(GetHWnd(), L"是否進(jìn)入下一關(guān)", 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;
//游戲規(guī)則,開始界面設(shè)計(jì)
void start();
//封面按鈕
int rules();
//加載資源
void init();
//游戲數(shù)據(jù)初始化
void GameInit();
//游戲渲染
void DrawMap();
//播放音樂(lè)
void BGM();
//玩家操作
void play();
//判斷輸贏
void Judge();
int main()
{
//設(shè)置窗口大小
initgraph(6 * 100, 6 * 100);
//設(shè)置圖片
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"游戲規(guī)則");
setfillcolor(BROWN);
setlinestyle(BS_SOLID, 5);
setlinecolor(BLACK);
fillcircle(490, 440, 30); //音樂(lè)控制按鈕:開
fillcircle(560, 440, 30); //音樂(lè)控制按鈕:關(guān)
outtextxy(380, 430, L"音樂(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個(gè)關(guān)卡
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);
//設(shè)置最后一張圖片為空白圖片,作為目標(biāo)圖片
loadimage(&imgs[8], L"white.jpg", 200, 200);
//設(shè)置隨機(jī)種子
srand((unsigned)time(NULL));
}
//封面選項(xiàng)函數(shù)
int rules()
{
ExMessage Mou; //鼠標(biāo)消息
while (1)
{
Mou = getmessage(EM_MOUSE);
switch (Mou.message) //對(duì)鼠標(biāo)信息進(jìn)行匹配
{
case WM_LBUTTONDOWN: //按下左鍵
if (Mou.x >= 220 && Mou.x <= 370 && Mou.y >= 300 && Mou.y <= 350)
{
HWND hwnd = GetHWnd();
MessageBox(NULL, L"1.鼠標(biāo)左鍵點(diǎn)擊空白圖處周圍圖片交換位置\n2.鼠標(biāo)右鍵任意處按下顯示參照?qǐng)D片\n3.鼠標(biāo)中鍵更換背景圖片\n4.按Esc鍵返回封面", L"游戲規(guī)則", MB_OKCANCEL);
break; //規(guī)則按鈕
}
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(); //音樂(lè)播放按鈕
break;
}
if (Mou.x >= 530 && Mou.x <= 590 && Mou.y >= 410 && Mou.y <= 470)
{
mciSendString(L"close back", 0, 0, 0); //音樂(lè)關(guān)閉按鈕
break;
}
}
}
}
//拼圖構(gòu)造函數(shù)
void GameInit()
{
//把拼圖貼上去
putimage(0, 0, &img[NUM]);
//設(shè)置繪圖目標(biāo)為img對(duì)象 對(duì)拼圖圖片進(jìn)行切割
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像素圖片,存儲(chǔ)在img中;
getimage(&imgs[n++], x * 200, y * 200, (x + 1) * 200, (y + 1) * 200);
}
}
//設(shè)置繪圖目標(biāo)為繪圖窗口
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++)
{
//得到目標(biāo)所在的行和列
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (map[i][j] == 8) //空白圖片作為交換目標(biāo)
{
aim_r = i;
aim_c = j;
break;
}
}
}
//一千次打亂順序之后需要將空白圖片轉(zhuǎn)移到右下角
//可以封裝成函數(shù)下面這個(gè)代碼
if (k == 1000)
{
//將空白圖片循環(huán)轉(zhuǎn)移到右下角
while (aim_r < 2)
{
//保證空白目標(biāo)在最下
map[aim_r][aim_c] = map[aim_r + 1][aim_c];
map[aim_r + 1][aim_c] = 8;
aim_r++;
}
while (aim_c < 2)
{
//保證空白目標(biāo)在最右
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; //隨機(jī)一個(gè)方向
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;
}
}
}
}
//繪圖函數(shù)
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();
}
//背景音樂(lè)函數(shù)
void BGM()
{
//打開音樂(lè),播放音樂(lè)
mciSendStringW(L"open ./Thrills.mp3 alias back", NULL, 0, NULL);
mciSendStringW(_T("play back repeat"), 0, 0, 0);
}
//數(shù)據(jù)更新函數(shù)
void play()
{
int col, row; //鼠標(biāo)點(diǎn)擊的位置
ExMessage msg; //鼠標(biāo)消息
msg = getmessage(EM_MOUSE|EM_KEY); //獲取鼠標(biāo)消息
switch (msg.message) //對(duì)鼠標(biāo)消息進(jìn)行匹配
{
case WM_LBUTTONDOWN: //當(dāng)鼠標(biāo)消息是左鍵按下時(shí)
//獲取鼠標(biāo)按下所在列
col = msg.x / 200;
if (msg.x == 600)
col = 2;
//獲取鼠標(biāo)按下所在行
row = msg.y / 200;
if (msg.y == 600)
row = 2;
//得到目標(biāo)所在行和列
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (map[i][j] == 8) //空白處為交換目標(biāo)
{
aim_r = i;
aim_c = j;
}
}
}
//判斷鼠標(biāo)點(diǎn)擊位置和目標(biāo)是否相鄰,相鄰交換數(shù)據(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)
{
//鼠標(biāo)點(diǎn)擊圖片和空白目標(biāo)圖片交換
map[aim_r][aim_c] = map[row][col];
map[row][col] = 8;
}
DrawMap();
break;
case WM_RBUTTONDOWN: //當(dāng)鼠標(biāo)消息是右鍵按下時(shí)
putimage(0, 0, &img[NUM]); //將關(guān)卡圖片貼到窗口上
break;
case WM_RBUTTONUP: //當(dāng)鼠標(biāo)消息是右鍵抬起時(shí)
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;
}
}
}
//通關(guān)判斷函數(shù)
void Judge()
{
//判斷當(dāng)前每張圖片是否在對(duì)應(yīng)位置
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 )
{
//挑戰(zhàn)成功之后將全圖貼上
putimage(0, 0, &img[NUM++]);
//四個(gè)關(guān)卡都勝利之后退出程序
if (NUM == 4)
{
MessageBox(GetHWnd(), L"挑戰(zhàn)成功", L"Vectory", MB_OK);
exit(0);
return;
}
//每過(guò)一個(gè)關(guān)卡判斷是否進(jìn)入下一個(gè)關(guān)卡
if (MessageBox(GetHWnd(), L"是否進(jìn)入下一關(guān)", L"Vectory", MB_YESNO) == IDYES)
{
//重新開始游戲
GameInit(); //游戲初始化
DrawMap(); //渲染地圖
}
//退出游戲
else exit(0);
}
}
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C語(yǔ)言?八大排序算法的過(guò)程圖解及實(shí)現(xiàn)代碼
排序是數(shù)據(jù)結(jié)構(gòu)中很重要的一章,本文主要為大家介紹了常用的八個(gè)排序算法(插入,希爾,選擇,堆排,冒泡,快排,歸并,計(jì)數(shù))的過(guò)程及代碼實(shí)現(xiàn),需要的朋友可以參考一下2021-12-12
C/C++時(shí)間庫(kù)chrono的使用總結(jié)
深入解析C++中的構(gòu)造函數(shù)和析構(gòu)函數(shù)
C語(yǔ)言中實(shí)現(xiàn)itoa函數(shù)的實(shí)例

