1. 程式人生 > >EasyX實現俄羅斯方塊(加BGM版)

EasyX實現俄羅斯方塊(加BGM版)

#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<Windows.h>
#include<time.h>
#include<iostream>
#include"resource.h"
using namespace std;
#pragma comment(lib,"winmm.lib")

#define WIDTH 10
#define HEIGHT 22
#define UNIT 20//沒個單位的實際畫素

BYTE g_World[WIDTH][HEIGHT] = { 0 };//定義遊戲區域
int grades;//得分數
TCHAR s[50];//更新分數用


enum CMD                            //方塊狀態列舉
{
	CMD_ROTATE,						// 方塊旋轉
	CMD_LEFT, 
	CMD_RIGHT, 
	CMD_DOWN,	// 方塊左、右、下移動
	CMD_SINK,						// 方塊沉底
	CMD_QUIT						// 退出遊戲
};


/*16進位制數必須以 0x開頭。比如 0x1表示一個16進位制數。而1則表示一個十進位制。
另外如:0xff,0xFF,0X102A,等等。其中的x也不區分大小寫。
(注意:0x中的0是數字0,而不是字母O)
以下是一些用法示例:
int a = 0x100F;
int b = 0x70 + a;*/

struct BLOCK
{
	WORD dir[4];
	COLORREF color;//方塊顏色

}g_Blocks[7] = { { 0x0F00, 0x4444, 0x0F00, 0x4444, RED },		// I
{ 0x0660, 0x0660, 0x0660, 0x0660, BLUE },		// 口
{ 0x4460, 0x02E0, 0x0622, 0x0740, MAGENTA },	// L
{ 0x2260, 0x0E20, 0x0644, 0x0470, YELLOW },	// 反L
{ 0x0C60, 0x2640, 0x0C60, 0x2640, CYAN },		// Z
{ 0x0360, 0x4620, 0x0360, 0x4620, GREEN },	// 反Z
{ 0x4E00, 0x4C40, 0x0E40, 0x4640, BROWN } };	// T

struct BLOCKINFO
{
	byte id;	// 方塊 ID
	char x, y;	// 方塊在遊戲區中的座標
	byte dir : 2;	// 方向
}	g_CurBlock, g_NextBlock;//當前方塊

enum DRAW
{
	SHOW,                   //顯示方塊
	CLEAR,                  //擦除方塊
	FIX                     //固定方塊
};

//獲取控制命令
DWORD m_oldtime;
/*DWORD 就是 Double Word, 每個word為2個位元組的長度,
DWORD 雙字即為4個位元組,每個位元組是8位,共32位*/

void Quit();          //退出遊戲
void NewGame();       //開始遊戲
void GameOver();      //結束遊戲
CMD GetCmd();         //獲取命令
void DispatchCmd(CMD _cmd);   //分發控制命令
void CreateNewBlock(); //建立新方塊
bool CheckBlockIsPut(BLOCKINFO _block);//檢測當前方塊是否可以被放下
void DrawUnit(int x, int y, COLORREF c, DRAW _draw); //畫單元方塊
void DrawBlock(BLOCKINFO _block, DRAW _draw = SHOW); //畫方塊
void OnRotate();                                     //旋轉方塊
void OnLeft();                                       //左移方塊
void OnRight();                                      //右移方塊
void OnDown();                                       //下移方塊
void OnSink();                                       //沉底方塊









void init()
{
	initgraph(800, 600);
	srand((unsigned)time(NULL));
	PlaySound(MAKEINTRESOURCE(IDR_WAVE1), NULL, SND_RESOURCE | SND_ASYNC | SND_LOOP);
	loadimage(NULL, _T("jpg"), MAKEINTRESOURCE(IDR_JPG1), 800, 600, 1);
	setbkmode(TRANSPARENT);//設定文字背景為透明色


	settextstyle(20, 20, _T("宋體"));
	settextcolor(RED);
	outtextxy(50, 50, _T("俄羅斯方塊遊戲"));
	settextcolor(WHITE);

	settextstyle(14, 0, _T("宋體"));
	outtextxy(20, 120, _T("空格:沉底"));
	outtextxy(20, 140, _T("A或方向鍵左:左移方塊"));
	outtextxy(20, 160, _T("S或方向鍵下:下移方塊"));
	outtextxy(20, 180, _T("D或方向鍵右:右移方塊"));
	outtextxy(20, 200, _T("W或方向鍵上:旋轉方塊"));
	outtextxy(20, 220, _T("Esc:退出遊戲"));
	outtextxy(20, 500, _T("製作:ZZK"));
	setorigin(480, 40);
	_stprintf(s, _T("你的得分是:   %d"), grades);
	outtextxy(20, -40, s);

	//畫遊戲區域
	rectangle(-1, -1, WIDTH*UNIT, HEIGHT*UNIT);
	rectangle((WIDTH + 1)*UNIT - 1, -1, (WIDTH + 5)*UNIT, 4 * UNIT);

	NewGame();
}





