c語言小遊戲 flybird Easyx程式設計 專案原始碼講解

2020-08-12 11:44:38

分享一個c語言小遊戲可以幫助大家更好的熟悉c語言
這個就是前幾年很火的手機小遊戲飛行的小鳥
1.遊戲流程:小鳥不斷下落使用按鍵讓其飛起來不斷的穿過兩個管子。
2.遊戲框架:採用老師提供的框架和重要模組分開編寫,使用easyx圖形系統。
a.核心演算法:使用了座標的判斷,和判斷語句的熟練運用。
b.程式排程。
c.透明貼圖消除圖片的背景方框
d.檔案的使用
3.程式碼優化。

先看一下遊戲效果
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
現在開始上程式碼

先上流程圖
在这里插入图片描述在这里插入图片描述



//
//
//flybrid小遊戲
//	主要玩法:
//		使用空格鍵讓不斷下落的小鳥在管子中間穿梭
//		若碰到了地板,頂部,管子遊戲結束
//		儲存最高分數



#include"head.h"

int x = 0;//分數
int max;//最高分

IMAGE back;//背景圖
IMAGE per;//小鳥圖
IMAGE bird1;//小鳥底圖
IMAGE up;//管子圖
IMAGE down;//管子圖
IMAGE lend;//地板圖
IMAGE over;//遊戲結束圖
IMAGE over1;//遊戲結束底圖
IMAGE began;//開始遊戲圖
IMAGE began1;//開始遊戲底圖
IMAGE diff;//困難模式遊戲圖
IMAGE again1;//重新開始遊戲圖
IMAGE out;//退出圖
IMAGE open;//開機動畫的圖
//IMAGE again;

MOUSEMSG k;

//小鳥的結構體
struct Bird {
	int x;		//小鳥的橫座標
	int y;		//小鳥的縱座標	
	int speed;	//小鳥下墜的高度
}bird = { 100,250,30 };	//初始化

//管子的結構體
struct pipe {
	int x;		//管子的橫座標
	int y;		//管子的縱座標
	int h;		//管子的高度
};

//載入資源
void loadRescue() {
	loadimage(&back, "bg_night.png");		//載入背景圖
	loadimage(&per, "bird.png",30,30);	//載入小鳥圖
	loadimage(&bird1, "bird1_1.png", 30, 30);
	loadimage(&lend, "land.png");			//載入地板圖
	loadimage(&over, "game_over.png");		//載入遊戲結束圖
	loadimage(&over1, "text_game_over_1.png");		//載入遊戲結束圖
	loadimage(&down, "down.png");	//載入管子圖
	loadimage(&up, "up.png");//載入上管子圖
	loadimage(&began, "button_play.png");//簡單模式遊戲圖
	//loadimage(&again, "again.png");
	loadimage(&again1, "again1.png");//重新開始圖
	loadimage(&began, "button_play.png");
	loadimage(&began1, "button_play_1.png");
	loadimage(&out, "back.png");

}

//開始的按鈕
void Booton() {
	Startmove();
	MOUSEMSG m;//滑鼠定義
	MOUSEMSG g;//
	loadimage(&back, "bg_night.png");		//載入背景圖
	putimage(0, 0, &back);
	rectangle(120, 210, 200, 250);//簡單模式
	rectangle(120, 310, 200, 350);//困難模式
	loadimage(&began, "button_play.png",80,40);
	loadimage(&diff, "diff.png",80,40);
	putimage(120, 310, &diff);
		putimage(120,210, &began);
	while (1) {
		m = GetMouseMsg();//獲取滑鼠
		if (m.uMsg == WM_LBUTTONDOWN /*|| _kbhit()*/) {
			if (((m.x > 100 && m.x < 216) && (m.y > 200 && m.y < 270))//簡單模式範圍
				/*|| _getch() == 'e'*/) {
				Begin();
			}
			
				}
		g = GetMouseMsg();
		if (g.uMsg == WM_LBUTTONDOWN /*|| _kbhit()*/) {
			if (((g.x > 120 && g.x < 216) && (g.y > 300 && g.y < 370))//困難模式範圍
				/*|| _getch() == 'd'*/) {
				Diffcult_Began();
			}
		}
		if (_kbhit()) {
			if (_getch() == 'e') {
				Begin();
			}
			else if (_getch() == 'd') {
				Diffcult_Began();
			}
		}
	}
	return;
}

