我罗斯方块最终篇
作业描述 | 详情 |
作业属于 | 2020面向对象程序设计 |
作业要求 |
|
作业目标 |
|
作业正文 | https://www.cnblogs.com/Es-war/p/13090414.html |
小组成员 |
|
代码仓库 | https://github.com/Es-war/Tetris |
一、运行效果
游戏过程 |
游戏结束 |
- 玩家一获胜
- 玩家二获胜
- 平局
游戏运行视频 |
二、代码要点
界面渲染 |
- 采用Easy_x进行界面渲染,将渲染功能打包在pait类中
class paint { public: void endGame(); //绘制结束界面 void initEnviroment(); //初始化环境 void drawGameBG(); //绘制游戏背景 void drawLeftSide(); //绘制游戏左侧边 void drawRightSide(); //绘制游戏右侧边 void drawLeftMap(); //绘制玩家一的地图 void drawRightMap(); //绘制玩家二的地图 void drawSquareNext(int num); //绘制预览框内的方块 void drawItem(int x, int y, COLORREF c); //绘制方块 void drawSquareNow(int num); //绘制当前“尘埃落定”的全体方块 };
初始化 |
- 游戏初始环境的初始化
void paint::initEnviroment() { // 窗口设置 initgraph(1210, 540); HWND hwnd = GetHWnd(); SetWindowText(hwnd, L"我罗斯"); SetWindowPos(hwnd, HWND_TOP, 0, 20, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); // 绘图模式设置 setbkmode(TRANSPARENT); // 随机数种子 srand(time(NULL)); }
- 每一轮游戏的初始化:分数、储存方块信息的“地图”,结束标志重置
- 每一个新方块的初始化:获取方块所需信息——颜色、类型、形状,生成下一方块标志的重置,预览框方块信息的设置
提前将各种方块的信息储存在数组中,通过随机数取模的方式来随机生成方块
now_c_idx[num] = next_c_idx[num]; now_s_idx[num] = next_s_idx[num]; now_d_idx[num] = next_d_idx[num]; next_c_idx[num] = rand() % 7; next_s_idx[num] = rand() % 7; next_d_idx[num] = rand() % 4;
各类操作 |
- 游戏过程中的方块左移、右移、旋转、下降均需要事先判断操作是否可行,若不可行,则不执行
- 长时间未执行操作,方块将自动下降(利用GetTickCount()函数来获取时间)
bool player::checkPut(int mp_x, int mp_y, int dir_idx) { int sq_x[4]; int sq_y[4]; for (int i = 0; i < 4; ++i) { sq_x[i] = mp_x + squares[now_s_idx[num]].dir[dir_idx][i][0]; sq_y[i] = mp_y + squares[now_s_idx[num]].dir[dir_idx][i][1]; } // 【左右越界、下方越界、重复占格】 for (int i = 0; i < 4; ++i) { if (sq_x[i] < 0 || sq_x[i] > 9 || sq_y[i] > 14) return false; if (sq_y[i] < 0) // 检查坐标合法性 continue; if (mp[num][sq_x[i]][sq_y[i]]) return false; } return true; }
玩家类 |
- 玩家类的各种操作大同小异,但是在下降、消行、给对方增加行、检查是否结束差异较大,所以将这部分剥离出来,构造玩家一、二类公有继承于玩家类,分别编写上述成员函数,便于进行相关操作
默认下降速度的改变 |
- 随着玩家分数的增加,游戏难度需相应提高,设置了默认下降速度改变的机制,但是最终会稳定在某个最大速度值
//检查玩家一是否长时间未执行正确指令 int speed1, speed2; if (score[0] <= 3000) speed1 = 1000 - score[0] / 10; else speed1 = 700; if (time_tmp[0] - time_now[0] >= speed1) { time_now[0] = time_tmp[0]; one.moveDown(); over = true; } //检查玩家二是否长时间未执行正确指令 if (score[1] <= 3000) speed2 = 1000 - score[1] / 10; else speed2 = 700; if (time_tmp[1] - time_now[1] >= speed2) { time_now[1] = time_tmp[1]; two.moveDown(); over = true; }
玩家分数的计算 |
- 玩家在游戏过程中消除的行数不同所得分数也不相同,并且多行消除后的得分不是简单的一行消除得分的多次叠加
score[num] += 100.0 * clearNum * (1 + clearNum * 0.25);
键盘敲击事件监听 |
- 利用_kbhit()来监听键盘敲击,并且在每次执行完操作后 Sleep(20)来降低CPU占用
//接受指令 if (_kbhit()) { //兼顾大小写 switch (_getch()) { case 'W': case 'w': one.moveRotate(); break; case 'A': case 'a': one.moveLeft(); break; case 'D': case 'd': one.moveRight(); break; case 'S': case 's': one.moveDown(); break; case 72: two.moveRotate(); break; case 75: two.moveLeft(); break; case 77: two.moveRight(); break; case 80: two.moveDown(); break; } } // 降低CPU占用 Sleep(20);
游戏结束后的弹窗 |
- 每局游戏结束后在屏幕上显示本局游戏的赢家(或者出现平局),并显示询问是否“再来亿局”的弹窗
starpaint.endGame(); if (MessageBox(GetHWnd(), over_tips, L"再来亿局?", MB_ICONQUESTION | MB_YESNO) == IDNO) break;
最终效果 |
- 可以保证游戏的正常运行,实现方块左移、右移、旋转、下降、长时间不操作自动下降、消行操作,并且补充消行后给对方增加相应行数的随机方块功能。
- 侧边方块预览功能正常实现。
- 随着等级的提升方块下降速度加快。
- 每局游戏结束后在屏幕上显示本局游戏的赢家(或者出现平局),实现了“再来亿局”的功能正常使用。
三、依然存在的问题
- 游戏流畅度的进一步优化
- 使用了较多全局变量,未能很好地实现代码的封装性,所以在最后未能分离出类代码文件
四、收获与心得
学习了之前没有接触过的Easy_x的操作,了解到如何利用它来进行页面的渲染。明白了要想真正写出这样一个小游戏并不容易,在一开始,就应该利用流程图、思维导图等工具来对思路进行处理。在实现过程中会碰到各种各样意料之外的问题,不要急于实现全部功能,应该是在已有的、正确的代码基础上进行功能的完善和补充。适时进行代码功能的测试,以便及时修改,否则bug堆积多了,修改时也无从下手。在这个过程中,与组员的分工合作也十分重要,及时与组员保持联系,确认编码上的一些修改等问题。在这次制作“我罗斯”的过程中,慢慢体会到做项目与平常写PTA上的作业的深刻不同,不要只满足于完成PTA这类作业上,而不学习、实践项目的编写,否则将来只会成为一名“面向PTA的程序员”,而不是一个能够编写项目的、合格的程序员。