int main()
{

	init();
	CMD c;
	while (1)
	{
		c = GetCmd();
		DispatchCmd(c);
		if (c == CMD_QUIT)
		{
			HWND wnd = GetHWnd();
			if (MessageBox(wnd, _T("您要退出遊戲嗎?"), _T("提醒"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
			{
				Quit();
			}
		}
	}





	closegraph();


	return 0;
}


void Quit()
{
	closegraph();
	exit(0);
}

void NewGame()
{
	grades = 0;
	setfillcolor(BLACK);
	//畫填充矩形
	solidrectangle(0, 0, WIDTH*UNIT - 1, HEIGHT*UNIT - 1);
	/*ZeroMemory用0來填充一塊記憶體區域,原型為:
    void ZeroMemory([in] PVOID Destination,[in] SIZE_T Length);
    memset給字串設定緩衝,原型為:
    void *memset( void *dest, int c, size_t count );*/
	ZeroMemory(g_World, WIDTH*HEIGHT);
	g_NextBlock.id = rand() % 7;
	g_NextBlock.dir = rand() % 4;
	g_NextBlock.x = WIDTH + 1;
	g_NextBlock.y = HEIGHT - 1;


	//得到新方塊
	CreateNewBlock();

}


void GameOver()
{

	//控制代碼
	HWND wnd = GetHWnd();
	/*HWND 是一個基本型別 和char int等同級別的 不過你可以把它當做long型去看待。
	它就想是身份證號一樣,人生下來政府給發個身份證號,視窗建立系統就分配一個控制代碼,
	通過身份號 可以知道你的 姓名 住址、年齡,通過控制代碼也就能知道視窗類,視窗指標了*/

	
	if (MessageBox(wnd, _T("遊戲結束。\n你想重來嗎?"), _T("遊戲結束"), MB_YESNO | MB_ICONQUESTION) == IDYES)
		NewGame();
	else
		Quit();
}

CMD GetCmd()
{
	while (1)
	{
		/*GetTickCount是函式。GetTickCount返回(retrieve)從作業系統啟動所經過
		(elapsed)的毫秒數,它的返回值是DWORD。*/
		DWORD newtime = GetTickCount();

		if (newtime - m_oldtime >= 500)
		{
			m_oldtime = newtime;
			return CMD_DOWN;
		}

		if (kbhit())
		{
			switch (getch())
			{
			case 'w':
			case 'W':return CMD_ROTATE;
			case 'a':
			case 'A':return CMD_LEFT;
			case 's':
			case 'S':return CMD_DOWN;
			case 'd':
			case 'D':return CMD_RIGHT;
			case 27:return CMD_QUIT;
			case ' ':return CMD_SINK;
			case 0:
			case 0xE0://鍵盤上下左右對應鍵值
				switch (getch())
				{
				case 72:return CMD_ROTATE;
				case 75:return CMD_LEFT;
				case 77:return CMD_RIGHT;
				case 80:return CMD_DOWN;
				}
			}
		}
		//延時 (降低 CPU 佔用率)
		Sleep(20);
	}
}

void DispatchCmd(CMD _cmd)
{
	switch (_cmd)
	{
	case CMD_ROTATE:	OnRotate();		break;
	case CMD_LEFT:		OnLeft();		break;
	case CMD_RIGHT:		OnRight();		break;
	case CMD_DOWN:		OnDown();		break;
	case CMD_SINK:		OnSink();		break;
	case CMD_QUIT:		break;
	}
}

void CreateNewBlock()
{
	g_CurBlock.id = g_NextBlock.id;
	g_NextBlock.id = rand() % 7;
	g_CurBlock.dir = g_NextBlock.dir;
	g_NextBlock.dir = rand() % 4;
	g_CurBlock.x = (WIDTH - 4) / 2;
	g_CurBlock.y = HEIGHT + 2;

	// 下移新方塊直到有區域性顯示

	WORD c = g_Blocks[g_CurBlock.id].dir[g_CurBlock.dir];
	while ((c & 0xF) == 0)
	{
		g_CurBlock.y--;
		c >>= 4;//右移4位
	}
	DrawBlock(g_CurBlock);
	//繪製下一個方塊
	setfillcolor(BLACK);
	solidrectangle((WIDTH + 1)*UNIT, 0, (WIDTH + 5)*UNIT - 1, 4 * UNIT - 1);
	DrawBlock(g_NextBlock);

	// 設定計時器,用於判斷自動下落
	m_oldtime = GetTickCount();

}

void DrawUnit(int x, int y, COLORREF c, DRAW _draw)
{
	//計算單元方塊對應螢幕座標
	int left = x*UNIT;
	int top = (HEIGHT - y - 1)*UNIT;
	int right = (x + 1)*UNIT - 1;
	int bottom = (HEIGHT - y)*UNIT - 1;

	switch (_draw)
	{
	case SHOW:
		//設定線條顏色
		setlinecolor(0x006060);
		//這個函式用於畫空心圓角矩形。
		roundrect(left + 1, top + 1, right - 1, bottom - 1, 5, 5);
		setlinecolor(0x003030);
		roundrect(left, top, right, bottom, 8, 8);
		//設定填充顏色
		setfillcolor(c);
		setlinecolor(LIGHTGRAY);
		fillrectangle(left + 2, top + 2, right - 2, bottom - 2);
		break;
	case FIX:
		//設定填充顏色通過獲取c的RGB值實現
		setfillcolor(RGB(GetRValue(c) * 2 / 3, GetGValue(c) * 2 / 3, GetBValue(c) * 2 / 3));
		setlinecolor(DARKGRAY);
		fillrectangle(left + 1, top + 1, right - 1, bottom - 1);
		break;
	case CLEAR:
		setfillcolor(BLACK);
		solidrectangle(x*UNIT, (HEIGHT - y - 1)*UNIT, (x + 1)*UNIT-1, (HEIGHT - y)*UNIT - 1);
		break;
	}
}


void DrawBlock(BLOCKINFO _block, DRAW _draw )//畫那個方塊,以什麼形式畫
{
	WORD b = g_Blocks[_block.id].dir[_block.dir];
	int x, y;
	for (int i = 0; i < 16; i++, b <<= 1)
	{
		if (b & 0x8000)
		{
			x = _block.x + i % 4;
			y = _block.y - i / 4;
			if (y < HEIGHT)
			{
				DrawUnit(x, y, g_Blocks[_block.id].color, _draw);
			}
		}
	}
}


bool CheckBlockIsPut(BLOCKINFO _block)
{
	WORD b = g_Blocks[_block.id].dir[_block.dir];
	int x, y;

	for (int i = 0; i < 16; i++, b <<= 1)
	{
		if (b & 0x8000)
		{
			x = _block.x + i % 4;
			y = _block.y - i / 4;
			//如果越界就無法放下
			if (x<0 || x>=WIDTH || y < 0)
			{
				return false;
			}
			//如果不越界並且下一個移動的位置有方塊就不能放下
			if (y < HEIGHT&&g_World[x][y])
			{
				return false;
			}
		}
	}
	return true;
}


void OnRotate()
{
	//獲取可旋轉的x的偏移量
	int dx;
	BLOCKINFO tmp = g_CurBlock;
	tmp.dir++;
	if (CheckBlockIsPut(tmp))
	{
		dx = 0;
		goto rotate;
	}
	tmp.x = g_CurBlock.x - 1;
	if (CheckBlockIsPut(tmp))
	{dx = -1;   goto rotate;}
	tmp.x = g_CurBlock.x + 1;
	if (CheckBlockIsPut(tmp)) 
	{ dx = 1;		goto rotate; }
	tmp.x = g_CurBlock.x - 2;
	if (CheckBlockIsPut(tmp)) 
	{ dx = -2;	goto rotate; }
	tmp.x = g_CurBlock.x + 2;
	if (CheckBlockIsPut(tmp)) 
	{ dx = 2;		goto rotate; }
	return;

rotate:
	DrawBlock(g_CurBlock, CLEAR);
	g_CurBlock.dir++;
	g_CurBlock.x += dx;
	DrawBlock(g_CurBlock);

}


void OnLeft()
{
	BLOCKINFO tmp = g_CurBlock;
	tmp.x--;
	if (CheckBlockIsPut(tmp))
	{
		DrawBlock(g_CurBlock, CLEAR);
		//左移只需x--;
		g_CurBlock.x--;
		DrawBlock(g_CurBlock);
	}
}

void OnRight()
{
	BLOCKINFO tmp = g_CurBlock;
	tmp.x++;
	if (CheckBlockIsPut(tmp))
	{
		DrawBlock(g_CurBlock, CLEAR);
		g_CurBlock.x++;
		DrawBlock(g_CurBlock);
	}
}

void OnDown()
{
	BLOCKINFO tmp = g_CurBlock;
	tmp.y--;
	if (CheckBlockIsPut(tmp))
	{
		DrawBlock(g_CurBlock, CLEAR);
		g_CurBlock.y--;
		DrawBlock(g_CurBlock);
	}
	else
		OnSink();	// 不可下移時,執行“沉底方塊”操作
}

void OnSink()
{
	int i, x, y;
	//連續下移方塊
	DrawBlock(g_CurBlock, CLEAR);
	BLOCKINFO tmp = g_CurBlock;
	tmp.y--;
	while (CheckBlockIsPut(tmp))
	{
		g_CurBlock.y--;
		tmp.y--;
	}
	DrawBlock(g_CurBlock, FIX);
	//固定方塊在遊戲區

	WORD b = g_Blocks[g_CurBlock.id].dir[g_CurBlock.dir];
	for (int i = 0; i < 16; i++, b <<= 1)
	{
		if (b & 0x8000)
		{
			if (g_CurBlock.y - i / 4 >= HEIGHT)
			{
				GameOver();
				return;
			}
			else
			{
				//修改地圖為1,標記這點存在一個單位方塊
				g_World[g_CurBlock.x + i % 4][g_CurBlock.y - i / 4] = 1;
			}
		}
	}

	BYTE mark = 0;
	//因為最多可以同時消除4行,所以只需檢測4行內是否有消除行為
	for (y = g_CurBlock.y; y >= max(g_CurBlock.y - 3, 0); y--)
	{
		i = 0;
		for (x = 0; x < WIDTH; x++)
		{
			if (g_World[x][y] == 1)
				i++;
		}
		if (i == WIDTH)
		{
			grades += 10;
			mark |= (1 << (g_CurBlock.y - y));
			setfillcolor(LIGHTGREEN);
			setlinecolor(LIGHTGREEN);
			setfillstyle(BS_HATCHED, HS_DIAGCROSS);//BS_HATCHED 圖案填充。HS_DIAGCROSS  xxxxxxx圖案填充 
			fillrectangle(0, (HEIGHT - y - 1)*UNIT + UNIT / 2 - 5, WIDTH*UNIT - 1, (HEIGHT - y - 1)*UNIT + UNIT / 2 + 5);
			setfillstyle(BS_SOLID);//BS_SOLID  固實填充。 

		}

	}
	if (mark)//如果產生整行消除
	{
		
		Sleep(300);
		//擦掉剛剛標記的行

		IMAGE img;
		for (int i = 0; i < 4; i++, mark >>= 1)
		{
			if (mark & 1)
			{
				for (y = g_CurBlock.y - i + 1; y < HEIGHT; y++)
				{
					for (x = 0; x < WIDTH; x++)
					{

						//將上面的一行的值賦值給最後一行
						g_World[x][y - 1] = g_World[x][y];
						g_World[x][y] = 0;
					}
				}

				//// 從當前繪圖裝置獲取影象

				getimage(&img, 0, 0, WIDTH*UNIT, (HEIGHT - (g_CurBlock.y - i + 1))*UNIT);
				putimage(0, UNIT, &img);
			}
		}

		settextcolor(getbkcolor());
		outtextxy(20, -40, s);
		_stprintf(s, _T("你的得分是:   %d"), grades);
		settextcolor(WHITE);
		outtextxy(20, -40, s);
	}
	CreateNewBlock();
}

執行效果



寫這個程式花了一上午,畢竟自己還是新手,才開始學easyX庫,

所以寫的有點慢,寫好了下午執行時發現了幾個BUG,

從1:00一直改到3:00,才把BUG改完,哎。

這個程式主要是音樂佔用太大的空間60M左右.

程式下載地址

連結:http://pan.baidu.com/s/1kUXUjfL 密碼:s35l