//設定鳥
void drawperson(int x, int y) {
	putimage(x,y,&per,SRCAND);//底圖
	putimage(x, y, &bird1, SRCPAINT);//正圖
	bird.y += 5; //鳥一直往下墜落5
}


//多執行緒 否則會卡頓
//載入按鍵音樂 
DWORD WINAPI playMusic(LPVOID IpParamer) {
	mciSendString("open correct.mp3", 0, 0, 0);
	mciSendString("play correct.mp3 wait", 0, 0, 0);
	mciSendString("close correct.mp3", 0, 0, 0);
	return 0;
}

//載入遊戲結束音樂
DWORD WINAPI OverMusic(LPVOID IpParamer) {
	mciSendString("open 12321.mp3", 0, 0, 0);
	mciSendString("play 12321.mp3 wait", 0, 0, 0);
	mciSendString("close 12321.mp3", 0, 0, 0);
	return 0;
}

//載入地板
void drawland() {
	putimage(0,414, &lend, SRCAND);
	putimage(0,414, &lend, SRCPAINT);

}


//鳥的按鍵處理
void Down() {

	char userkey = _getch();
	switch (userkey) {
	//按下空格鍵時鳥向上彈跳
	case ' ':						
		bird.y -= bird.speed;
		CreateThread(NULL,NULL,playMusic,NULL,NULL,NULL); //按下空格鍵時的音效
		break;
	default:
		break;
	}
}
//撞擊地板後
int hitland() {
	if (bird.y <= 0 || bird.y >= (512 - 96)) {
		return 1;
	}
	return 0;
}

//遊戲死亡後的彈出的圖示
void GameOver() {
//圖示的座標
	int x = 50; 
	int y = 512;
//圖示從底層出現 提留在中間層5s
	while (y >= 200) {
		putimage(0, 0, &back);
		putimage(x, y, &over1, SRCAND);
		putimage(x, y, &over, SRCPAINT);
		y -= 30;
		Sleep(100);
	}
	CreateThread(NULL, NULL, OverMusic, NULL, NULL, NULL); //遊戲結束後的音效
	Again();//重新開始遊戲圖示選項
	Sleep(5000);
	
	
}

//定義管子
void printpipe(struct pipe pip[], int i) {
	pip[i].h = rand() % 100 + 130; //管子在螢幕的高度 隨機顯示在130—230之間
	pip[i].x = 288;
	pip[i].y = 0;
}


//畫出管子
void drawpip(struct pipe pip) {
	//上方的管子
	putimage(pip.x, 0, 52, pip.h, &down, 0, 320 - pip.h);// xy軸座標 長框 圖片指針名 圖片開始從哪畫的xy軸
	//下方的管子
	putimage(pip.x, 412-(320-pip.h), 52, 320 - pip.h, &up, 0, 0);
	
}

//簡單模式管子的移動和重新整理
void Movetube(struct pipe pip[], int i) {
	pip[i].x -= 5;


	//當管子被刷出螢幕外時載入新管子
	if (pip[i].x < (-52 - 150)) //第一個出去150後 重新載第四跟管子 螢幕永遠都有兩個管子
	{
		printpipe(pip, i);
	}
	return;
}

//困難模式下管子的移動和重新整理
void Diff_Movetube(struct pipe pip[], int i) {
	pip[i].x -= 10;


	//當管子被刷出螢幕外時載入新管子
	if (pip[i].x < (-52 - 150))
	{
		printpipe(pip, i);
	}
	return;
}

//是否碰撞到了上面的柱子
int HItUp(struct pipe pip[],int i) {
	//判斷鳥的座標與上方柱子座標之間的關係
	if ((bird.y <= pip[i].h) && (bird.x + 30 >= pip[i].x) && (pip[i].x <= pip[i].x + 52)) { 
		return 1;//1表示撞到了
	}
	return 0;
}


//是否碰撞到了下面 下麪的柱子
int HItDown(struct pipe pip[], int i) {
	//判斷鳥的座標與下方柱子座標之間的關係
	if ((bird.y + 30 >= 412 - (320 - pip[i].h)) && (bird.x + 30 >= pip[i].x) && (pip[i].x <= pip[i].x + 52)) {
		return 1;
	}
	return 0;
}

