俄罗斯方块游戏

C语言零基础项目:俄罗斯方块游戏!详细思路+源码分享

每天一个C语言小项目,提升你的编程能力!

俄罗斯方块是童年的经典游戏~~

由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。

本次用C语言来实现!这个程序在界面上没有做额外的修饰,重点放在游戏手感和消息处理过程。游戏手感上做了一些细节处理,仔细体验感受一下。

效果如图:

其中也是加入了按键操作功能:"A"左移一格;"D"右移一格;"W"旋转方块;S 下移一格;空格键让方块下落到底),"ESC"健退出游戏,用户还可以自己根据自定义习惯的按键来操作游戏。

编译环境:Visual Studio 2019/2022,EasyX插件

代码展示:

#include #include #include #include /////////////////////////////////////////////// 定义常量、枚举量、结构体、全局变量/////////////////////////////////////////////#defineWIDTH10// 游戏区宽度#defineHEIGHT22// 游戏区高度#defineUNIT20// 每个游戏区单位的实际像素// 定义操作类型enum CMD{CMD_ROTATE,// 方块旋转CMD_LEFT, CMD_RIGHT, CMD_DOWN,// 方块左、右、下移动CMD_SINK,// 方块沉底CMD_QUIT// 退出游戏};// 定义绘制方块的方法enum DRAW{SHOW,// 显示方块CLEAR,// 擦除方块FIX// 固定方块};// 定义七种俄罗斯方块struct BLOCK{WORD dir[4];// 方块的四个旋转状态COLORREF color;// 方块的颜色};BLOCK 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;// 方块 IDchar x, y;// 方块在游戏区中的坐标byte dir:2;// 方向}g_CurBlock, g_NextBlock;// 定义游戏区BYTE g_World[WIDTH][HEIGHT] = {0};/////////////////////////////////////////////// 函数声明/////////////////////////////////////////////void Init();// 初始化游戏void Quit();// 退出游戏void NewGame();// 开始新游戏void GameOver();// 结束游戏CMD GetCmd();// 获取控制命令void DispatchCmd(CMD _cmd);// 分发控制命令void NewBlock();// 生成新的方块bool CheckBlock(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 main(){Init();CMD c;while(true){c = GetCmd();DispatchCmd(c);// 按退出时,显示对话框咨询用户是否退出if (c == CMD_QUIT){HWND wnd = GetHWnd();if (MessageBox(wnd, _T("您要退出游戏吗?"), _T("提醒"), MB_OKCANCEL | MB_ICONQUESTION) == IDOK)Quit();}}}// 初始化游戏void Init(){initgraph(640, 480);srand((unsigned)time(NULL));setbkmode(TRANSPARENT);// 设置图案填充的背景色为透明// 显示操作说明settextstyle(14, 0, _T("宋体"));outtextxy(20, 330, _T("操作说明"));outtextxy(20, 350, _T("上:旋转"));outtextxy(20, 370, _T("左:左移"));outtextxy(20, 390, _T("右:右移"));outtextxy(20, 410, _T("下:下移"));outtextxy(20, 430, _T("空格:沉底"));outtextxy(20, 450, _T("ESC:退出"));// 设置坐标原点setorigin(220, 20);// 绘制游戏区边界rectangle(-1, -1, WIDTH * UNIT, HEIGHT * UNIT);rectangle((WIDTH 1) * UNIT - 1, -1, (WIDTH 5) * UNIT, 4 * UNIT);// 开始新游戏NewGame();}// 退出游戏void Quit(){closegraph();exit(0);}// 开始新游戏void NewGame(){// 清空游戏区setfillcolor(BLACK);solidrectangle(0, 0, WIDTH * UNIT - 1, HEIGHT * UNIT - 1);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;// 获取新方块NewBlock();}// 结束游戏void GameOver(){HWND wnd = GetHWnd();if (MessageBox(wnd, _T("游戏结束。您想重新来一局吗?"), _T("游戏结束"), MB_YESNO | MB_ICONQUESTION) == IDYES)NewGame();elseQuit();}// 获取控制命令DWORD m_oldtime;CMD GetCmd(){// 获取控制值while(true){// 如果超时,自动下落一格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 'd':case 'D':return CMD_RIGHT;case 's':case 'S':return CMD_DOWN;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 NewBlock(){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;}// 绘制新方块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:// 画固定的方块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 CheckBlock(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 = 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 (CheckBlock(tmp)){dx = 0;goto rotate;}tmp.x = g_CurBlock.x - 1;if (CheckBlock(tmp)){dx = -1;goto rotate;}tmp.x = g_CurBlock.x 1;if (CheckBlock(tmp)){dx = 1;goto rotate;}tmp.x = g_CurBlock.x - 2;if (CheckBlock(tmp)){dx = -2;goto rotate;}tmp.x = g_CurBlock.x 2;if (CheckBlock(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 (CheckBlock(tmp)){DrawBlock(g_CurBlock, CLEAR);g_CurBlock.x--;DrawBlock(g_CurBlock);}}// 右移方块void OnRight(){BLOCKINFO tmp = g_CurBlock;tmp.x ;if (CheckBlock(tmp)){DrawBlock(g_CurBlock, CLEAR);g_CurBlock.x ;DrawBlock(g_CurBlock);}}// 下移方块void OnDown(){BLOCKINFO tmp = g_CurBlock;tmp.y--;if (CheckBlock(tmp)){DrawBlock(g_CurBlock, CLEAR);g_CurBlock.y--;DrawBlock(g_CurBlock);}elseOnSink();// 不可下移时,执行“沉底方块”操作}// 沉底方块void OnSink(){int i, x, y;// 连续下移方块DrawBlock(g_CurBlock, CLEAR);BLOCKINFO tmp = g_CurBlock;tmp.y--;while (CheckBlock(tmp)){g_CurBlock.y--;tmp.y--;}DrawBlock(g_CurBlock, FIX);// 固定方块在游戏区WORD b = g_Blocks[g_CurBlock.id].dir[g_CurBlock.dir];for(i = 0; i < 16; i , b <= HEIGHT){// 如果方块的固定位置超出高度,结束游戏GameOver();return;}elseg_World[g_CurBlock.x i % 4][g_CurBlock.y - i / 4] = 1;}// 检查是否需要消掉行,并标记BYTE remove = 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){remove |= (1 << (g_CurBlock.y - y));setfillcolor(LIGHTGREEN);setlinecolor(LIGHTGREEN);setfillstyle(BS_HATCHED, HS_DIAGCROSS);fillrectangle(0, (HEIGHT - y - 1) * UNIT UNIT / 2 - 5, WIDTH * UNIT - 1, (HEIGHT - y - 1) * UNIT UNIT / 2 5);setfillstyle(BS_SOLID);}}if (remove)// 如果产生整行消除{// 延时 300 毫秒Sleep(300);// 擦掉刚才标记的行IMAGE img;for(i = 0; i >= 1){if (remove & 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);}}}// 产生新方块NewBlock();}

大家赶紧去动手试试吧!

此外,我也给大家分享我收集的其他资源,从最零基础开始的教程到C语言C 项目案例,帮助大家在学习C语言的道路上披荆斩棘!

编程学习书籍分享:

编程学习视频分享:

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

对于C/C 感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C 的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。