EasyX实现俄罗斯方块(加BGM版)

xiaoxiao2021-02-28  71

#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

转载请注明原文地址: https://www.6miu.com/read-80211.html

最新回复(0)