//是否撞到管子
void Hittube(struct pipe pip[], int i) {
	//判斷鳥穿過當前這個管子
	if (pip[i].x + 52 <= bird.x) {
		return;
	}
				
			//與下一個管子比較
				else if (HItUp(pip, i) == 1) {
					GameOver();
					return;
				}
				else if (HItDown(pip, i) == 1) {
				GameOver();
				return;
			}

}
//記錄分數
void Score(struct pipe pip[], int i){
	
	char s[10];
	sprintf_s(s, " %d", x);//將整型數位轉成字元儲存在s中
	//settextcolor(RED);
	outtextxy(72, 0, s);
	outtextxy(0, 0, "你的分數: ");
	if (pip[i].x + 52 == bird.x){ //判斷鳥是否串過管子了
		x++;
		sprintf_s(s, " %d", x);
		outtextxy(0, 0, "你的分數: ");
		outtextxy(72, 0, s);
	}
}

//簡單分數管理 
void ChangeMaxScore(int x) {
	FILE* fp = fopen("Score.txt", "r+"); //存放普通模式下的最高分檔案
	fscanf(fp, "%d", &max); //取出儲存在max中
	char s1[10];
	sprintf_s(s1, " %d", max);//轉換型別 用outtextxy輸出到遊戲介面
	outtextxy(72, 16, s1);
	outtextxy(0, 16, "最高分數: ");
	FlushBatchDraw();
	fclose(fp);
	if (x > max) {//在數據庫中存入最高分
		FILE* fp = fopen("Score.txt", "w+");
		fprintf(fp, "%d", x);
		fclose(fp);
	}
	
}

//困難模式分數管理 
void Diff_ChangeMaxScore(int x) {
	FILE* fp = fopen("Diff_Score.txt", "r+");//困難模式最高分的檔案
	fscanf(fp, "%d", &max);
	char s1[10];
	sprintf_s(s1, " %d", max);
	outtextxy(72, 16, s1);
	outtextxy(0, 16, "最高分數: ");
	fclose(fp);
	if (x > max) {//在數據庫中存入最高分
		FILE* fp = fopen("Diff_Score.txt", "w+");
		fprintf(fp, "%d", x);
		fclose(fp);
	}

}

//遊戲開始
void Begin() {
	
	struct pipe pip[3];//三個管子
	//初始化三個管子
	for (int i = 0; i < 3; i++) {
		printpipe(pip, i);//在螢幕中顯示管子
		pip[i].x = 288 + i * 150;//每個管子之間的間隔150
	}
	loadRescue();	//載入資源
	while (1) {
		putimage(0, 0, &back); //畫出背景圖
		drawland();			   //畫出地板
		drawperson(bird.x, bird.y);	//畫出鳥
		for (int i = 0; i < 3; i++) {
			drawpip(pip[i]);//畫出管子
			Movetube(pip, i);
		}
		FlushBatchDraw();
		//判斷是否撞上了管子
		for (int i = 0; i < 3; i++) {
			Hittube(pip, i);
			Score(pip, i);	//顯示當前分數
		}
		
		//MaxScore();//顯示最高分
		ChangeMaxScore(x);

		//判斷按鍵是否按下
		if (_kbhit()) {
			Down();
		}

		//判斷是否撞擊地板
		if (hitland()) {
			GameOver();
			break;
		}
		Sleep(100);

	}
}

void Diffcult_Began() {

	struct pipe pip[3];//三個管子
	//初始化三個管子
	for (int i = 0; i < 3; i++) {
		printpipe(pip, i);//在螢幕中顯示管子
		pip[i].x = 288 + i * 150;//每個管子之間的間隔150
	}
	loadRescue();	//載入資源
	while (1) {
		putimage(0, 0, &back); //畫出背景圖
		drawland();			   //畫出地板
		drawperson(bird.x, bird.y);	//畫出鳥
		for (int i = 0; i < 3; i++) {
			drawpip(pip[i]);//畫出管子
			Diff_Movetube(pip, i);
		}
		FlushBatchDraw();
		//判斷是否撞上了管子
		for (int i = 0; i < 3; i++) {
			Hittube(pip, i);
			Score(pip, i);	//顯示當前分數
		}

		//MaxScore();//顯示最高分
		Diff_ChangeMaxScore(x);

		//判斷按鍵是否按下
		if (_kbhit()) {
			Down();
		}

		//判斷是否撞擊地板
		if (hitland()) {
			GameOver();
			break;
		}
		Sleep(100);

	}
}

