> 文档中心 > 基于easyX实现俄罗斯方块

基于easyX实现俄罗斯方块


一:效果展示

 

二:环境准备

1,安装VS,目前最新版本是VS2022,安装方法可以参考我的博客

手把手教你安装VS2022_ftzchina的博客-CSDN博客一:官网下载地址Visual Studio: 面向软件开发人员和 Teams 的 IDE 和代码编辑器Visual Studio 开发工具和服务让任何开发人员在任何平台和语言的应用开发都更加轻松。 随时随地免费使用代码编辑器或 IDE 进行开发。https://visualstudio.microsoft.com/zh-hans/?rr=https://www.microsoft.com/zh-cn/二:下载VS下载选择的时候需要注意有三个选项,Professional是个人试用版,Ente..https://blog.csdn.net/qq_27071221/article/details/123387110?spm=1001.2014.3001.55022,安装easyX图形库

2.1 官网下载安装程序

EasyX Graphics Library for C++EasyX Graphics Library 是针对 Visual C++ 的绘图库,支持 VC6.0 ~ VC2019,简单易用,学习成本极低,应用领域广泛。目前已有许多大学将 EasyX 应用在教学当中。https://easyx.cn/

 2.2 安装easyX

我本地已经提前安装好了VS2022,这时候安装就非常简单了,直接点击安装即可

         我原来用的是vscode搞了很长时间,easyX根本不能识别,当然网上也有很多资料怎么手动去安装,我也尝试了尽力了,还是不行,最后还是选择安装vs2022,图省事的用easyX可以先安装vs,然后安装easyX就可以直接用了,当然有大神怎么把vscode和easyX结合起来的,也可以教教我

 三:俄罗斯方块游戏代码

 下面的代码来自,里面也有代码的详细解释C语言实现俄罗斯方块_霸道小明的博客-CSDN博客_c语言设计俄罗斯方块代码使用C语言完成俄罗斯方块。《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。顾名思义,俄罗斯方块自然是俄罗斯人发明的。https://blog.csdn.net/qq_54169998/article/details/122800521

 不过在我本地还是微调了一把,刚运行有错误

outtextxy和setfont没有参数匹配的重载函数

 修正方法:字符集需要改成使用多字节字符,在工程点右键,最下面的属性

点高级, 字符集改成使用多字节字符

 另外方向键也不好使,可能是平台的原因,我的是win10,需要调整方向键的宏值

 假如你也遇到了方向键不好使的问题,可以将方向键的值打出来,然后做出调整

 可直接运行的代码

