EasyX實現俄羅斯方塊(加BGM版)
阿新 • • 發佈:2019-01-25
#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