//重新開始
void Again() {
	
	rectangle(70, 310,240, 380);
	putimage(70, 310, &again1);
	putimage(100, 100, &out);
	//putimage(80, 310, &again, SRCPAINT);
	MOUSEMSG k;
	while (1) {
	
		k = GetMouseMsg();
		if (k.uMsg == WM_LBUTTONDOWN) {
			if (((k.x > 80 && k.x < 240) && (k.y > 300 && k.y < 380))
				) {
				x = 0;
				bird.y = 250;
				Begin();//重新開始遊戲
			}
			
		}
		if (_kbhit()) {
			if (_getch() == 'k') {
				x = 0;
				Begin();//重新開始遊戲
			}
			else if (_getch() == 'q') {
				Endmove();
			}

		}
	}
}

void Startmove() {//開機動畫
	clearrectangle(0, 0, 288, 512);
	setbkcolor(BLACK);
	
	int i;
	for (i = 0; i < 5; i++) {
		//隨機出現圖畫
		int x = rand() % 180;
		int y = rand() % 480;
		loadimage(&open, "open.png");
		putimage(x, y, &open);
		Sleep(50);
	}
}


void Endmove() { //關機動畫 
	clearrectangle(0, 0, 288, 512);//清屏
	setbkcolor(BLACK);//背景顏色
	 int y = 100;
	for (int x = 50; x < 288; x += 20) {
		settextcolor(BLUE);
		settextstyle(32, 0, NULL);//高度 寬度0自適應 字型樣式無
		outtextxy(100, y,"GOOD BYE");
		y += 52;//下移
		setcolor(YELLOW);
		setfillstyle(BLUE);
		fillellipse(x, 50, 10, 10);
		Sleep(500);
		setcolor(GREEN);
		setfillstyle(RED);
		fillellipse(x, 50, 10, 10);

	}
	closegraph();
}




//標頭檔案
#define  _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<graphics.h> //圖形庫檔案
#include<conio.h> //按鍵標頭檔案
#include<time.h>
#include<mmsystem.h>//呼叫音樂的標頭檔案
#pragma comment(lib,"winmm.lib")
/*全域性變數*/
extern int x;//分數
extern int max;//最高分

/*遊戲資源的載入初始化*/
void loadRescue();//載入資源

void drawperson(int x, int y);//畫鳥

void Booton();//開始的按鈕

DWORD WINAPI playMusic(LPVOID IpParamer);//載入按鍵音樂   多執行緒 否則會卡頓

DWORD WINAPI OverMusic(LPVOID IpParamer);//載入遊戲結束音樂

void drawland();//載入地板

void printpipe(struct pipe pip[], int i);//定義管子

void drawpip(struct pipe pip);//畫出管子





/*遊戲過程的處理*/
void Down();//鳥的按鍵處理

int hitland();//撞擊地板後

void Movetube(struct pipe pip[], int i);//管子的移動和重新整理

int HItUp(struct pipe pip[], int i);//是否碰撞到了上面的柱子

int HItDown(struct pipe pip[], int i);//是否碰撞到了下面 下麪的柱子

void Hittube(struct pipe pip[], int i);//是否撞到管子

void Diff_Movetube(struct pipe pip[], int i);//困難模式下的管子移動



/*分數管理*/
void Diff_ChangeMaxScore(int x);//最高分的記錄

void Score(struct pipe pip[], int i);//記錄分數

void ChangeMaxScore(int x);//分數管理


/*遊戲的幾種模式*/
void Diffcult_Began();//困難模式

void Begin();//遊戲開始

void Again();//重新開始

void GameOver();//遊戲死亡後的彈出的圖示




/*開關機動畫*/
void Endmove();//關機動畫

void Startmove();//開機動畫

//主函數
#include"head.h"

int main() {
initgraph(288, 512);//初始化圖形系統
setbkcolor(BLACK);
Booton(); //開始按鈕 點選開始後進入遊戲
return 0;
}

//剛剛學的c語言如果有不對的地方歡迎大家給我提出
//不懂的地方可以評論提問 我儘量解答
//需要圖片資源的可以留下郵箱