#include#include#include#include//kbhit()int score = 0;//总分int rank = 0;//等级#define BLOCK_COUNT 5#define BLOCK_WIDTH 5#define BLOCK_HEIGHT 5#define UNIT_SIZE 20//小方块宽度#define START_X 130//方块降落框,方块降落起始位置#define START_Y 30#define KEY_UP  72//用户操作#define KEY_LEFT 75#define KEY_RIGHT 77#define KEY_DOWN 80#define KEY_SPACE 32#define MinX 30//游戏左上角位置#define MinY 30int speed = 500;//方块降落速度int NextIndex = -1;//下一个方块int BlockIndex = -1;//当前方块typedef enum {//方块朝向BLOCK_UP,BLOCK_RIGHT,BLOCK_LEFT,BLOCK_DOWN}block_dir_t;typedef enum {//方块移动方向MOVE_DOWN,MOVE_LEFT,MOVE_RIGHT}move_dir_t;//方块颜色int color[BLOCK_COUNT] = {GREEN,CYAN,MAGENTA,YELLOW,BROWN};int visit[30][15];//访问数组visit[i][j] = 1表示该位置有方块int markColor[30][15];//对应位置颜色int block[BLOCK_COUNT * 4][BLOCK_WIDTH][BLOCK_HEIGHT] = {// | 形方块{ 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 },// L 形方块   { 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0 },   { 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 },   // 田 形方块   { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },   // T 形方块   { 0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0 },   { 0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 },   // Z 形方块   { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 },   { 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 },};/*************************** * 功能:欢迎页面 * 输入: *无 * 返回: *无 **************************/void welcome() {//1.初始化画布initgraph(550, 660);//2.设置窗口标题HWND window = GetHWnd();//获得窗口,获得当前窗口SetWindowText(window, _T("俄罗斯方块"));//设置标题//3.设置游戏初始页面setfont(40, 0, _T("微软雅黑"));//设置文本的字体样式(高,宽(0表示自适应),字体)setcolor(WHITE);// 设置颜色outtextxy(205, 200, _T("俄罗斯方块"));setfont(20, 0, _T("楷体"));setcolor(WHITE);// 设置颜色outtextxy(150, 300, _T("支援俄罗斯,从俄罗斯方块开始"));Sleep(3000);}/*************************** * 功能:初始化游戏场景 * 输入: *无 * 返回: *无 **************************/void initGameSceen() {char str[16];//存放分数//1.清屏cleardevice();//2.画场景rectangle(27, 27, 336, 635);//方块降落框外框rectangle(29, 29, 334, 633);//方块降落框内框rectangle(370, 50, 515, 195);//方块提示框setfont(24, 0, _T("楷体"));//写“下一个”setcolor(LIGHTGRAY);//灰色outtextxy(405, 215, _T("下一个:"));setcolor(RED);//写分数outtextxy(405, 280, _T("分数:"));//按指定格式,将score写入strsprintf_s(str, 16, "%d", score);//这里设置字符集为多字符,保证outtextxy可以写出变量strouttextxy(415, 310, str);outtextxy(405, 375, _T("等级:"));//等级//按指定格式,将rank写入strsprintf_s(str, 16, "%d", rank);//这里设置字符集为多字符,保证outtextxy可以写出变量strouttextxy(415, 405, str);setcolor(LIGHTBLUE);//操作说明outtextxy(390, 475, "操作说明:");outtextxy(390, 500, "↑: 旋转");outtextxy(390, 525, "↓: 下降");outtextxy(390, 550, "←: 左移");outtextxy(390, 575, "→: 右移");outtextxy(390, 600, "空格: 暂停");}/***************************************** * 功能:清空方块提示框里的方块 * 输入: *无 * 返回: *无 ****************************************/void clearBlock() {setcolor(BLACK);setfont(23, 0, "楷体");for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j < BLOCK_WIDTH; ++j) {int x = 391 + j * UNIT_SIZE;int y = 71 + i * UNIT_SIZE;outtextxy(x, y, "■");}}}/***************************************** * 功能:清除降落过程中的方块 * 输入: *x,y - 方块的坐标(二维数组左上角位置) *block_dir_t - 方块方向 * 返回: *无 ****************************************/void clearBlock(int x, int y, block_dir_t blockDir) {setcolor(BLACK);//setfont(23, 0, "楷体");int id = BlockIndex * 4 + blockDir;for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j < BLOCK_WIDTH; ++j) {if (block[id][i][j] == 1) {int x2 = x + j * UNIT_SIZE;int y2 = y + i * UNIT_SIZE;outtextxy(x2, y2, "■");}}}}/***************************************** * 功能:在提示框 与 降落框的起始位置画方块 * 输入: *x,y - 方块的坐标(二维数组左上角位置) * 返回: *无 ****************************************/void drawBlock(int x, int y) {setcolor(color[NextIndex]);setfont(23, 0, "楷体");for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j < BLOCK_WIDTH; ++j) {if (block[NextIndex * 4][i][j] == 1) {int x2 = x + j * UNIT_SIZE;int y2 = y + i * UNIT_SIZE;outtextxy(x2, y2, "■");}}}}/***************************************** *功能:绘制下降过程中的方块 *输入: *x,y - 方块的坐标(二维数组左上角位置) *block_dir_t - 方块方向 * 返回: *无 ****************************************/void drawBlock(int x, int y, block_dir_t dir) {setcolor(color[BlockIndex]);setfont(23, 0, "楷体");int id = BlockIndex * 4 + dir;for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j < BLOCK_WIDTH; ++j) {if (block[id][i][j] == 1) {//擦除该方块的第i行第j列outtextxy(x + j * UNIT_SIZE, y + i * UNIT_SIZE, "■");}}}}/***************************************** *功能:方块提示框中产生新方块 *输入: *无 *返回: *无 ****************************************/void nextblock() {clearBlock();//产生随机数,随机选择方块srand((unsigned)time(NULL));//使用时间函数的返回值,来作为随机种子NextIndex = rand() % BLOCK_COUNT;//产生0~5的随机数drawBlock(391, 71);}/***************************************** *功能:判断在指定位置向指定方向是否可以移动 *输入: *x,y - 方块位置 *moveDir - 下一步想要移动的方向 *blockDir - 当前方块的方向 * 返回: *true - 可以移动 *false - 不可以移动 ****************************************/bool moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) {//计算方块左上角在30×15的游戏区位置(第多少行, 第多少列)int x = (y0 - MinY) / UNIT_SIZE;int y = (x0 - MinX) / UNIT_SIZE;int ret = 1;int id = BlockIndex * 4 + blockDir;if (moveDir == MOVE_DOWN) {for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j < BLOCK_WIDTH; ++j) {//向下不能运动的条件:实心方块已经达到底部(x+i+1==30),或者底部已有方块if (block[id][i][j] == 1 &&(x + i + 1 == 30 || visit[x + i + 1][y + j] == 1)) {ret = 0;}}}}else if (moveDir == MOVE_LEFT) {for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j < BLOCK_WIDTH; ++j) {//向左不能运动的条件:实心方块已经达到左边界(y+j==0),或者左边已有方块if (block[id][i][j] == 1 &&(y + j <= 0 || visit[x + i][y + j - 1] == 1)) {ret = 0;}}}}else if (moveDir == MOVE_RIGHT) {for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j =15),或者右边已有方块if (block[id][i][j] == 1 &&(y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) {ret = 0;}}}}return ret;}/***************************** *功能:检测游戏是否结束 *输入: *无 * 返回: *无 *****************************/void failCheck() {//游戏结束条件是顶部新被绘制出的方块就要“固化”,顶部新绘制的方块方向朝上,运动方向朝下if (!moveable(START_X, START_Y, MOVE_DOWN, (block_dir_t)BLOCK_UP)) {setcolor(WHITE);setfont(45, 0, "隶体");outtextxy(75, 300, "Game Over!");Sleep(1000);system("pause");closegraph();exit(0);}}/************************** * 功能:延时等待 * 输入: * * 返回: *无 *************************/void wait(int interval) {int count = interval / 10;for (int i = 0; i < count; ++i) {Sleep(10);//如果休眠期间用户有按键,则结束休眠if (_kbhit()) {return;}}}/***************************************** * 功能:判断当前方块是否可以向指定方向旋转 * 输入: *x,y - 方块位置(二维数组坐标) *dir - 方块旋转方向 * 返回: *true - 可以旋转 *false - 不可以旋转 ****************************************/bool rotatable(int x, int y, block_dir_t dir) {//首先判断是否可以继续向下移动if (!moveable(x, y, MOVE_DOWN, dir)) {return false;}int x2 = (y - MinY) / UNIT_SIZE;int y2 = (x - MinX) / UNIT_SIZE;int id = BlockIndex * 4 + dir;for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j < BLOCK_WIDTH; ++j) {//不能旋转条件:左右边界越界或者已有方块“阻挡”if (block[id][i][j] == 1 && (y2 + j = 15 || visit[x2 + i][y2 + j] == 1)) {return false;}}}return true;}/***************************************** * 功能: * 输入: * * 返回: *无 ****************************************/void mark(int x, int y, block_dir_t dir) {int id = BlockIndex * 4 + dir;int x2 = (y - MinY) / UNIT_SIZE;int y2 = (x - MinX) / UNIT_SIZE;for (int i = 0; i < BLOCK_HEIGHT; ++i) {for (int j = 0; j  0; --i) {for (int j = 0; j < 15; ++j) {if (visit[i - 1][j] == 1) {visit[i][j] = 1;markColor[i][j] = markColor[i - 1][j];setcolor(markColor[i][j]);outtextxy(20 * j + MinX, 20 * i + MinY, "■");}else {visit[i][j] = 0;setcolor(BLACK);outtextxy(20 * j + MinX, 20 * i + MinY, "■");}}}//清除最顶层方格setcolor(BLACK);for (int j = 0; j < 15; ++j) {visit[0][j] = 0;outtextxy(20 * j + MinX, MinY, "■");}}/***************************************** * 功能:更新分数 * 输入: *无 * 返回: *无 ****************************************/void addScore(int lines) {char str[32];score += lines * 10;sprintf_s(str, 32, "%d", score);setcolor(RED);outtextxy(415, 310, str);}/************************* * 功能:更新等级 * 输入: *无 * 返回: *无 *************************/void updateGrade() {//更新等级//假设50分一级rank = score / 50;char str[32];sprintf_s(str, 32, "%d", rank);setcolor(RED);outtextxy(415, 405, str);//更新速度if (speed = 0; i--) {// 检查第i行有没有满for (j = 0; j < 15 && visit[i][j]; j++);//执行到此处时,有两种情况:// 1. 第i行没有满,即表示有空位 此时 j=15if (j >= 15) {// 此时,第i行已经满了,就需要消除第i行down(i);  //消除第i行,并把上面的行都下移i++;  // 因为最外层的循环中有 i--, 所以我们先i++, 使得下次循环时,再把这一行检查一下clearLines++;}}// 更新分数addScore(clearLines);// 更新等级(更新等级提示,更新速度)updateGrade();}int main() {welcome();initGameSceen();//产生新方块nextblock();//system("pause");Sleep(800);//初始化访问数组memset(visit, 0, sizeof(visit));while (1) {newblock();//消除满行,并更新分数和速度check();}system("pause");closegraph();return 0;}