這是經典的下 100 層游戲。>_<
通過鼠標左右鍵或者鍵盤 A、D 按鍵及左右方向鍵控制小球左右移動,木板會不斷上升,小球到底認定游戲結束,按下鍵盤后小球會加速到一個最大速度,小球在木板上則會跟著木板上移,達到一定分數啟動反向操作。
本程序采用單例設計模式,RollingBall 公有繼承 BallAndPlank,protected 便于派生類訪問基類數據成員。
木板的顏色隨機,位置隨機。
界面由初始化界面大小控制,可自行更改。
簡單了解游戲后我們就來試試吧!(直接上源碼,大家可以看注釋)
代碼展示:
/* 項目名稱: RollingBall 作者: tzdhu.z@qq.com 項目介紹: 本程序由單例設計模式 RollingBall 公有繼承 BallAndPlank protected 便于派生類訪問基類數據成員 模板顏色隨機,位置隨機 界面由初始化界面大小控制,初學者可自行更改 版權聲明: 本程序完全由作者所創,不涉及任何侵權行為,僅用于學習 */ // 頭文件 #include#include #include // 全局變量 const COLORREF BKCOLOR = BLACK; // 繪圖窗口背景顏色 const int max_x = 640; // 繪圖窗口像素寬度 const int max_y = 480; // 繪圖窗口像素高度 const int max_smx = GetSystemMetrics(SM_CXSCREEN); // 電腦屏幕像素寬度 const int max_smy = GetSystemMetrics(SM_CYSCREEN); // 電腦屏幕像素高度 const int inverse_operate_score = 600; // 反向操作啟動分數 static IMAGE img_start(80, 40), id_start(80, 40), img_exit(80, 40), id_exit(80, 40); // 用于按鈕效果的 IMAGE //////////////////////////////////////////////////// // 小球和木板類 class BallAndPlank { //小球結構體 typedef struct BALL { int ball_x; // 小球球心位置 x int ball_y; // 小球球心位置 y }Ball; //木板結構體 typedef struct PLANK { int plank_x; // 木板左端位置 x int plank_y; // 木板位置 y int plank_len; // 木板長度 COLORREF plank_color; // 木板顏色 int thorn_x; // 尖刺左端位置 x bool is_thorn; // 木板是否有尖刺 }Plank; public: // 構造函數初始化參數 BallAndPlank() { // 小球 ball_r = 4; ball_ddx = 1; ball_dx_min = 0; ball_dx_max = 8; left_right = STOP; ball_dx = ball_dx_min; ball_dy = ball_dx_min; ball_color = RGB(255, 0, 0); // 木板 plank_dy = 1; plank_len_min = 50; plank_len_max = 150; thorn_len = 32; thorn_h = 4; plank_gap = (max_y - 1) / plank_num; } ~BallAndPlank() { // 未定義 } // 小球顏色繪制小球 void DrawBall(int x, int y) { setfillcolor(ball_color); solidcircle(x, y, ball_r); } // 背景顏色清除小球 void CleanBall(int x, int y) { setfillcolor(BKCOLOR); solidcircle(x, y, ball_r); } bool IsThorn() { return (rand() % 1000 > 600) ? true : false; } // 木板顏色繪制木板 void DrawPlank() { for (int i = 0; i < plank_num; i++) { setlinecolor(plank[i].plank_color); line(plank[i].plank_x, plank[i].plank_y, plank[i].plank_x + plank[i].plank_len, plank[i].plank_y); if (plank[i].is_thorn == true) { for (int j = plank[i].thorn_x; j < plank[i].thorn_x + thorn_len; j += 2 * thorn_h) { line(j, plank[i].plank_y, j + thorn_h, plank[i].plank_y - thorn_h - 1); line(j + thorn_h, plank[i].plank_y - thorn_h - 1, j + 2 * thorn_h, plank[i].plank_y); } } } } // 背景顏色清除木板 void CleanPlank() { setlinecolor(BKCOLOR); for (int i = 0; i < plank_num; i++) { line(plank[i].plank_x, plank[i].plank_y, plank[i].plank_x + plank[i].plank_len, plank[i].plank_y); if (plank[i].is_thorn == true) { for (int j = plank[i].thorn_x; j < plank[i].thorn_x + thorn_len; j += 2 * thorn_h) { line(j, plank[i].plank_y, j + thorn_h, plank[i].plank_y - thorn_h - 1); line(j + thorn_h, plank[i].plank_y - thorn_h - 1, j + 2 * thorn_h, plank[i].plank_y); } } } } protected: // 保護用于派生類訪問數據成員 // 小球屬性 enum Left_Right { STOP, LEFT, RIGHT }; // 枚舉小球左右方向 int ball_r; // 小球半徑 int ball_ddx; // 可視為小球加速度 int ball_dx_min; // 小球 x 方向最小步長 int ball_dx_max; // 小球 x 方向最大步長 int left_right; // 小球左右方向 int ball_dx; // 可視為小球 x 方向速度 int ball_dy; // 可視為小球 y 方向速度 COLORREF ball_color; // 小球顏色 Ball ball; // 小球結構對象 // 木板屬性 enum Plank_Num { plank_num = 7 }; // 枚舉初始化木板數量 int plank_dy; // 可視為木板速度 int plank_len_min; // 木板最小長度 int plank_len_max; // 木板最大長度 int plank_gap; // 木板間隔 int thorn_len; // 尖刺長度 int thorn_h; // 尖刺高度 Plank plank[plank_num]; // 木板結構對象數組 }; //////////////////////////////////////////////////// // 單例設計模式 RollingBall派生類 class RollingBall : public BallAndPlank { public: ~RollingBall() { // 未定義 } // 獲取單例指針 static RollingBall *GetInstance() { static RollingBall RB; return &RB; } // 開始前介紹界面 void Introduce() { setbkcolor(BKCOLOR); cleardevice(); settextcolor(LIGHTMAGENTA); settextstyle(50, 0, _T("黑體")); outtextxy((max_x - textwidth(_T("RollingBall"))) / 2, max_y / 5, _T("RollingBall")); settextcolor(GREEN); settextstyle(25, 0, _T("黑體")); outtextxy((max_x - textwidth(_T("ESC退出,空格暫停"))) / 2, max_y / 5 * 2 + 20, _T("ESC退出,空格暫停")); outtextxy((max_x - textwidth(_T("控制方向:左右方向鍵,AD,鼠標左右鍵"))) / 2, max_y / 5 * 2 + 60, _T("控制方向:左右方向鍵,AD,鼠標左右鍵")); SetWorkingImage(&img_start); setbkcolor(LIGHTGRAY); cleardevice(); settextcolor(BROWN); settextstyle(25, 0, _T("黑體")); outtextxy((80 - textwidth(_T("開始"))) / 2, (40 - textheight(_T("開始"))) / 2, _T("開始")); SetWorkingImage(&id_start); setbkcolor(DARKGRAY); cleardevice(); settextcolor(BROWN); settextstyle(25, 0, _T("黑體")); outtextxy((80 - textwidth(_T("開始"))) / 2, (40 - textheight(_T("開始"))) / 2, _T("開始")); SetWorkingImage(&img_exit); setbkcolor(LIGHTGRAY); cleardevice(); settextcolor(BROWN); settextstyle(25, 0, _T("黑體")); outtextxy((80 - textwidth(_T("退出"))) / 2, (40 - textheight(_T("退出"))) / 2, _T("退出")); SetWorkingImage(&id_exit); setbkcolor(DARKGRAY); cleardevice(); settextcolor(BROWN); settextstyle(25, 0, _T("黑體")); outtextxy((80 - textwidth(_T("退出"))) / 2, (40 - textheight(_T("退出"))) / 2, _T("退出")); SetWorkingImage(); int yy = max_y / 4 * 3; int exit_x = max_x / 2 - 200; int start_x = max_x / 2 + 120; putimage(start_x, yy, &img_start); putimage(exit_x, yy, &img_exit); // 檢測是否點擊相關按鈕及按鍵 MOUSEMSG msg; bool selected = false; while (!selected) { while (MouseHit()) { msg = GetMouseMsg(); if ((msg.x >= start_x && msg.x <= start_x + 80 && msg.y >= yy && msg.y <= yy + 40 && msg.uMsg == WM_LBUTTONDOWN) || GetAsyncKeyState(VK_RETURN) & 0x8000) { putimage(start_x, yy, &id_start); Sleep(200); putimage(start_x, yy, &img_start); Sleep(100); selected = true; break; } else if ((msg.x >= exit_x && msg.x <= exit_x + 80 && msg.y >= yy && msg.y <= yy + 40 && msg.uMsg == WM_LBUTTONDOWN) || GetAsyncKeyState(VK_ESCAPE) & 0x8000) { putimage(exit_x, yy, &id_exit); Sleep(200); putimage(exit_x, yy, &img_exit); Sleep(100); exit(0); } } Sleep(16); } } // 初始化游戲界面 void Initialize() { setbkcolor(BKCOLOR); cleardevice(); setlinecolor(DARKGRAY); line(0, 0, 0, max_y - 1); line(max_y, 0, max_y, max_y - 1); line(0, 0, max_y - 1, 0); line(0, max_y - 1, max_y - 1, max_y - 1); for (int i = 0; i < max_y; i += 2 * die_top) { line(i, 0, i + die_top, die_top); line(i + die_top, die_top, i + 2 * die_top, 0); } for (int i = 0; i < plank_num; i++) { plank[i].plank_y = (i + 1) * plank_gap; plank[i].plank_len = rand() % (plank_len_max - plank_len_min) + plank_len_min + 1; plank[i].plank_x = rand() % (max_y - plank[i].plank_len); plank[i].plank_color = HSVtoRGB(float(rand() % 360), float(1.0), float(1.0)); plank[i].is_thorn = IsThorn(); if (plank[i].is_thorn == true) plank[i].thorn_x = plank[i].plank_x + rand() % (plank[i].plank_len - thorn_len); } plank[3].is_thorn = false; ball.ball_x = plank[3].plank_x + plank[3].plank_len / 2; ball.ball_y = plank[3].plank_y - 1 - ball_r; DrawBall(ball.ball_x, ball.ball_y); DrawPlank(); Sleep(sleep_time); } // 檢測是否死亡 bool IsDead() { if (ball.ball_y <= die_top + ball_r || ball.ball_y >= max_y - 1 - ball_r || is_dead) { is_dead = true; return true; } else return false; } // 打印分數,速度及是否開啟反向操作 void PrintScore() { settextcolor(RED); settextstyle(16, 0, _T("黑體")); score = time_num / 5; TCHAR str[20]; _stprintf_s(str, _T("當前得分: %d"), score); outtextxy(max_y + 5, max_y / 6 * 5 + 10, str); if (score < 50) plank_dy = 1; else if (score < 200) plank_dy = 2; else if (score < 500) plank_dy = 3; else if (score < 1000) plank_dy = 4; else if (score < 1500) plank_dy = 5; else plank_dy = 6; _stprintf_s(str, _T("當前速度: %d/6"), plank_dy); outtextxy(max_y + 5, max_y / 2 - 10, str); if (score > inverse_operate_score) outtextxy(max_y + 5, max_y / 11, _T("反向操作 已開啟")); else outtextxy(max_y + 5, max_y / 11, _T("反向操作 未開啟")); } // 非ESC結束時顯示最終分數 void Finish() { if (is_dead) { TCHAR str[50]; _stprintf_s(str, _T(" 您的最終得分為: %d "), score); MessageBox(GetHWnd(), str, _T("游戲結束"), MB_OK); } } // 游戲運行處理 void GameRunning() { // 清除小球和木板 CleanBall(ball.ball_x, ball.ball_y); CleanPlank(); // 計算木板位置 for (int i = 0; i < plank_num; i++) plank[i].plank_y -= plank_dy; // 頂木板是否消失,是 則生成尾木板 if (plank[0].plank_y < die_top + ball_r + 1) { for (int i = 0; i < plank_num - 1; i++) plank[i] = plank[i + 1]; plank[plank_num - 1].plank_y = (plank_num)* plank_gap; plank[plank_num - 1].plank_len = rand() % (plank_len_max - plank_len_min) + plank_len_min + 1; plank[plank_num - 1].plank_x = rand() % (max_y - plank[plank_num - 1].plank_len); plank[plank_num - 1].plank_color = HSVtoRGB(float(rand() % 360), float(1.0), float(1.0)); plank[plank_num - 1].is_thorn = IsThorn(); if (plank[plank_num - 1].is_thorn == true) plank[plank_num - 1].thorn_x = plank[plank_num - 1].plank_x + rand() % (plank[plank_num - 1].plank_len - thorn_len); } // 計算小球球心 x 位置(加減速效果) if ((GetAsyncKeyState(VK_LEFT) & 0x8000) || (GetAsyncKeyState('A') & 0x8000) || (GetAsyncKeyState(VK_LBUTTON) & 0x8000)) { if (score < inverse_operate_score) { if (left_right == LEFT) ball_dx = (ball_dx += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dx; else { ball_dx = ball_dx_min; left_right = LEFT; } ball.ball_x = (ball.ball_x -= ball_dx) < ball_r ? ball_r : ball.ball_x; } else { if (left_right == RIGHT) ball_dx = (ball_dx += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dx; else { ball_dx = ball_dx_min; left_right = RIGHT; } ball.ball_x = (ball.ball_x += ball_dx) > (max_y - 1 - ball_r) ? max_y - 1 - ball_r : ball.ball_x; } } else if ((GetAsyncKeyState(VK_RIGHT) & 0x8000) || (GetAsyncKeyState('D') & 0x8000) || (GetAsyncKeyState(VK_RBUTTON) & 0x8000)) { if (score > inverse_operate_score) { if (left_right == LEFT) ball_dx = (ball_dx += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dx; else { ball_dx = ball_dx_min; left_right = LEFT; } ball.ball_x = (ball.ball_x -= ball_dx) < ball_r ? ball_r : ball.ball_x; } else { if (left_right == RIGHT) ball_dx = (ball_dx += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dx; else { ball_dx = ball_dx_min; left_right = RIGHT; } ball.ball_x = (ball.ball_x += ball_dx) > (max_y - 1 - ball_r) ? max_y - 1 - ball_r : ball.ball_x; } } else { ball_dx -= ball_ddx; if (ball_dx > ball_dx_min) { if (left_right == LEFT) ball.ball_x = (ball.ball_x -= ball_dx) < ball_r ? ball_r : ball.ball_x; else if (left_right == RIGHT) ball.ball_x = (ball.ball_x += ball_dx) > (max_y - 1 - ball_r) ? max_y - 1 - ball_r : ball.ball_x; } else { ball_dx = ball_dx_min; left_right = STOP; } } // 計算小球球心 y 位置(加速效果) int ii = 0; // 用于確定小球位于哪塊木板上方 while (ball.ball_y - plank_dy > plank[ii].plank_y - 1 - ball_r) ii++; if (ii < plank_num && ball.ball_x >= plank[ii].plank_x && ball.ball_x <= plank[ii].plank_x + plank[ii].plank_len && (ball.ball_y - plank_dy == plank[ii].plank_y - 1 - ball_r || ball.ball_y >= plank[ii].plank_y - 1 - ball_r)) { ball.ball_y = plank[ii].plank_y - 1 - ball_r; ball_dy = ball_dx_min; } else { ball_dy = (ball_dy += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dy; ball.ball_y += ball_dy; if (ii < plank_num && ball.ball_x >= plank[ii].plank_x && ball.ball_x <= plank[ii].plank_x + plank[ii].plank_len && ball.ball_y >= plank[ii].plank_y - 1 - ball_r) { ball.ball_y = plank[ii].plank_y - 1 - ball_r; ball_dy = ball_dx_min; } else if (ball.ball_y > max_y - 1 - ball_r) ball.ball_y = max_y - 1 - ball_r; } // 判斷小球是否觸碰尖刺 if (ball.ball_x >= plank[ii].thorn_x - ball_r / 2 && ball.ball_x <= plank[ii].thorn_x + thorn_len + ball_r / 2 && ball.ball_y == plank[ii].plank_y - 1 - ball_r && plank[ii].is_thorn) is_dead = true; // 繪制木板和小球 DrawPlank(); DrawBall(ball.ball_x, ball.ball_y); FlushBatchDraw(); time_num++; // 打印分數,速度及是否開啟反向操作 PrintScore(); Sleep(sleep_time); } private: // 構造函數初始化參數 RollingBall() { score = 0; die_top = 5; time_num = 0; sleep_time = 20; is_dead = false; } RollingBall(const RollingBall &rb) {} // 禁止拷貝構造 RollingBall &operator = (const RollingBall &rb) {} // 禁止賦值重載 int sleep_time; // 游戲刷新時間間隔 int time_num; // 記錄游戲刷新次數 int die_top; // 頂部尖刺位置 int score; // 記錄分數 bool is_dead; // 是否死亡 }; //////////////////////////////////////////////////// // main 主函數 int main() { initgraph(max_x, max_y, NOMINIMIZE); srand((unsigned)time(NULL)); // 獲取單例指針 RollingBall *rb = RollingBall::GetInstance(); rb->Introduce(); rb->Initialize(); BeginBatchDraw(); while (!(GetAsyncKeyState(VK_ESCAPE) & 0x8000)) { rb->GameRunning(); if (rb->IsDead()) break; if (_kbhit() && _getwch() == ' ') _getwch(); } EndBatchDraw(); rb->Finish(); closegraph(); return 0; }
大家趕緊去動手試試吧!
審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
游戲
+關注
關注
2文章
742瀏覽量
26313 -
C語言
+關注
關注
180文章
7604瀏覽量
136713 -
編程
+關注
關注
88文章
3614瀏覽量
93698 -
C++
+關注
關注
22文章
2108瀏覽量
73627 -
代碼
+關注
關注
30文章
4780瀏覽量
68539
原文標題:C/C++項目實戰:《是男人就下一百層》,530 行源碼分享來啦!
文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學習基地】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
C++中的結構和類
C++ 仍然是嵌入式開發的少數??語言,但當項目變得太大而無法有效使用 C 時,開發人員通常會采用 C++。這些開發人員通常從 C 過渡到
發表于 07-18 17:37
?806次閱讀
現代C++項目的最佳實踐
本系列是開源書C++ Best Practises[1]的中文版,全書從工具、代碼風格、安全性、可維護性、可移植性、多線程、性能、正確性等角度全面介紹了現代C++項目的最佳實踐。本文是該系列的第三篇。
發表于 09-29 11:32
?1139次閱讀
編程實戰!C++快遞入門與進階!
monitor”開發者在線講解各種C++開發實戰以及分享經歷!1.C與C++的基本語法的區別2.面向過程向面向對象的轉變3.面向對象中各個知識點的關系4.串口軟件在自動化監控中的重要
發表于 08-09 16:21
C++開發實戰PDF電子書免費下載
《C++開發實戰》從初學者的角度全面介紹了使用C++進行程序開發的各種技術。在內容安排上由淺入深,讓讀者循序漸進地掌握編程技術;在內容講解上結合豐富的圖解和形象的比喻,幫助讀者理解晦澀難懂的技術
發表于 12-23 08:00
?14次下載
C++中mutable關鍵字詳解與實戰
mutable關鍵字詳解與實戰 在C++中mutable關鍵字是為了突破const關鍵字的限制,被mutable關鍵字修飾的成員變量永遠處于可變的狀態,即使是在被const修飾的成員函數中。 在
Linux C/C++ 學習路線
一、秋招 Linux C/C++ offer 情況二、Linux C/C++ 方向的一些思考三、計算機基礎知識的梳理四、C++ 方向的深入學
發表于 11-06 19:36
?14次下載
STM32實戰三 C++ IO.cpp
這一章開始編寫代碼,主要是兩個方面,一是C++,二是進行簡單的IO封裝。其它教程一般是用C語言,從按鍵或LED燈開始,比較直觀,容易上手,但與實際應用有一定的區別,這里要做的是實用控制程序,開始
發表于 01-12 17:40
?4次下載
C++項目常見的命名規范
本系列是開源書C++ Best Practises[1]的中文版,全書從工具、代碼風格、安全性、可維護性、可移植性、多線程、性能、正確性等角度全面介紹了現代C++項目的最佳實踐。本文是該系列的第二篇。
C/C++項目實戰:2D射擊游戲開發(簡易版)
關于無阻塞延時,首先,先要 ctime 創建一個 clock_t 變量 a,初始化為 clock(),貌似是自從 1970 年到現在的毫秒數。我們要每隔 0.5 秒執行函數 func() 一次。
C++之父新作帶你勾勒現代C++地圖
為了幫助大家解決這些痛點問題,讓大家領略現代C++之美,掌握其中的精髓,更好地使用C++,C++之父Bjarne Stroustrup坐不住了,他親自操刀寫就了這本《C++之旅》!
C++簡史:C++是如何開始的
MISRA C++:2023,MISRA? C++ 標準的下一個版本,來了!為了幫助您做好準備,我們介紹了 Perforce 首席技術支持工程師 Frank van den Beuken 博士撰寫
評論