《Flappy Bird》是曾經一款流行的一款手機游戲,你只要讓小鳥保持飛行,不要碰到綠色的管道就可以啦。操作雖然簡單,但是非常具有挑戰!本次我們也是自己動手來實現這樣一款游戲的高仿版,大家不妨自己先讀一遍代碼然后動手試試!
該程序是用 C 語言實現的 FlappyBird 的電腦版,玩法和手機版的相同。
程序通過 alpha 域實現透明貼圖,并且通過雙緩沖繪圖防止刷新閃屏。
程序執行效果如下:
本程序設計了三種不同顏色的小鳥(可以實現三人對戰)另外也有白天模式和夜晚模式,不同模式和不同小鳥是在上次死亡后隨機刷新,避免產生視覺疲勞。
簡單了解游戲后我們就來試試吧!
本項目編譯環境:Visual Studio 2019/2022,EasyX插件
必備素材:
網盤鏈接:https://pan.baidu.com/s/10ALP-QdcTu0UV4p8alTE4w?pwd=3p3s 提取碼:3p3s
代碼展示:(有五百多行代碼,每一個功能實現都會給出對應注釋)
#include
#include
#include
#include
#include
#include
// 初始背景圖 白天、黑夜
IMAGE BK[2];
// 全局畫板
IMAGE bk;
// 飛鳥資源 橘、藍、紅
IMAGE BIRD[3][3];
// 管道資源 綠、紅
IMAGE PIPE[2][2];
// 三種數字字體
IMAGE NUMBER[3][10];
// 獎牌資源
IMAGE MEDAL[4];
// 地面資源
IMAGE LAND;
// 開始游戲按鈕
IMAGE PLAY;
// 得分面板
IMAGE PANEL;
// 以下為五種游戲文字
// 游戲結束 游戲預備 游戲標題 游戲提示 最高分
IMAGE OVER;
IMAGE READY;
IMAGE TITLE;
IMAGE TUTORIAL;
IMAGE NEW;
// 結束時狀態
IMAGE OVEIMG;
// 是否為白天
int isDay = 0;
// 鳥的顏色
int birdColor = 0;
// 最高分
int best = 0;
// 游戲初始時間
long startTime = 0;
// 游戲基本屬性
// 重力加速度
const double g = 6.5;
// 管道的移動速度
double vx = 11;
// 鳥的下落速度
double vy = 10;
// 鳥的當前位置
double y = 220;
const double x = 288 / 2 - (41 + 8) / 2 - 80;
// 管道的橫向坐標
double pipeX[2];
// 管道的縱向坐標
int pipeY[2];
// 當前得分
int score;
// 鳥的矩形判斷區域
const int top = 12;
const int left = 10;
const int right = 37;
const int buttom = 33;
// 獎牌顯示位置
const int medalXY[2] = { 0, 0 };
// 陸地坐標
const double landY = 420;
double landX = -20;
// 飛鳥姿態
int pose = 0;
// 飛行間隔
const int diff = 110;
// 管道上補償
const int pipeUp = -280;
// 管道下補償
const int pipeDown = 140;
// 游戲預處理
void start();
// 游戲函數
void game();
// 結束游戲函數
void end();
// 移動函數
void move(long time);
// 繪制函數
void draw();
// 重置畫布
void reset();
// 繪制畫布
void put();
// 加載資源
void loadRes();
// 根據透明度繪圖
void drawAlpha(IMAGE* dstimg, int x, int y, IMAGE* srcimg);
// 繪制分數
void drawScore(int y, int sc, int dif, int se, int st, int type);
// 繪制管道
void drawPipe();
// 判斷鳥死亡
bool isDie();
// 初始化游戲資源
void init();
int main()
{
// 游戲開局頁面
init();
while (true)
{
// 游戲初始化
start();
// 游戲進行
game();
// 顯示得分
end();
}
return 0;
}
bool GetControl()
{
bool res = false;
if (_kbhit())
{
char ch = _getch();
if (ch == ' ')
{
res = true;
}
}
MOUSEMSG msg;
while (MouseHit())
{
msg = GetMouseMsg();
if (msg.mkLButton)
{
res = true;
}
}
return res;
}
// 游戲預處理
void start()
{
// 初始化數據
isDay = rand() % 2;
birdColor = rand() % 3;
pipeX[0] = 288 + 30;
pipeX[1] = 288 + 30 + 190;
pipeY[0] = rand() % 250;
pipeY[1] = rand() % 250;
pose = 0;
landX = 0;
score = 0;
y = 220;
vy = 0;
// 游戲初始時間
clock_t time = clock();
// 開場動畫
clock_t t = clock();
while (true)
{
reset();
drawScore(60, score, 13, 26, 144, 0);
drawAlpha(&bk, 50, 120, &READY);
drawAlpha(&bk, (int)x, (int)y, &BIRD[birdColor][pose]);
drawAlpha(&bk, 90, 220, &TUTORIAL);
drawAlpha(&bk, (int)landX, (int)landY, &LAND);
landX -= (clock() - t) * vx / 100;
t = clock();
pose = ((clock() - time) / diff) % 3;
put();
if (landX < -44)
{
landX = -20;
}
if (GetControl())
{
break;
}
Sleep(10);
}
}
// 游戲函數
void game()
{
// 根據毫秒
startTime = clock();
long time = clock();
while (!isDie())
{
// 移動
move(clock() - time);
time = clock();
// 控制
if (GetControl())
{
vy = -26;
}
// 繪制
draw();
Sleep(10);
}
startTime = clock();
while (clock() - startTime < 1000);
vy = -30;
time = clock();
while (y < 520)
{
y += (clock() - time) * vy / 100;
vy += g * (clock() - time) / 100;
time = clock();
draw();
Sleep(10);
}
// 備份此時圖片
drawAlpha(&OVEIMG, 0, 0, &bk);
}
void flush()
{
while (MouseHit())
GetMouseMsg();
while (_kbhit())
_getch();
}
// 結束函數
void end()
{
// 將面板移出
startTime = clock();
// 重置畫板
reset();
// 面板從下方移出
drawPipe();
drawAlpha(&bk, (int)landX, (int)landY, &LAND);
drawAlpha(&bk, 0, 0, &OVEIMG);
drawAlpha(&bk, 25, 150, &PANEL);
// 根據評分打印獎牌
int r = (score - 50) / 50;
if (score > 50)
{
if (r > 3)
{
r = 3;
}
drawAlpha(&bk, 57, 195, &MEDAL[r]);
}
// 打印當前分
drawScore(189, score, 16, 16, 240, 2);
// 寫最高分
int tmp = best;
if (best < score)
{
best = score;
}
// 打印最高分
drawScore(231, best, 16, 16, 240, 2);
// 如果當前分超過最高分 則顯示新分數圖標
if (tmp < score)
{
drawAlpha(&bk, 165, 210, &NEW);
}
flush();
put();
while (true)
{
if (GetControl())
{
break;
}
Sleep(10);
}
}
void drawScore(int y, int sc, int dif, int se, int st, int type)
{
// 將分數居中顯示
int t = sc;
int num[8];
int length = 0;
do
{
num[length] = t % 10;
length++;
t /= 10;
} while (t != 0);
// 計算出起始坐標
int s = st - dif * length;
for (int i = length - 1; i >= 0; i--)
{
drawAlpha(&bk, s, y, &NUMBER[type][num[i]]);
s += se;
}
}
// 管道、主角、地面移動
void move(long time)
{
y += time * vy / 100;
vy += g * time / 100;
pipeX[0] -= time * vx / 100;
pipeX[1] -= time * vx / 100;
landX -= time * vx / 100;
pose = ((clock() - startTime) / diff) % 3;
if (landX < -44)
{
landX = -20;
}
if (pipeX[0] < -52)
{
pipeX[0] = pipeX[1] + 190;
pipeY[0] = rand() % 250;
}
if (pipeX[1] < -52)
{
pipeX[1] = pipeX[0] + 190;
pipeY[1] = rand() % 250;
}
if (y < 0 - top)
{
y = -top;
}
score = (int)(((clock() - startTime) * vx / 100 - (288 - x + 30)) / 190 + 1);
if (score < 0)
{
score = 0;
}
}
void draw()
{
// 初始化背景圖
drawAlpha(&bk, 0, 0, &BK[isDay]);
// 畫管道
drawPipe();
// 畫陸地
drawAlpha(&bk, (int)landX, (int)landY, &LAND);
// 畫鳥
drawAlpha(&bk, (int)x, (int)y, &BIRD[birdColor][pose]);
// 畫分數
drawScore(60, score, 13, 26, 144, 0);
// 將背景畫到窗口上
put();
}
// 根據透明度繪圖
void drawAlpha(IMAGE* dstimg, int x, int y, IMAGE* srcimg)
{
if (dstimg == NULL)
{
return;
}
// 變量初始化
DWORD *dst = GetImageBuffer(dstimg);
DWORD *src = GetImageBuffer(srcimg);
int src_width = srcimg->getwidth();
int src_height = srcimg->getheight();
int dst_width = dstimg->getwidth();
int dst_height = dstimg->getheight();
// 實現透明貼圖 可優化
for (int iy = 0; iy < src_height; iy++)
{
for (int ix = 0; ix < src_width; ix++)
{
int srcX = ix + iy * src_width;
int sa = ((src[srcX] & 0xff000000) >> 24);
int sr = ((src[srcX] & 0xff0000) >> 16);
int sg = ((src[srcX] & 0xff00) >> 8);
int sb = src[srcX] & 0xff;
if (x + ix >= 0 && x + ix < dst_width
&& y + iy >= 0 && y + iy < dst_height)
{
int dstX = (x + ix) + (y + iy) * dst_width;
int dr = ((dst[dstX] & 0xff0000) >> 16);
int dg = ((dst[dstX] & 0xff00) >> 8);
int db = dst[dstX] & 0xff;
dst[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16)
| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8)
| (sb * sa / 255 + db * (255 - sa) / 255);
}
}
}
}
void drawPipe()
{
// 畫管道
drawAlpha(&bk, (int)pipeX[0], pipeY[0] + pipeUp, &PIPE[isDay][0]);
drawAlpha(&bk, (int)pipeX[0], pipeY[0] + pipeDown, &PIPE[isDay][1]);
// 畫管道
drawAlpha(&bk, (int)pipeX[1], pipeY[1] + pipeUp, &PIPE[isDay][0]);
drawAlpha(&bk, (int)pipeX[1], pipeY[1] + pipeDown, &PIPE[isDay][1]);
}
// 判斷鳥死亡
bool isDie()
{
if (y + buttom > landY)
return true;
if (x + right > pipeX[0] && x + left < pipeX[0] + 52)
{
if (y + top < pipeY[0] + 40 || y + buttom > pipeY[0] + 140)
return true;
}
if (x + right > pipeX[1] && x + left < pipeX[1] + 52)
{
if (y + top < pipeY[1] + 40 || y + buttom > pipeY[1] + 140)
return true;
}
return false;
}
void reset()
{
drawAlpha(&bk, 0, 0, &BK[isDay]);
}
void put()
{
putimage(0, 0, &bk);
}
// 初始化游戲資源
void init()
{
// 加載圖形資源
loadRes();
// 初始化圖形界面
initgraph(288, 512);
// 初始化隨機數種子
srand((unsigned int)time(NULL));
// 初始化變量
best = 0;
isDay = rand() % 2;
birdColor = rand() % 3;
// 游戲初始時間
clock_t time = clock();
// 開場動畫
while (true)
{
reset();
drawAlpha(&bk, 60, 120, &TITLE);
drawAlpha(&bk, 125, 200, &BIRD[birdColor][pose]);
drawAlpha(&bk, 90, 270, &PLAY);
pose = ((clock() - time) / diff) % 3;
put();
if (GetControl())
{
break;
}
Sleep(10);
}
}
// 加載圖片資源
void loadRes()
{
loadimage(&BK[0], _T("res\bg_day.png"));
loadimage(&BK[1], _T("res\bg_night.png"));
loadimage(&bk, _T("res\bg_day.png"));
loadimage(&OVEIMG, _T("res\bg_day.png"));
loadimage(&BIRD[0][0], _T("res\bird0_0.png"));
loadimage(&BIRD[0][1], _T("res\bird0_1.png"));
loadimage(&BIRD[0][2], _T("res\bird0_2.png"));
loadimage(&BIRD[1][0], _T("res\bird1_0.png"));
loadimage(&BIRD[1][1], _T("res\bird1_1.png"));
loadimage(&BIRD[1][2], _T("res\bird1_2.png"));
loadimage(&BIRD[2][0], _T("res\bird2_0.png"));
loadimage(&BIRD[2][1], _T("res\bird2_1.png"));
loadimage(&BIRD[2][2], _T("res\bird2_2.png"));
loadimage(&PIPE[0][0], _T("res\pipe_down.png"));
loadimage(&PIPE[0][1], _T("res\pipe_up.png"));
loadimage(&PIPE[1][0], _T("res\pipe2_down.png"));
loadimage(&PIPE[1][1], _T("res\pipe2_up.png"));
loadimage(&NUMBER[0][0], _T("res\font_048.png"));
loadimage(&NUMBER[0][1], _T("res\font_049.png"));
loadimage(&NUMBER[0][2], _T("res\font_050.png"));
loadimage(&NUMBER[0][3], _T("res\font_051.png"));
loadimage(&NUMBER[0][4], _T("res\font_052.png"));
loadimage(&NUMBER[0][5], _T("res\font_053.png"));
loadimage(&NUMBER[0][6], _T("res\font_054.png"));
loadimage(&NUMBER[0][7], _T("res\font_055.png"));
loadimage(&NUMBER[0][8], _T("res\font_056.png"));
loadimage(&NUMBER[0][9], _T("res\font_057.png"));
loadimage(&NUMBER[1][0], _T("res\number_context_00.png"));
loadimage(&NUMBER[1][1], _T("res\number_context_01.png"));
loadimage(&NUMBER[1][2], _T("res\number_context_02.png"));
loadimage(&NUMBER[1][3], _T("res\number_context_03.png"));
loadimage(&NUMBER[1][4], _T("res\number_context_04.png"));
loadimage(&NUMBER[1][5], _T("res\number_context_05.png"));
loadimage(&NUMBER[1][6], _T("res\number_context_06.png"));
loadimage(&NUMBER[1][7], _T("res\number_context_07.png"));
loadimage(&NUMBER[1][8], _T("res\number_context_08.png"));
loadimage(&NUMBER[1][9], _T("res\number_context_09.png"));
loadimage(&NUMBER[2][0], _T("res\number_score_00.png"));
loadimage(&NUMBER[2][1], _T("res\number_score_01.png"));
loadimage(&NUMBER[2][2], _T("res\number_score_02.png"));
loadimage(&NUMBER[2][3], _T("res\number_score_03.png"));
loadimage(&NUMBER[2][4], _T("res\number_score_04.png"));
loadimage(&NUMBER[2][5], _T("res\number_score_05.png"));
loadimage(&NUMBER[2][6], _T("res\number_score_06.png"));
loadimage(&NUMBER[2][7], _T("res\number_score_07.png"));
loadimage(&NUMBER[2][8], _T("res\number_score_08.png"));
loadimage(&NUMBER[2][9], _T("res\number_score_09.png"));
loadimage(&MEDAL[0], _T("res\medals_3.png"));
loadimage(&MEDAL[1], _T("res\medals_2.png"));
loadimage(&MEDAL[2], _T("res\medals_1.png"));
loadimage(&MEDAL[3], _T("res\medals_0.png"));
loadimage(&LAND, _T("res\land.png"));
loadimage(&PLAY, _T("res\button_play.png"));
loadimage(&OVER, _T("res\text_game_over.png"));
loadimage(&READY, _T("res\text_ready.png"));
loadimage(&TITLE, _T("res\title.png"));
loadimage(&TUTORIAL, _T("res\tutorial.png"));
loadimage(&PANEL, _T("res\score_panel.png"));
loadimage(&NEW, _T("res\new.png"));
}
大家趕緊去動手試試吧!
審核編輯 :李倩
-
C語言
+關注
關注
180文章
7614瀏覽量
137401 -
源碼
+關注
關注
8文章
652瀏覽量
29353
原文標題:C語言小游戲:飛翔的小鳥(完整版),素材源碼都準備好了!
文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學習基地】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論