每天一個C語言小項目,提升你的編程能力!
一、遊戲說明本遊戲仿造 4399 的小遊戲-圍住神經貓。
遊戲操作:通過鼠标點擊操作,設置路障,圍住神經貓,當成功圍住神經貓時,遊戲勝利。當神經貓逃離地圖邊緣,遊戲失敗。
二、遊戲截圖三、實現思路
1. 地圖還原:
首先是遊戲的道路,這裡我們采用繪制灰白色的圓來表示可走的路,用黃色的圓來表示已存在的障礙物。同時還需要注意奇偶行需要交錯排列。
2. 貓的移動:
這裡我們采用廣度優先搜索求最短路徑。"貓"在一個位置,能夠移動的方向有6個,需要注意的是:
由于奇偶行交替排列,導緻奇偶行貓的可行路徑是不一樣的,奇數行:上,下,左,右,左下,左上。
偶數行:上,下,左,右,右下,右上。剩下的就是常規的求最短路徑即可。
注:代碼中所用圖片,請見文末
四、完整代碼
簡單了解遊戲後我們就來試試吧!(直接上源碼,大家可以看注釋)
/////////////////////////////////////////////////////////
// 程序名稱:圍住神經貓
// 編譯環境 Visual Studio2019(C 語言标準選擇C 17),EasyX
// C語言/C 編程交流Q群:734106058
#include <graphics.h>
#include <queue>
#include <cstring>
#include <ctime>
#include <vector>
#pragma comment( lib, "MSIMG32.LIB")
#define pix 50 // 像素比例
#define hight (14 * pix)
#define width (10 * pix)
using namespace std;
int stepS; // 記錄已經走的步數
int startBarrier; // 開始的障礙物數目
wchar_t Score_[30];
MOUSEMSG m; // 鼠标操作
IMAGE pig, bkimg;; // 加載圖片
enum class picture
{
none, barrier
};
struct XY
{
int x, y;
int lastX, lastY; //記錄上一個點的坐标
}cat;
struct node
{
int x, y; //坐标,圓心位置
picture pic; //當前位置的圖片内容
};
node canvas[10][10]; // 地圖
bool visit[10][10]; // 記錄是否訪問過地圖中的元素
int path[10][10][2]; // 記錄上一個位置
//注意:因為地圖是交錯排列的,奇數列與偶數列貓的移動不同
int dirOdd[6][2]{ 1,0,-1,0,0,1,0,-1,-1,-1,-1,1 }; //控制方向奇數列
int dirEven[6][2]{ 1,0,-1,0,0,1,0,-1,1,-1,1,1 }; //控制方向偶數列
int main();
//貼圖函數
void transparentimage(IMAGE* dstimg, int x, int y, IMAGE* srcimg,int direction)
{
HDC dstDC = GetImageHDC(dstimg);
HDC srcDC = GetImageHDC(srcimg);
int w = 50;
int h = 100;
// 使用 Windows GDI 函數實現透明位圖
if (direction == 0)
TransparentBlt(dstDC, x, y, w, h, srcDC, 0, 0, w, h, 0);
else
TransparentBlt(dstDC, x, y, w, h, srcDC, 10, 187, w, h, 0);
}
//遊戲初始化
void initial()
{
srand(time(0));
stepS = 0;
startBarrier = rand() % 6 8; //障礙物數量
loadimage(&pig, L"pig.png");
loadimage(&bkimg, L"bkground.jpg", width, hight, true);
initgraph(width, hight);
HWND wnd = GetHWnd();
SetWindowText(wnd, L"圍住神經貓");//設置文章标題
for (int i = 1; i <= 9; i)
for (int j = 1; j <= 9; j)
{
if (i & 1) //如果是奇數行
canvas[i][j] = node{ j * pix - pix / 4, pix * 4 i * pix ,picture::none };
else
canvas[i][j] = node{ j * pix pix / 4, pix * 4 i * pix ,picture::none };
}
cat.x = 5; cat.y = 5; //貓最開始的地方
while (startBarrier--)
{
int bx, by; //設置初始障礙
do
{
bx = rand() % 10;
by = rand() % 10;
} while (canvas[by][bx].pic == picture::barrier || (by == cat.y && bx == cat.x));
canvas[by][bx].pic = picture::barrier;
}
setbkmode(TRANSPARENT);
BeginBatchDraw();
}
//繪制遊戲畫面, 白色:空 黃色:障礙物
void show()
{
putimage(0, 0, &bkimg);
setbkcolor(WHITE);
settextstyle(20, 0, L"微軟雅黑");
outtextxy(200, 170, L"重玩");
outtextxy(250, 180, L"步數: ");
swprintf(Score_, 29, L"%d", stepS);
outtextxy(290, 180, Score_);
for (int i = 1; i <= 9; i)
{
for (int j = 1; j <= 9; j)
{
if (canvas[i][j].pic == picture::barrier)
setfillcolor(YELLOW);
else
setfillcolor(LIGHTGRAY);
solidcircle(canvas[i][j].x, canvas[i][j].y, (pix - 4) / 2);
}
}
if (cat.y & 1) //奇數列
transparentimage(NULL, cat.x * pix - pix / 4 - 25, pix * 3 cat.y * pix - 21, &pig,0);
else //偶數列
transparentimage(NULL, cat.x * pix - 25 pix / 4, pix * 3 cat.y * pix - 21, &pig,1);
FlushBatchDraw();
}
//尋找下一個點的位置
struct LastOrder
{
int x, y;
};
vector<LastOrder> vec;
void findNextXY(int x, int y)
{
if (x == cat.x && y == cat.y)
{
vec.push_back({ x,y });
return;
}
else
{
findNextXY(path[y][x][0], path[y][x][1]);
vec.push_back({ x,y });
}
}
//利用廣度優先搜索求最短路徑,xy為數組的i,j下标,注意傳參
void bfs(XY xy)
{
//每次搜索時初始化數組
memset(visit, false, sizeof(visit));
memset(path, 0, sizeof(path));
bool tag = true;
queue<XY> que;
que.push(xy);
visit[xy.y][xy.x] = true;
while (!que.empty())
{
XY temp = que.front();
que.pop();
//如果找到出口
if (temp.x == 1 || temp.x == 9 || temp.y == 1 || temp.y == 9)
{
findNextXY(temp.x, temp.y);
cat.x = vec[1].x;
cat.y = vec[1].y;
vec.clear();
tag = false;
break;
}
int dx, dy;
//尋找可走的路
for (int i = 0; i < 6; i)
{
if (temp.y & 1)
{
dx = temp.x dirOdd[i][0];
dy = temp.y dirOdd[i][1];
}
else
{
dx = temp.x dirEven[i][0];
dy = temp.y dirEven[i][1];
}
if (dx >= 1 && dx <= 9 && dy >= 1 && dy <= 9 && !visit[dy][dx] && canvas[dy][dx].pic == picture::none)
{
visit[dy][dx] = true;
path[dy][dx][0] = temp.x;
path[dy][dx][1] = temp.y;
que.push({ dx,dy,temp.x,temp.y });
}
}
}
if (tag) //如果沒找到出口
{
show();
HWND wnd = GetHWnd();
swprintf(Score_, 29, L"你共用了%d步,重玩一局嗎", stepS);
FlushBatchDraw();
if (MessageBox(wnd, Score_, L"成功", MB_YESNO | MB_ICONQUESTION) == IDYES)
main();
else
exit(-1);
}
}
//鼠标操作
void dataChangeWithMouseHit()
{
while (true)
{
m = GetMouseMsg();
if (m.x >= 200 && m.x <= 230 && m.y >= 170 && m.y <= 200)
settextcolor(BLACK);
else
settextcolor(WHITE);
outtextxy(200, 170, L"重玩");
FlushBatchDraw();
if (m.uMsg == WM_LBUTTONDOWN)
{
if (m.x >= 200 && m.x <= 230 && m.y >= 170 && m.y <= 200)
main();
for (int i = 1; i <= 9; i)
for (int j = 1; j <= 9; j)
//如果在當前方格内,則改變信息
if (canvas[i][j].pic != picture::barrier && (m.x - canvas[i][j].x) * (m.x - canvas[i][j].x)
(m.y - canvas[i][j].y) * (m.y - canvas[i][j].y) <= (pix - 4) * (pix - 4) / 4)
{
canvas[i][j].pic = picture::barrier;
stepS ;
bfs({ cat.x,cat.y,0,0 });
return;
}
}
}
}
//不需要鼠标的操作,判斷貓是否跑掉
void dataChangeWithoutMouseHit()
{
if (cat.x == 1 || cat.y == 1 || cat.x == 9 || cat.y == 9)
{
show();
HWND wnd = GetHWnd();
if (MessageBox(wnd, L"遊戲結束。\n神經貓跑掉了!,重玩一局嗎", L"詢問", MB_YESNO | MB_ICONQUESTION) == IDYES)
main();
else
exit(-1);
}
}
int main()
{
initial();
while (true)
{
show();
dataChangeWithMouseHit();
dataChangeWithoutMouseHit();
Sleep(20);
}
return 0;
}
大家趕緊去動手試試吧!
此外,我也給大家分享我收集的其他資源,從最零基礎開始的教程到C語言C 項目案例,幫助大家在學習C語言的道路上披荊斬棘!
編程學習書籍分享:
編程學習視頻分享:
整理分享(多年學習的源碼、項目實戰視頻、項目筆記,基礎入門教程)最重要的是你可以在群裡面交流提問編程問題哦!
對于C/C 感興趣可以關注小編在後台私信我:【編程交流】一起來學習哦!可以領取一些C/C 的項目學習視頻資料哦!已經設置好了關鍵詞自動回複,自動領取就好了!
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!