每天一個(gè)C語言小項(xiàng)目,提升你的編程能力!
網(wǎng)上有一個(gè)香蕉金剛的跑酷游戲,不過我們這個(gè)扔香蕉游戲模仿的并不是這個(gè),而是模仿的微軟在 20 多年前的一個(gè)小游戲,不知道誰也有印象呢?
圖片都是從原來的游戲中抓圖弄出來的,顏色也是從原游戲抓圖中取色設(shè)置的,應(yīng)該和原來的風(fēng)格很像。
你的任務(wù)是用香蕉擊中你的對(duì)手。
你可以通過鼠標(biāo)調(diào)整投擲香蕉的角度和力度,香蕉會(huì)受重力加速度的影響。同時(shí),請(qǐng)注意屏幕底部表示風(fēng)力的箭頭,香蕉同樣會(huì)受風(fēng)力影響。風(fēng)力的箭頭越長(zhǎng),表示風(fēng)力越強(qiáng)。還有,周圍的樓宇會(huì)阻擋你的香蕉。(好像有點(diǎn)像憤怒的小鳥)
游戲中涉及到兩個(gè)玩家的代表人物和香蕉們,你可能需要自己找到兩張圖,然后通過easyx的貼圖技術(shù)弄進(jìn)去。當(dāng)然你也可以來找我(在文末)
其他的部分你可以直接查看下面的游戲源代碼:
本項(xiàng)目編譯環(huán)境:Visual Studio 2013/2019/2022,EasyX插件
代碼展示:
1.定義變量、函數(shù)和一些必要的常量
#include#include #include #include #include // 定義常量 #define PI 3.1415926536 // 圓周率 #define SCRWIDTH 640 // 屏幕寬度 #define SCRHEIGHT 480 // 屏幕高度 #define GRAVITY 9.8 // 重力加速度 #define BACKATTR BLUE // 背景的顏色 #define OBJECTCOLOR 0x55AAFF // 對(duì)手的顏色 #define EXPLOSIONCOLOR 0x5500FF // 爆炸的顏色 #define SUNATTR 0x00FFFF // 太陽的顏色 #define SUNHEIGHT 40 // 太陽的高度 #define SUNHAPPY true // 太陽高興 #define SUNSHOCK false // 太陽受驚 // 全局變量 IMAGE g_imgBanana[4]; // 香蕉圖片 IMAGE g_imgGorD; // 大猩猩(雙手放下) IMAGE g_imgGorL; // 大猩猩(左邊的手抬起) IMAGE g_imgGorR; // 大猩猩(右邊的手抬起) POINT g_ptGorilla[2]; // 兩個(gè)游戲者的位置 int g_iLastBuilding; // 最后一棟樓的編號(hào) int g_iWind; // 風(fēng)力 bool g_bSunHit; // 是否擊中太陽 // 函數(shù)定義 void Init(); // 初始化 void Intro(); // 游戲介紹 void PlayGame(TCHAR *player1, TCHAR *player2); // 主游戲函數(shù) void MakeCityScape(POINT *aryBCoor); // 創(chuàng)建隨機(jī)的游戲場(chǎng)景 void PlaceGorillas (POINT *aryBCoor); // 將游戲者放到樓宇頂端 void DoSun(bool smile); // 繪制太陽 bool DoShot(int idPlayer, int x, int y, int* win); // 接收游戲者輸入,實(shí)現(xiàn)扔香蕉攻擊對(duì)方 int PlotShot(int startX, int startY, double angle, int velocity, int idPlayer); // 進(jìn)行香蕉攻擊,使香蕉劃過屏幕 void DrawBanana(int x, int y, int r, bool d); // 繪制香蕉 void DoExplosion(int x, int y); // 香蕉攻擊后的爆炸效果 int ExplodeGorilla(int x, int y); // 游戲者死亡后爆炸 void VictoryDance(int idPlayer); // 繪制跳舞的大猩猩(勝利后執(zhí)行)
2.初始化游戲圖片元素(香蕉和猩猩本猩)
void Init() { initgraph(SCRWIDTH, SCRHEIGHT); // 創(chuàng)建繪圖窗口 srand((unsigned int)time(NULL)); // 設(shè)置隨機(jī)種子 // 初始化香蕉圖案 IMAGE tmp; loadimage(&tmp, _T("res\Banana.gif")); SetWorkingImage(&tmp); getimage(&g_imgBanana[0], 0, 0, 9, 7); getimage(&g_imgBanana[1], 9, 0, 9, 7); getimage(&g_imgBanana[2], 18, 0, 9, 7); getimage(&g_imgBanana[3], 27, 0, 9, 7); // 初始化大猩猩圖案 loadimage(&tmp, _T("res\Gorilla.gif"), 0, 0, true); SetWorkingImage(&tmp); getimage(&g_imgGorD, 0, 0, 30, 30); getimage(&g_imgGorL, 30, 0, 30, 30); getimage(&g_imgGorR, 60, 0, 30, 30); SetWorkingImage(NULL); }
3.游戲的簡(jiǎn)單介紹
void Intro() { settextstyle(24, 0, _T("宋體")); // 在屏幕中央輸出字符串 RECT r = {0, 40, 640, 80}; drawtext(_T("扔香蕉的大猩猩"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); settextstyle(16, 0, _T("System")); r.top = 120; r.bottom = 480; drawtext(_T("這個(gè)游戲模仿的微軟在 20 多年前的一個(gè)小游戲, 不知道誰也有印象呢? ") _T("你的任務(wù)是用香蕉擊中你的對(duì)手。 你可以通過鼠標(biāo)調(diào)整投擲香蕉的角度和力度, ") _T("香蕉會(huì)受重力加速度的影響。 同時(shí),請(qǐng)注意屏幕底部表示風(fēng)力的箭頭,") _T("香蕉同樣會(huì)受風(fēng)力影響。 風(fēng)力的箭頭越長(zhǎng),表示風(fēng)力越強(qiáng)。 ") _T("還有,周圍的樓宇會(huì)阻擋你的香蕉。 "), &r, DT_CENTER | DT_VCENTER); r.top = 400; drawtext(_T("按任意鍵繼續(xù)"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); getmessage(EM_CHAR); }
4.主游戲函數(shù)
// 參數(shù): // player1, player2:游戲者名稱 void PlayGame(TCHAR *player1, TCHAR *player2) { POINT aryBCoor[31]; // 樓宇群的坐標(biāo) int aryScore[2] = {0, 0}; // 兩個(gè)游戲者的得分 TCHAR sScore[20]; // 保存得分的字符串 int player = 0; // 攻擊者 setbkcolor(BACKATTR); while(true) { cleardevice(); MakeCityScape(aryBCoor); PlaceGorillas(aryBCoor); DoSun(SUNHAPPY); bool bHit = false; while(bHit == false) { settextcolor(WHITE); RECT r = {0, 0, SCRWIDTH, 20}; drawtext(player1, &r, DT_LEFT | DT_SINGLELINE); drawtext(player2, &r, DT_RIGHT | DT_SINGLELINE); r.top = SCRHEIGHT - 40; r.bottom = SCRHEIGHT - 20; #if _MSC_VER > 1200 _stprintf_s(sScore, _T("%d >Score< %d"), aryScore[0], aryScore[1]); #else _stprintf(sScore, _T("%d >Score< %d"), aryScore[0], aryScore[1]); #endif drawtext(sScore, &r, DT_CENTER | DT_SINGLELINE); int win; // 進(jìn)行攻擊。擊中任意游戲者即返回 true。同時(shí),更新 win 為勝利者 bHit = DoShot(player, g_ptGorilla[player].x, g_ptGorilla[player].y, &win); // 如果太陽被擊中,重繪太陽 if (g_bSunHit) DoSun(SUNHAPPY); // 如果擊中對(duì)手,更新分?jǐn)?shù) if (bHit == true) aryScore[win]++; // 交替攻擊 player = 1 - player; Sleep(100); } Sleep(1000); }; }
5.創(chuàng)建隨機(jī)的游戲場(chǎng)景
// 參數(shù): // aryBCoor[]:存儲(chǔ)每一棟樓的左上角坐標(biāo) void MakeCityScape(POINT *aryBCoor) { int x = -10; // 設(shè)置隨機(jī)的樓群傾斜的趨勢(shì) int slope = rand() % 6; int iNewHt; // 新樓的高度 switch(slope) { case 0: iNewHt = 15; break; // 逐漸升高 case 1: iNewHt = 130; break; // 逐漸降低 case 2: case 3: case 4: iNewHt = 15; break; // 倒 "V" 型(比較常見) case 5: iNewHt = 130; break; // "V" 型 } int iBottomLine = 465; // 建筑的最低端 int iHtInc = 10; // 高度增加值 int iDefBWidth = 37; // 默認(rèn)的建筑寬度 int iRandomHeight = 120; // 隨機(jī)的高度差異 int iWWidth = 3; // 窗戶寬度 int iWHeight = 6; // 窗戶高度 int iWDifV = 15; // 窗戶的垂直間距 int iWDifH = 10; // 窗戶的水平間距 int iCurBuilding = 0; do { switch(slope) { case 0: iNewHt += iHtInc; break; case 1: iNewHt -= iHtInc; break; case 2: case 3: case 4: if (x > SCRWIDTH / 2) iNewHt -= 2 * iHtInc; else iNewHt += 2 * iHtInc; break; case 5: if (x > SCRWIDTH / 2) iNewHt += 2 * iHtInc; else iNewHt -= 2 * iHtInc; break; } // 設(shè)置樓宇寬度,并檢查是否超出屏幕 int iBWidth = iDefBWidth + rand() % iDefBWidth; // 設(shè)置樓宇高度,并檢查樓宇是否超出屏幕下方 int iBHeight = iNewHt + rand() % iRandomHeight; if (iBHeight < iHtInc) iBHeight = iHtInc; // 檢查樓宇是否太高 if (iBottomLine - iBHeight <= 25) iBHeight = 20; // 保存樓的坐標(biāo) aryBCoor[iCurBuilding].x = x; aryBCoor[iCurBuilding].y = iBottomLine - iBHeight; // 繪制樓宇 COLORREF aryBuildingColor[3] = {CYAN, LIGHTGRAY, RED}; // 定義樓宇的三種顏色 int colorID = rand() % 3; setlinecolor(BACKATTR); rectangle(x - 1, iBottomLine + 1, x + iBWidth + 1, iBottomLine - iBHeight - 1); setfillcolor(aryBuildingColor[colorID]); solidrectangle(x, iBottomLine, x + iBWidth, iBottomLine - iBHeight); // 繪制窗戶 int c = x + 3; do { for(int i = iBHeight - 3; i >= 7; i -= iWDifV) { int winColor; if (rand() % 4 == 0) winColor = DARKGRAY; else winColor = YELLOW; setfillcolor(winColor); solidrectangle(c, iBottomLine - i, c + iWWidth, iBottomLine - i + iWHeight); } c += iWDifH; } while(c < x + iBWidth - 3); x += iBWidth + 2; iCurBuilding++; } while(x < SCRWIDTH - 1); g_iLastBuilding = iCurBuilding - 1; // 保存最后一棟樓的編號(hào) // 設(shè)置隨機(jī)風(fēng)力 g_iWind = rand() % 61 - 30; // 繪制風(fēng)向箭頭 if (g_iWind != 0) { int windLine = g_iWind * 3 * (SCRWIDTH / 320); setlinecolor(EXPLOSIONCOLOR); int arrowDir = (g_iWind > 0) ? -2 : 2; line(SCRWIDTH / 2, SCRHEIGHT - 5, SCRWIDTH / 2 + windLine, SCRHEIGHT - 5); line(SCRWIDTH / 2 + windLine, SCRHEIGHT - 5, SCRWIDTH / 2 + windLine + arrowDir, SCRHEIGHT - 5 - 2); line(SCRWIDTH / 2 + windLine, SCRHEIGHT - 5, SCRWIDTH / 2 + windLine + arrowDir, SCRHEIGHT - 5 + 2); } }
6.繪制游戲者和太陽的位置
// 將游戲者放到樓宇頂端(從邊緣數(shù)第二個(gè)或第三個(gè)樓宇上) // 參數(shù): // aryBCoor[]:樓宇數(shù)組。保存每棟樓的左上角坐標(biāo) void PlaceGorillas(POINT *aryBCoor) { for (int i = 0; i <= 1; i++) { int iBNum = (i == 0) ? rand() % 2 + 1 : g_iLastBuilding - 1 - rand() % 2; int iBWidth = aryBCoor[iBNum + 1].x - aryBCoor[iBNum].x; g_ptGorilla[i].x = aryBCoor[iBNum].x + iBWidth / 2 - g_imgGorD.getwidth() / 2; g_ptGorilla[i].y = aryBCoor[iBNum].y - g_imgGorD.getheight(); putimage(g_ptGorilla[i].x, g_ptGorilla[i].y, &g_imgGorD); } } // 繪制太陽 // 參數(shù): // smile:太陽是否微笑 void DoSun(bool smile) { // 設(shè)置太陽的位置 int x = SCRWIDTH / 2; int y = SUNHEIGHT - 15; // 繪制太陽 // 臉 setlinecolor(SUNATTR); setfillcolor(SUNATTR); fillcircle(x, y, 12); // 光芒 for (double a = 0; a < PI * 2; a += PI / 8) line(x, y, int(x + cos(a) * 20 + 0.5), int(y + sin(a) * 16 + 0.5)); // 嘴 setlinecolor(BACKATTR); setfillcolor(BACKATTR); if (smile) // 繪制笑臉 arc(x - 8, y - 8, x + 8, y + 8, (210 * PI / 180), (330 * PI / 180)); else // 繪制受驚表情("o" 型嘴) fillcircle(x, y + 5, 3); // 眼睛 fillcircle(x - 3, y - 2, 1); fillcircle(x + 3, y - 2, 1); }
7.實(shí)現(xiàn)按鍵操作,實(shí)現(xiàn)扔香蕉功能
// 參數(shù): // idPlayer:游戲者(準(zhǔn)備扔香蕉的) // x, y:游戲者的位置 bool DoShot(int idPlayer, int x, int y, int *win) { // 清空鼠標(biāo)消息緩沖區(qū) flushmessage(EM_MOUSE); // 攻擊的起始位置 int startx = x + (idPlayer == 1 ? g_imgGorD.getwidth() : 0); int starty = y; // 角度輔助線的位置 int mx = startx, my = starty - 90; int oldmx = mx, oldmy = my; double angle = PI / 2; // 投擲角度 int velocity = 2; // 投擲力度 setrop2(R2_XORPEN); setlinecolor(RED); line(startx, starty, mx, my); // 鼠標(biāo)輸入攻擊角度 ExMessage msg; while(true) { msg = getmessage(EM_MOUSE); if (msg.message == WM_MOUSEMOVE) { if (msg.y > y) { mx = startx + (msg.x > startx ? 90 : -90); my = starty; angle = msg.x > startx ? 0 : PI; } else if (msg.x != startx) { angle = atan((double(starty) - msg.y) / (double(msg.x) - startx)); if (angle < 0) angle += PI; mx = startx + int(cos(angle) * 90 + 0.5); my = starty - int(sin(angle) * 90 + 0.5); } else { mx = msg.x; my = y - 90; angle = PI / 2; } line(startx, starty, oldmx, oldmy); line(startx, starty, mx, my); oldmx = mx; oldmy = my; } else if (msg.message == WM_LBUTTONDOWN) break; } line(startx, starty, oldmx, oldmy); // 鼠標(biāo)輸入攻擊力度 setlinestyle(PS_SOLID, 8); oldmx = mx = startx + int(cos(angle) * velocity + 0.5); oldmy = my = starty - int(sin(angle) * velocity + 0.5); line(startx, starty, mx, my); while(true) { if (peekmessage(&msg)) { if (msg.message == WM_LBUTTONUP) break; } mx = startx + int(cos(angle) * velocity + 0.5); my = starty - int(sin(angle) * velocity + 0.5); line(startx, starty, oldmx, oldmy); line(startx, starty, mx, my); oldmx = mx; oldmy = my; if (++velocity > 90) velocity = 2; Sleep(20); } velocity *= 2; // 力度擴(kuò)大一倍 line(startx, starty, oldmx, oldmy); // 恢復(fù)設(shè)置 setlinestyle(PS_SOLID, 1); setrop2(R2_COPYPEN); // 實(shí)施攻擊 g_bSunHit = false; int iPlayerHit = PlotShot(x, y, angle, velocity, idPlayer); // 攻擊結(jié)果 if (iPlayerHit == -1) { *win = -1; return false; } else { *win = (iPlayerHit == idPlayer) ? 1 - idPlayer : idPlayer; VictoryDance(*win); return true; } }
8.扔出香蕉,計(jì)算坐標(biāo),弧度等等
// 進(jìn)行香蕉攻擊,使香蕉劃過屏幕 // 參數(shù): // startX, startY:游戲者(扔香蕉的)的坐標(biāo) // angle:扔出的方向(弧度) // velocity:扔出的力度 // idPlayer:游戲者(扔香蕉的) int PlotShot(int startX, int startY, double angle, int velocity, int idPlayer) { // 投擲力量在 x、y 方向上的分量 double initXVel = cos(angle) * velocity; double initYVel = sin(angle) * velocity; double x, y; double oldx = startX; double oldy = startY; // 繪制游戲者(投擲動(dòng)作) putimage(startX, startY, idPlayer == 0 ? &g_imgGorL : &g_imgGorR); Sleep(100); // 繪制游戲者(站立動(dòng)作) putimage(startX, startY, &g_imgGorD); bool bImpact = false; // 是否碰撞 bool bShotInSun = false; // 是否擊中太陽 bool bOnScreen = true; // 香蕉是否在屏幕上 int iPlayerHit = -1; // 是否擊中對(duì)手(-1:未擊中;0、1:被擊中者的 ID) bool bNeedErase = false; // 是否需要擦掉舊香蕉 POINT look[4]; // 碰撞檢測(cè)的位置(香蕉中心上下左右四個(gè)邊的中點(diǎn)) look[2].x = 0; look[3].x = g_imgBanana[0].getwidth() - 1; look[0].x = look[1].x = look[3].x / 2; look[0].y = 0; look[1].y = g_imgBanana[0].getheight() - 1; look[2].y = look[3].y = look[1].y / 2; int startXPos = startX; int startYPos = startY - g_imgBanana[0].getheight(); if (idPlayer == 1) startXPos = startXPos + g_imgGorD.getwidth() - g_imgBanana[0].getwidth(); int pointColor = 0; int rot; double t = 0; while(!bImpact && bOnScreen) { // 擦掉舊香蕉 if (bNeedErase) { bNeedErase = false; DrawBanana(int(oldx + 0.5), int(oldy + 0.5), -1, false); } x = startXPos + (initXVel * t) + (g_iWind / 5.0 * t * t); y = startYPos + (-1 * (initYVel * t) + (GRAVITY * t * t)); if ((x >= SCRWIDTH - 10.0) || (x <= 3) || (y >= SCRHEIGHT - 3.0)) bOnScreen = false; if (bOnScreen && y > 0) { // 檢測(cè)是否擊中(對(duì)香蕉中心上下左右四個(gè)邊的中點(diǎn)做檢測(cè)) for (int i = 0; i < 4; i ++) { pointColor = getpixel(int(x + look[i].x + 0.5), int(y + look[i].y + 0.5)); if (pointColor == BACKATTR || pointColor == WHITE) // 目標(biāo)是背景色或白色字幕,未擊中 { bImpact = false; if (bShotInSun == true && (abs(SCRWIDTH / 2 - int(x)) > 20 || y > SUNHEIGHT)) bShotInSun = false; } else if (pointColor == SUNATTR && y < SUNHEIGHT) // 擊中太陽 { if (!g_bSunHit) DoSun(SUNSHOCK); g_bSunHit = true; bShotInSun = true; } else bImpact = true; if (bImpact) break; } if (!bShotInSun && !bImpact) { // 繪制香蕉 rot = int(t * 10) % 4; DrawBanana(int(x + 0.5), int(y + 0.5), rot, true); bNeedErase = true; } oldx = x; oldy = y; } t += 0.1; Sleep(50); } if (pointColor != OBJECTCOLOR && bImpact) DoExplosion(int(x + g_imgBanana[0].getwidth() / 2 + 0.5), int(y + g_imgBanana[0].getheight() / 2 + 0.5)); else if (pointColor == OBJECTCOLOR) iPlayerHit = ExplodeGorilla(int(x + 0.5), int(y + 0.5)); return iPlayerHit; }
9.當(dāng)然還是不能忘記不斷對(duì)香蕉的位置進(jìn)行刷新
// 參數(shù): // x, y:香蕉的位置 // r:香蕉的旋轉(zhuǎn)位置 // d:繪制還是擦除(true:繪制;false:擦除) void DrawBanana(int x, int y, int r, bool d) { static IMAGE oldimg; if (d) { getimage(&oldimg, x, y, g_imgBanana[0].getwidth(), g_imgBanana[0].getheight()); putimage(x, y, &g_imgBanana[r]); } else putimage(x, y, &oldimg); }
10.實(shí)現(xiàn)香蕉命中后的爆炸效果以及角色死亡效果
// 香蕉攻擊后的爆炸效果 // 參數(shù): // x, y:爆炸的位置 void DoExplosion(int x, int y) { int r = 10; int i; setlinecolor(EXPLOSIONCOLOR); for (i = 0; i <= r; i++) { circle(x, y, i); Sleep(16); } setlinecolor(BACKATTR); for (i = r; i >= 0; i--) { circle(x, y, i); Sleep(16); } setfillcolor(BACKATTR); fillcircle(x, y, r); } // 游戲者死亡后爆炸 // 參數(shù): // x, y:攻擊的位置 int ExplodeGorilla (int x, int y) { int iPlayerHit = (x < SCRWIDTH / 2) ? 0 : 1; int iPlayerX = g_ptGorilla[iPlayerHit].x + g_imgGorD.getwidth() / 2; int iPlayerY = g_ptGorilla[iPlayerHit].y + g_imgGorD.getheight() / 2; int i; setlinecolor(EXPLOSIONCOLOR); for (i = 1; i <= 10; i++) { circle(x, y, i); Sleep(10); } for (i = 1; i <= 16; i++) { circle(iPlayerX, iPlayerY + 11, i); Sleep(10); } for (i = 1; i <= 32; i++) { setlinecolor((i % 2 == 0) ? 0x54A8FC : 0x5400FC); circle(iPlayerX, iPlayerY, i); Sleep(10); } for (i = 48; i >= 1; i--) { setlinecolor(BACKATTR); circle(iPlayerX, iPlayerY, i); Sleep(10); } fillcircle(iPlayerX, iPlayerY, 48); return iPlayerHit; }
11.最后還可以添加一下死亡之后的游戲動(dòng)畫(比如跳舞慶祝勝利者)
// 繪制跳舞的大猩猩(勝利后執(zhí)行) // 參數(shù): // idPlayer:游戲者編號(hào) void VictoryDance(int idPlayer) { for (int i = 0; i < 4; i++) { putimage(g_ptGorilla[idPlayer].x, g_ptGorilla[idPlayer].y, &g_imgGorL); Sleep(200); putimage(g_ptGorilla[idPlayer].x, g_ptGorilla[idPlayer].y, &g_imgGorR); Sleep(200); } }
12.主函數(shù)(把所有的功能函數(shù)放這里來)
void main() { Init(); Intro(); PlayGame(_T("Player 1"), _T("Player 2")); }
大家趕緊去動(dòng)手試試吧!
審核編輯:湯梓紅
-
游戲
+關(guān)注
關(guān)注
2文章
750瀏覽量
26364 -
C語言
+關(guān)注
關(guān)注
180文章
7614瀏覽量
137726 -
編程
+關(guān)注
關(guān)注
88文章
3637瀏覽量
93988 -
源碼
+關(guān)注
關(guān)注
8文章
652瀏覽量
29458
原文標(biāo)題:C語言項(xiàng)目:扔香蕉的大猩猩(自制游戲)!詳細(xì)思路+源碼分享
文章出處:【微信號(hào):cyuyanxuexi,微信公眾號(hào):C語言編程學(xué)習(xí)基地】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論