1. 程式人生 > >【C / EasyX】十字消除遊戲的實現方法

【C / EasyX】十字消除遊戲的實現方法

十字消除遊戲是以前百度上火過一段時間的一個Flash小遊戲。

遊戲邏輯比較簡單,是我初次接觸EasyX圖形庫後使用EasyX寫的第一個i程式。

遊戲開始後,在棋盤中隨機生成成對的隨即顏色的方塊,預留一些空白位置。玩家點選空白位置,如果所在位置延伸出的十字線上,能遇到相同顏色的方塊,則消除之,累加分數。消除一對方塊會恢復一定時間,不進行任何操作時間條會自動減少,直至耗盡遊戲結束。如果棋盤上剩餘方塊少於一定數量,會繼續隨機生成一定數量的新方塊。

效果圖如下:





原始碼如下:

/////////////////////////////////////////////////////////
// 程式名稱:AyaCrossX(十字消除遊戲)
// 編譯環境:Visual C++ 6.0 / 2012,EasyX 2013霜降版
// 作    者:ls9512 <http://www.baidu.com/p/ls9512>
// 最後修改:2013-11-2
//

//系統函式庫
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <math.h>
#include <time.h>
//EasyX圖形庫
#include <graphics.h>


//////////////////////////////以下是 巨集定義引數 //////////////////////////////
#define WIN_WIDTH		600					//螢幕寬度
#define WIN_HEIGHT		430					//螢幕高度
#define WIN_BLANKTOP	45					//螢幕頂部空邊
#define WIN_BLANK		25					//螢幕邊緣留空
#define G_BN			20					//方塊橫向數量
#define G_BM			12					//方塊縱向數量
#define G_BW			20					//方塊寬度
#define G_BS			8					//方塊間隔寬度
#define G_BSELECT		3					//方塊選中大小
#define G_FLOATW		4					//選中浮動大小
#define G_BDWO			1					//消失初始大小
#define G_BDW			6					//消失大小
#define G_CN			10					//顏色數量
#define G_BUL			25					//方塊增量程度
#define C_BLOCKPANEL	RGB(255, 255, 221)	//底板顏色
#define C_HPBAR			RGB(255, 170, 255)	//HP條顏色


//////////////////////////////以下是資料結構定義//////////////////////////////
//方塊結構
struct Block{
	COLORREF color;				//顏色
	float	 FLOAT;				//浮動大小
	float	 DIS;				//消除延遲
	bool	 isDIS;				//是否消失中
	bool	 isClick;			//是否被點選
};

//點結構
struct Point{
	int x;
	int y;
};

//矩形結構
struct Rect{
	int x;
	int y;
	int w;
	int h;
};


//////////////////////////////以下是  全域性變數  //////////////////////////////
int GamePhase = 0;				//遊戲階段 0 準備 1 進行 2 結束 3 幫助介面
int isShowFPS = 0;				//是否顯示幀數
int mouseX;						//滑鼠位置座標X
int mouseY;						//滑鼠位置座標Y
int time_max;					//總遊戲時間
int time_now;					//當前剩餘時間
int level;						//等級
int score;						//分數
bool isMouseDown;				//滑鼠按下
Block block[G_BM][G_BN];		//方塊陣列
COLORREF Defcolor[G_CN];		//候選顏色陣列


//////////////////////////////以下是  函式宣告  //////////////////////////////
float	GetFPS();						//獲取幀數
void	HpSleep(DWORD ms);				//絕對延時
Point	GetMouseLocal(int x, int y);	//獲取滑鼠所在座標
void	AddBlock(int n);				//新增指定個數的未選中方塊
void	Init();							//初始化
void	DisCheck(int x, int y);			//檢測指定位置的消除
int		CountBlock();					//統計方塊個數
void	Manager();						//邏輯處理
void	Draw();							//繪圖處理
void	StartDraw();					//遊戲開始繪圖
void	GamingDraw();					//遊戲進行中繪圖
void	EndDraw();						//遊戲結束繪圖
void	HelpDraw();						//遊戲幫助繪圖
void	EndFrame();						//幀結束處理
bool	IsInRect(int x, int y, Rect r);	//是否在矩形內


//////////////////////////////以下是輔助功能函式//////////////////////////////
//計算畫面FPS(每秒幀數)
float GetFPS()
{
#define FPS_COUNT 8
	static int i = 0;
	static DWORD oldTime = GetTickCount();
	static float fps;
	if (i > FPS_COUNT)
	{
		i = 0;
		int newTime = GetTickCount();
		int elapsedTime = newTime - oldTime;
		fps = FPS_COUNT / (elapsedTime / 1000.0f);
		oldTime = newTime;
	}
	i++;
	return fps;
}

