這是經(jīng)典的下 100 層游戲。>_<
通過鼠標(biāo)左右鍵或者鍵盤 A、D 按鍵及左右方向鍵控制小球左右移動,木板會不斷上升,小球到底認定游戲結(jié)束,按下鍵盤后小球會加速到一個最大速度,小球在木板上則會跟著木板上移,達到一定分數(shù)啟動反向操作。
本程序采用單例設(shè)計模式,RollingBall 公有繼承 BallAndPlank,protected 便于派生類訪問基類數(shù)據(jù)成員。
木板的顏色隨機,位置隨機。
界面由初始化界面大小控制,可自行更改。
簡單了解游戲后我們就來試試吧?。ㄖ苯由显创a,大家可以看注釋)
代碼展示:
/*
項目名稱:
RollingBall
作者:
tzdhu.z@qq.com
項目介紹:
本程序由單例設(shè)計模式 RollingBall 公有繼承 BallAndPlank
protected 便于派生類訪問基類數(shù)據(jù)成員
模板顏色隨機,位置隨機
界面由初始化界面大小控制,初學(xué)者可自行更改
版權(quán)聲明:
本程序完全由作者所創(chuàng),不涉及任何侵權(quán)行為,僅用于學(xué)習(xí)
*/
// 頭文件
#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; // 反向操作啟動分數(shù)
static IMAGE img_start(80, 40), id_start(80, 40), img_exit(80, 40), id_exit(80, 40); // 用于按鈕效果的 IMAGE
////////////////////////////////////////////////////
// 小球和木板類
class BallAndPlank
{
//小球結(jié)構(gòu)體
typedef struct BALL
{
int ball_x; // 小球球心位置 x
int ball_y; // 小球球心位置 y
}Ball;
//木板結(jié)構(gòu)體
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:
// 構(gòu)造函數(shù)初始化參數(shù)
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: // 保護用于派生類訪問數(shù)據(jù)成員
// 小球?qū)傩? 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; // 小球結(jié)構(gòu)對象
// 木板屬性
enum Plank_Num { plank_num = 7 }; // 枚舉初始化木板數(shù)量
int plank_dy; // 可視為木板速度
int plank_len_min; // 木板最小長度
int plank_len_max; // 木板最大長度
int plank_gap; // 木板間隔
int thorn_len; // 尖刺長度
int thorn_h; // 尖刺高度
Plank plank[plank_num]; // 木板結(jié)構(gòu)對象數(shù)組
};
////////////////////////////////////////////////////
// 單例設(shè)計模式 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,鼠標(biāo)左右鍵"))) / 2, max_y / 5 * 2 + 60, _T("控制方向:左右方向鍵,AD,鼠標(biāo)左右鍵"));
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);
// 檢測是否點擊相關(guān)按鈕及按鍵
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;
}
// 打印分數(shù),速度及是否開啟反向操作
void PrintScore()
{
settextcolor(RED);
settextstyle(16, 0, _T("黑體"));
score = time_num / 5;
TCHAR str[20];
_stprintf_s(str, _T("當(dāng)前得分: %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āng)前速度: %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結(jié)束時顯示最終分數(shù)
void Finish()
{
if (is_dead)
{
TCHAR str[50];
_stprintf_s(str, _T(" 您的最終得分為: %d "), score);
MessageBox(GetHWnd(), str, _T("游戲結(jié)束"), 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++;
// 打印分數(shù),速度及是否開啟反向操作
PrintScore();
Sleep(sleep_time);
}
private:
// 構(gòu)造函數(shù)初始化參數(shù)
RollingBall()
{
score = 0;
die_top = 5;
time_num = 0;
sleep_time = 20;
is_dead = false;
}
RollingBall(const RollingBall &rb) {} // 禁止拷貝構(gòu)造
RollingBall &operator = (const RollingBall &rb) {} // 禁止賦值重載
int sleep_time; // 游戲刷新時間間隔
int time_num; // 記錄游戲刷新次數(shù)
int die_top; // 頂部尖刺位置
int score; // 記錄分數(shù)
bool is_dead; // 是否死亡
};
////////////////////////////////////////////////////
// main 主函數(shù)
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;
}
大家趕緊去動手試試吧!
審核編輯:湯梓紅
-
游戲
+關(guān)注
關(guān)注
2文章
787瀏覽量
27237 -
C語言
+關(guān)注
關(guān)注
183文章
7642瀏覽量
144519 -
編程
+關(guān)注
關(guān)注
90文章
3707瀏覽量
96709 -
C++
+關(guān)注
關(guān)注
22文章
2121瀏覽量
76684 -
代碼
+關(guān)注
關(guān)注
30文章
4940瀏覽量
73069
原文標(biāo)題:C/C++項目實戰(zhàn):《是男人就下一百層》,530 行源碼分享來啦!
文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學(xué)習(xí)基地】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
C++中的結(jié)構(gòu)和類
現(xiàn)代C++項目的最佳實踐
編程實戰(zhàn)!C++快遞入門與進階!
如何用Bazel構(gòu)建C++項目
C++中mutable關(guān)鍵字詳解與實戰(zhàn)
嵌入式項目實戰(zhàn)經(jīng)驗
Linux C/C++ 學(xué)習(xí)路線
STM32實戰(zhàn)三 C++ IO.cpp
C++項目常見的命名規(guī)范
C/C++項目實戰(zhàn):2D射擊游戲開發(fā)(簡易版)
C++之父新作帶你勾勒現(xiàn)代C++地圖
C++簡史:C++是如何開始的

C/C++項目實戰(zhàn)分享
評論