//絕對延時
void HpSleep(DWORD ms)
{
	static clock_t oldclock = clock();		// 靜態變數,記錄上一次 tick

	oldclock += ms * CLOCKS_PER_SEC / 1000;	// 更新 tick

	if (clock() > oldclock)					// 如果已經超時,無需延時
		oldclock = clock();
	else
		while(clock() < oldclock)			// 延時
			Sleep(1);						// 釋放 CPU 控制權,降低 CPU 佔用率
}

//獲取滑鼠選中點
Point GetMouseLocal(int x, int y)
{
	Point p;
	x = (x - WIN_BLANK) / (G_BW + G_BS);
	y = (y - WIN_BLANK - WIN_BLANKTOP) / (G_BW + G_BS);
	if (x < 0 || x >= G_BN) x = -1;
	if (y < 0 || y >= G_BM) y = -1;
	p.x = x;
	p.y = y;
	return p;
}

//是否在矩形內
bool IsInRect(int x, int y, Rect r)
{
	return ((x >= r.x && x <= r.w) && (y >= r.y && y <= r.h));
}

//新增制定個數的未選中方塊
void AddBlock(int n)
{
	//隨機生成方塊
	int num = 0, x1, y1, x2, y2;
	int num_max = n;
	while(num < num_max)
	{
		x1 = rand() % G_BN;
		y1 = rand() % G_BM;
		x2 = rand() % G_BN;
		y2 = rand() % G_BM;
		if(block[y1][x1].isClick && block[y2][x2].isClick)
		{
			COLORREF color = Defcolor[rand() % G_CN];
			block[y1][x1].color = color;
			block[y1][x1].isClick = false;
			block[y1][x1].FLOAT = 0;
			block[y1][x1].DIS = 0;
			block[y1][x1].isDIS = false;
			block[y2][x2].color = color;
			block[y2][x2].isClick = false;
			block[y2][x2].FLOAT = 0;
			block[y2][x2].DIS = 0;
			block[y2][x2].isDIS = false;
			num += 2;
		}
	}
}

//初始化
void Init()
{
	//預置顏色
	Defcolor[0] = RGB(255, 153, 0);
	Defcolor[1] = RGB(204, 102, 0);
	Defcolor[2] = RGB(27,  118, 255);
	Defcolor[3] = RGB(255, 136, 255);
	Defcolor[4] = RGB(204, 204, 102);
	Defcolor[5] = RGB(104, 204, 204);
	Defcolor[6] = RGB(255, 125, 125);
	Defcolor[7] = RGB(204, 104, 204);
	Defcolor[8] = RGB(190, 190, 190);
	Defcolor[9] = RGB(0,   204, 0);

	//初始化時間
	time_max = 10000;
	time_now = 10000;

	//初始化分數
	score = 0;

	//置全選中
	for(int i = 0; i < G_BM; i++)
	{
		for(int j = 0; j < G_BN; j++)
		{
			block[i][j].isClick = true;
			block[i][j].FLOAT = 0;
			block[i][j].DIS = 0;
		}
	}

	//新增方塊
	AddBlock(G_BN * G_BM * 2 / 3);

	//清除滑鼠訊息佇列
	FlushMouseMsgBuffer();
}

//統計方塊個數
int CountBlock()
{
	int n = 0;
	for(int i = 0; i < G_BN; i++)
	{
		for(int j = 0; j < G_BM; j++)
		{
			if(!block[j][i].isClick) n++;
		}
	}
	return n;
}

//檢測指定位置消除
void DisCheck(int x, int y)
{
	//記錄待消除的方塊
	Block* b[4];
	int i, j, n = 0;

	//向左
	for(i = x; i >= 0; i--)
	{
		if(!block[y][i].isClick)
		{
			b[n] = &block[y][i];
			n++;
			break;
		}
	}

	//向右
	for(i = x; i < G_BN; i++)
	{
		if(!block[y][i].isClick)
		{
			b[n] = &block[y][i];
			n++;
			break;
		}
	}

	//向上
	for(j = y; j >= 0; j--)
	{
		if(!block[j][x].isClick)
		{
			b[n] = &block[j][x];
			n++;
			break;
		}
	}

	//向下
	for(j = y; j < G_BM; j++)
	{
		if(!block[j][x].isClick)
		{
			b[n] = &block[j][x];
			n++;
			break;
		}
	}

	//消除
	for(i = 0; i < n; i++)
	{
		for(j = i + 1; j < n; j++)
		{
			if(b[i]->color == b[j]->color)
			{
				if(!b[i]->isDIS){
					b[i]->DIS = G_BDWO;
					b[i]->isDIS = true;
				}
				if(!b[j]->isDIS){
					b[j]->DIS = G_BDWO;
					b[j]->isDIS = true;
				}
			}
		}
	}
	isMouseDown = false;
}

//邏輯處理
void Manager()
{
	//如果在遊戲中
	if(GamePhase == 1)
	{
		Point p = GetMouseLocal(mouseX, mouseY);
		int x = p.x, y = p.y;
		//如果選中
		if(p.x != -1 && p.y != -1)
		{
			//如果單擊
			if(isMouseDown && block[y][x].isClick)
			{
				//觸發消除檢測
				DisCheck(x, y);
			}
		}
		//新增新方塊新增隨機個數新方塊
		if(CountBlock() < G_BN * G_BM / 3)
		{
			AddBlock((rand() % 15 + 15) * 2);
			//時間恢復
			time_now += 500;
			if(time_now > time_max) time_now = time_max;
		}
	}
}

//開始繪圖
void StartDraw()
{
	settextstyle(52, 30, _T("Impact"));
	//標題
	settextcolor(RGB(255, 17, 102));
	outtextxy(143, 93, _T("AyaCrossX"));
	settextcolor(RGB(255, 153, 238));
	outtextxy(140, 90, _T("AyaCrossX"));
	//版本
	settextstyle(22, 10, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(340, 153, _T("v0.91 By:ls9512"));
	//開始遊戲按鈕
	Rect r;
	r.x = 240;
	r.y = 220;
	r.w = r.x + 125;
	r.h = r.y + 35;
	setlinecolor(RGB(255, 17, 102));
	if(IsInRect(mouseX, mouseY, r)) 
	{
		setfillcolor(RGB(255, 187, 119));
		//開始遊戲按鍵響應
		if(isMouseDown)
		{
			Init();
			GamePhase = 1;
			isMouseDown = false;
		}
	}
	else
		setfillcolor(RGB(255, 255, 204));
	fillrectangle(r.x, r.y, r.w, r.h);
	settextstyle(25, 10, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(r.x + 5, r.y + 5, _T("PLAY GAME"));
	
	//幫助遊戲按鈕
	r.x = 240;
	r.y = 270;
	r.w = r.x + 125;
	r.h = r.y + 35;
	setlinecolor(RGB(255, 17, 102));
	if(IsInRect(mouseX, mouseY, r))
	{
		setfillcolor(RGB(255, 187, 119));
		//幫助按鍵響應
		if(isMouseDown) GamePhase = 3;
	}
	else
		setfillcolor(RGB(255, 255, 204));
	fillrectangle(r.x, r.y, r.w, r.h);
	settextstyle(25, 10, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(r.x + 5, r.y + 5, _T("HELP INFO"));
	//退出遊戲按鈕
	r.x = 240;
	r.y = 320;
	r.w = r.x + 125;
	r.h = r.y + 35;
	setlinecolor(RGB(255, 17, 102));
	if(IsInRect(mouseX, mouseY, r))
	{
		setfillcolor(RGB(255, 187, 119));
		//退出按鍵響應
		if(isMouseDown) exit(0);
	}
	else
		setfillcolor(RGB(255, 255, 204));
	fillrectangle(r.x, r.y, r.w, r.h);
	settextstyle(25, 10, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(r.x + 5, r.y + 5, _T("EXIT GAME"));
	//說明
	settextstyle(16, 5, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(175, 400, _T("Programing By VC++ & EasyX     Date:2013.11.01"));
}

//遊戲中繪圖
void GameingDraw()
{
	int x, y;
	COLORREF c, c2;

	//時間條
	x = WIN_BLANK;
	y = WIN_BLANK;
	int HPBARW = (G_BW + G_BS) * G_BN - 200;
	setlinecolor(RGB(255, 17, 255));
	setfillcolor(WHITE);
	fillrectangle(x, y, x + HPBARW, y + 10);
	setfillcolor(C_HPBAR);
	fillrectangle(x, y, x + (int)(HPBARW * (1.0 * time_now / time_max)), y + 10);

	//底板
	setlinecolor(RGB(255, 25, 22));
	setfillcolor(C_BLOCKPANEL);
	x = WIN_BLANK - G_BS;
	y = WIN_BLANK + WIN_BLANKTOP - G_BS;
	fillrectangle(x, y, x + (G_BW + G_BS) * G_BN + G_BS, y + (G_BW + G_BS) * G_BM + G_BS);

	//畫方塊
	for(int i = 0; i < G_BM; i++)
	{
		for(int j = 0; j < G_BN; j++)
		{
			if(!block[i][j].isClick)
			{
				c = block[i][j].color;
				c = RGB(GetRValue(c) - (BYTE)G_BUL, GetGValue(c) - (BYTE)G_BUL, GetBValue(c) - (BYTE)G_BUL);
				int fw = (int)block[i][j].FLOAT;
				int fd = (int)block[i][j].DIS;
				setlinecolor(c);
				setfillcolor(block[i][j].color);
				x = j * (G_BW + G_BS) + WIN_BLANK - fw - fd;
				y = i * (G_BW + G_BS) + WIN_BLANK + WIN_BLANKTOP - fw - fd;
				fillrectangle(x, y, x + G_BW + 2 * (fw + fd), y + G_BW + 2 * (fw + fd));
				//浮動縮減
				if(block[i][j].FLOAT > 0) block[i][j].FLOAT -= 0.4f;
				//消失延遲
				if(block[i][j].isDIS)
				{
					if(block[i][j].DIS < G_BDW)
						block[i][j].DIS += 0.4f;
					else
					{
						//消除
						block[i][j].isClick = true;
						//加分
						score += 100;
						time_now += 25;
						if (time_now > time_max) time_now = time_max;
					}
				}
			}
		}
	}

	//獲取滑鼠位置
	Point p = GetMouseLocal(mouseX, mouseY);

	//畫出滑鼠游標
	if(p.x != -1 && p.y != -1)
	{
		c = RED;	
		x = p.x, y = p.y;
		int fw;
		if(block[y][x].isClick) fw = 0;
		else fw = (int)block[y][x].FLOAT;
		x = x * (G_BW + G_BS) + WIN_BLANK - G_BSELECT - fw;
		y = y * (G_BW + G_BS) + WIN_BLANK + WIN_BLANKTOP - G_BSELECT -fw;
		//選中方塊加亮
		if(!block[p.y][p.x].isClick)
		{
			//置浮動大小
			block[p.y][p.x].FLOAT = G_FLOATW;
			c2 = block[p.y][p.x].color;
		}
		else
		{
			c2 = C_BLOCKPANEL;
		}
		setlinecolor(c);
		setfillcolor(c2);
		fillrectangle(x, y, x + G_BW + 2 * (G_BSELECT + fw), y + G_BW + 2 * (G_BSELECT + fw));
	}

	//畫出分數
	settextstyle(25, 10, _T("Verdana"));
	settextcolor(RGB(155, 51, 68));
	TCHAR b[10];
	#if _MSC_VER > 1200
		_stprintf_s(b, _T("%d"), score);
	#else
		_stprintf(b, _T("%d"), score);
	#endif
	outtextxy(390, 15, _T("分數:"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(440, 16, b);
}

//遊戲結束繪圖
void EndDraw()
{
	//標題
	settextstyle(52, 30, _T("Impact"));
	settextcolor(RGB(255, 17, 102));
	outtextxy(143, 103, _T("GAME OVER"));
	settextcolor(RGB(215, 193, 238));
	outtextxy(140, 100, _T("GAME OVER"));

	//版本
	settextstyle(34, 12, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	TCHAR b[10];
	#if _MSC_VER > 1200
		_stprintf_s(b, _T("%d"), score);
	#else
		_stprintf(b, _T("%d"), score);
	#endif
	outtextxy(232, 173, _T("分數:"));
	outtextxy(290, 173, b);

	//重新開始遊戲按鈕
	Rect r;
	r.x = 240;
	r.y = 240;
	r.w = r.x + 125;
	r.h = r.y + 35;
	setlinecolor(RGB(255, 17, 102));
	if(IsInRect(mouseX, mouseY, r))
	{
		setfillcolor(RGB(255, 187, 119));
		//開始遊戲按鍵響應
		if(isMouseDown)
		{
			Init();
			GamePhase = 1;
		}
	}
	else
		setfillcolor(RGB(255, 255, 204));
	fillrectangle(r.x, r.y, r.w, r.h);
	settextstyle(25, 10, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(r.x + 5, r.y + 5, _T("   REPLAY"));

	//退出
	r.x = 240;
	r.y = 290;
	r.w = r.x + 125;
	r.h = r.y + 35;
	setlinecolor(RGB(255, 17, 102));
	if(IsInRect(mouseX, mouseY, r)) 
	{
		setfillcolor(RGB(255, 187, 119));
		//退出按鍵響應
		if(isMouseDown) exit(0);
	}
	else
		setfillcolor(RGB(255, 255, 204));
	fillrectangle(r.x, r.y, r.w, r.h);
	settextstyle(25, 10, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(r.x + 5, r.y + 5, _T("EXIT GAME"));
}

//遊戲幫助繪圖
void HelpDraw()
{
	//標題
	settextstyle(52, 30, _T("Impact"));
	settextcolor(RGB(255, 17, 152));
	outtextxy(233, 33, _T("HELP"));
	settextcolor(RGB(215, 193, 238));
	outtextxy(230, 30, _T("HELP"));

	//底板
	setlinecolor(RGB(255, 17, 204));
	setfillcolor(RGB(255, 221, 255));
	fillrectangle(60, 100, 550, 280);

	//版本
	settextstyle(18, 10, _T("黑體"));
	settextcolor(RGB(245, 126, 92));
	outtextxy(70, 110, _T("十字消除是一款經典消除類小遊戲。"));
	outtextxy(70, 145, _T("【玩法】"));
	outtextxy(80, 170, _T("1.點選空白處,所在橫縱十字線上同色方塊會被消除。"));
	outtextxy(80, 195, _T("2.消除方塊會累加得分,並恢復一定量的時間."));
	outtextxy(80, 220, _T("3.方塊數量少於某數值會出現新方塊並回復時間."));
	outtextxy(80, 245, _T("4.時間耗盡時則遊戲結束."));

	//返回
	Rect r;
	r.x = 240;
	r.y = 360;
	r.w = r.x + 125;
	r.h = r.y + 35;
	setlinecolor(RGB(255, 17, 102));
	if(IsInRect(mouseX, mouseY, r)) 
	{
		setfillcolor(RGB(255, 187, 119));
		//返回按鍵響應
		if(isMouseDown) GamePhase = 0;
	}
	else
		setfillcolor(RGB(255, 255, 204));
	fillrectangle(r.x, r.y, r.w, r.h);
	settextstyle(25, 9, _T("Verdana"));
	settextcolor(RGB(255, 51, 68));
	outtextxy(r.x + 5, r.y + 5, _T("BACK MENU"));
}

//輸出幀數
void DrawFPS()
{
	//輸出幀數
	if(isShowFPS)
	{
		settextcolor(RED);
		settextstyle(14, 0, _T("宋體"));
		TCHAR s[5];
		#if _MSC_VER > 1200
			_stprintf_s(s, _T("%.1f"), GetFPS());
		#else
			_stprintf(s, _T("%.1f"), GetFPS());
		#endif
		outtextxy(0, 0, s);
	}
}

//繪圖
void Draw()
{
	//清屏
	setlinecolor(WHITE);
	setfillcolor(WHITE);
	fillrectangle(0, 0, WIN_WIDTH, WIN_HEIGHT);
	switch(GamePhase)
	{
		case 0:	StartDraw();	break;
		case 1:	GameingDraw();	break;
		case 2:	EndDraw();		break;
		case 3:	HelpDraw();		break;
	}
	DrawFPS();
}

//幀結束處理
void EndFrame()
{
	time_now -= 2;
	//遊戲結束
	if(time_now <= 0 && GamePhase == 1)
	{
		GamePhase = 2;	
	}
}

//主入口函式
int main()
{
	//置隨機數種子
	srand((unsigned)time(NULL));
	//初始化裝置,載入圖片
	initgraph(WIN_WIDTH, WIN_HEIGHT);
	//設定視窗標題
	SetWindowText(GetHWnd(), _T("AyaCrossX v0.91 By:ls9512"));
	cleardevice();
	//設定黑色背景
	setbkmode(TRANSPARENT);
	settextcolor(BLACK);
	//開啟雙緩衝,防止閃屏
	BeginBatchDraw();
	// 滑鼠訊息變數
	MOUSEMSG mmsg;

	//初始化
	Init();

	while(true)
	{
		//處理滑鼠訊息
		while(MouseHit())
		{
			mmsg = GetMouseMsg();
			switch(mmsg.uMsg)
			{
				case WM_MOUSEMOVE:		mouseX = mmsg.x; mouseY = mmsg.y;	break;
				case WM_LBUTTONDOWN:	isMouseDown = true;					break;
				case WM_LBUTTONUP:		isMouseDown = false;				break;
			}
		}
		//邏輯處理
		Manager();
		//繪圖處理
		Draw();
		//顯示快取的繪製內容
		FlushBatchDraw();
		//幀結束處理
		EndFrame();
		//延遲,幀數控制
		HpSleep(18);
	}

	// 關閉
	EndBatchDraw();
	closegraph();
	return 0;
}

這個程式是我在13年10月時寫的,有些細節也記不太清楚了,在這就不贅述了,如果有疑問的地方可以留言我看到了會回覆解答。

【注】

此程式已被EasyX官網收錄為範例: