时间:2020-10-29 14:19:49 | 栏目:C代码 | 点击:次
本文实例为大家分享了C语言实现俄罗斯方块的具体代码,供大家参考,具体内容如下
GitHub:【C语言】实现俄罗斯方块源代码
Head.h
#ifndef _HEAD_H_ #define _HEAD_H_ #include<graphics.h> #include<stdio.h> #include<conio.h> #include<stdlib.h> #include<time.h> #include<string.h> #define _CRT_SECURE_NO_WARNINGS 1 //界面的相关的参数 #define WALL_SQUARE_WIDTH 10 //围墙方块的宽度 #define xWALL_SQUARE_NUM 30 //x轴方向的方块的数目 #define yWALL_SQUARE_WIDTH 46 //y轴方向的方块的数目 #define GAME_WALL_WIDTH (WALL_SQUARE_WIDTH*xWALL_SQUARE_NUM) //游戏区域的宽度 300 #define GAME_WALL_HTGH (WALL_SQUARE_WIDTH*yWALL_SQUARE_WIDTH) //游戏区域的高度 460 #define WINDOW_WIDTH 480 // 游戏总窗口宽度 480 #define WINDOW_HIGH 460 // 游戏总窗口高度 460 //定义俄罗斯方块的相关参数 #define ROCK_SQUARE_WIDTH (2*WALL_SQUARE_WIDTH) //俄罗斯方块的大小是围墙的两倍 20 #define xROCK_SQUARE_NUM ((GAME_WALL_WIDTH -20)/ROCK_SQUARE_WIDTH) // 游戏区x轴放的方块数目:14 #define yROCK_SQUARE_NUM ((GAME_WALL_HTGH -20)/ROCK_SQUARE_WIDTH) // 游戏区y轴放的方块数目:22 //定义移动方块的相关操作 #define DIRECT_UP 3 #define DIRECT_DOWN 2 #define DIRECT_LEFT -1 #define DIRECT_RIGHT 1 /*数据结构-线性表(结构体数组)*/ typedef struct ROCK { //用来表示方块的形状(每一个字节是8位,用每4位表示方块中的一行) unsigned short rockShapeBits; int nextRockIndex; //下一个方块,在数组中的下标 } RockType; //方块在图形窗口中的位置(即定位4*4大块的左上角坐标) typedef struct LOCATE { int left; int top; } RockLocation_t; //全局变量-游戏板的状态描述(即表示当前界面哪些位置有方块) //0表示没有,1表示有(多加了两行和两列,形成一个围墙,便于判断方块是否能够移动) int game_board[yROCK_SQUARE_NUM + 2][xROCK_SQUARE_NUM + 2] = { 0 }; int game_socres = 0; //全局分数 // 把俄罗斯方块的19种类放到数组中 int rockTypeNum = 19; RockType RockArray[19] = { (0, 0) }; //预览区的方块的位置 RockLocation_t preRockLocation = {GAME_WALL_WIDTH+70,70}; //每次生成初始化方块的位置 RockLocation_t initRockLocation = { (WALL_SQUARE_WIDTH + 100), WALL_SQUARE_WIDTH }; //分数显示的位置 //各个文件中的函数 // 画出界面以及画出方块Draw.h中 void DrawGameWindow(); void DisplayRock(int rockIdx, RockLocation_t* LocatePtr, bool displayed); //初始化Init源文件 void InitGame(); //game.h void PlayGame(); bool IsGameOver(); #endif
Draw.h
#include"Head.h" //画出游戏界面 void DrawGameWindow() { //先画出围墙 setcolor(BLUE); setlinestyle(PS_SOLID,NULL,0); setfillcolor(BLUE); //画出上下围墙 for (int x = WALL_SQUARE_WIDTH; x <= GAME_WALL_WIDTH; x += WALL_SQUARE_WIDTH) { fillrectangle(x - WALL_SQUARE_WIDTH, 0, x, WALL_SQUARE_WIDTH); //上 fillrectangle(x - WALL_SQUARE_WIDTH, GAME_WALL_HTGH - WALL_SQUARE_WIDTH, x, GAME_WALL_HTGH);//下 } //画出左右围墙 for (int y = WALL_SQUARE_WIDTH; y <= GAME_WALL_HTGH; y += WALL_SQUARE_WIDTH) { fillrectangle(0, y, WALL_SQUARE_WIDTH, y + WALL_SQUARE_WIDTH);//左 fillrectangle(GAME_WALL_WIDTH - WALL_SQUARE_WIDTH, y, GAME_WALL_WIDTH, y + WALL_SQUARE_WIDTH);//右 } //画出右边统计分数及相关东西 //画出分割线 setcolor(WHITE); setlinestyle(PS_DASH,2); line(GAME_WALL_WIDTH + 20, 0, GAME_WALL_WIDTH + 20, GAME_WALL_HTGH); //设置字体 LOGFONT font; gettextstyle(&font); settextstyle(18, 0, _T("宋体")); font.lfQuality = ANTIALIASED_QUALITY;//设置输出效果为抗锯齿 //1显示预览形状 outtextxy(GAME_WALL_WIDTH + 80, 30, _T("预览")); outtextxy(GAME_WALL_WIDTH + 80, 170, _T("分数")); outtextxy(GAME_WALL_WIDTH + 65, 250, _T("操作说明")); outtextxy(GAME_WALL_WIDTH + 40, 290, _T("w a s d控制方向")); outtextxy(GAME_WALL_WIDTH + 40, 335, _T("空格 暂停")); //显示分数 setcolor(RED); outtextxy(GAME_WALL_WIDTH + 90, 200, '0'); } //在游戏区显示编号为rockIdx的方块 void DisplayRock(int rockIdx, RockLocation_t* LocatePtr, bool displayed) { int color;//方块的填充颜色 int lineColor = WHITE;//线的颜色 int boardFalg = 0; int xRock = 0; int yRock = 0; unsigned short rockCode = RockArray[rockIdx].rockShapeBits; //如果displayed为true的话,将方块块颜色设置为white,game_board对应的位置设置为1; //如果displayed为false的话,将方块块颜色设置为black,game_board对应的位置设置为0; displayed ? (color = RED, boardFalg = 1) : (color = BLACK,lineColor = BLACK, boardFalg = 0); setcolor(lineColor); setfillcolor(color); setlinestyle(PS_SOLID);//设置为实线, xRock = LocatePtr->left; yRock = LocatePtr->top; int count = 0;//每4个换行,记录坐标偏移量 unsigned short mask = 1; for (int i = 1; i <= 16; ++i) { mask = 1 << (16 - i); if ((rockCode & mask) != 0) //如果不为0的话,就画出小方块 { fillrectangle(xRock , yRock, xRock + ROCK_SQUARE_WIDTH, yRock + ROCK_SQUARE_WIDTH); } if (i % 4 == 0) //换行 { yRock = yRock + ROCK_SQUARE_WIDTH; xRock = xRock = LocatePtr->left; } else { xRock += ROCK_SQUARE_WIDTH; } } }
Init.h
#include"Head.h" static void ShapeStrToBit(unsigned char *rockShapeStr, unsigned short& rockShapeBit); static void ReadRcok(); void InitGame() { //把全局游戏游戏版初始化,边界初始化为1 for (int i = 0; i < xROCK_SQUARE_NUM + 2; i++) { game_board[0][i] = 1; //上边界 game_board[yROCK_SQUARE_NUM + 1][i] = 1; //下边界 } for (int i = 0; i < yROCK_SQUARE_NUM + 2; i++) { game_board[i][0] = 1 ; //左边界 game_board[i][xROCK_SQUARE_NUM + 1] = 1; //右边界 } //读取俄罗斯方块 ReadRcok(); } //从文件中读取方块的形状存储到rockArray中 void ReadRcok() { FILE* fp = fopen("RockShape.ini","r"); if (NULL == fp) { printf("打开文件失败\n"); return; } unsigned char readBuf[1024]; //fp读取到字符串readbuf中 unsigned short rockShapeBit = 0;//存放方块形状,占16比特位 unsigned char rockShapeStr[16];//存放方块字符串 int ShapeStrIdx = 0; int rockNum = 0;//统计方块的个数以及存放方块数组RockArray的下标 int rocknext = 0;//方块数组中下一个形状 int rockShapeStart = 0;//同一类型的形状 while (true) { size_t readSize = fread(readBuf, 1, 1024, fp); if (readSize == 0) break; //处理readbuf for (size_t idx = 0; idx < readSize; ++idx) { //将字符存放到rockShapeStr中 while (ShapeStrIdx < 16 && idx < readSize) { if (readBuf[idx] == '@' || readBuf[idx] == '#') { rockShapeStr[ShapeStrIdx] = (unsigned char)readBuf[idx]; ++ShapeStrIdx; } ++idx; //可能idx == readSize了 if (readBuf[idx] == '*')//修改上一次方块的next值 { idx += 5; RockArray[--rockNum].nextRockIndex = rockShapeStart; rockNum++; rockShapeStart = rockNum; rocknext = rockShapeStart ; } } //可能没有填满 if (ShapeStrIdx < 16) { break; } else //填满shapestr { ShapeStrIdx = 0;//置0 //将rockShapeStr 转为rockShapeBit ShapeStrToBit(rockShapeStr, rockShapeBit); rocknext++; RockArray[rockNum].rockShapeBits = rockShapeBit; RockArray[rockNum].nextRockIndex = rocknext; rockNum++; } } } fclose(fp); } //将从文件中读取的字符串(长度默认为16)转换成 unsigned short void ShapeStrToBit(unsigned char *rockShapeStr, unsigned short& rockShapeBit) { rockShapeBit = 0; for (size_t idx = 0; idx < 16; ++idx) { if (rockShapeStr[idx] == '@') //1 { rockShapeBit |= (1 << (16 - idx - 1)); } // #为0 不需要处理 } }
game.h
#include"Head.h" #define _CRT_SECURE_NO_WARNINGS 1 bool MoveAble(int rockIndex, RockLocation_t* currentLocatePtr, int f_direction); void SetGameBoardFlag(int rockIdx, RockLocation_t* curRockLocation); void UserHitKeyBoard(char userHit, int* RockIndex, RockLocation_t* curRockLocation); void FullLine(); void UpdateSocres(int scores); void DelCurLine(int rowIdx); bool IsGameOver(); void PlayGame() { char userHit = 0;//用户敲击键盘 int curRockIndex = 0;//当前方块的rockArray下标 int nextRockIndex = 0;//下次 RockLocation_t curRockLocation; curRockLocation.left = initRockLocation.left; curRockLocation.top = initRockLocation.top; DWORD oldtime = 0; srand((unsigned int)time(NULL)); curRockIndex = rand() % rockTypeNum; nextRockIndex = rand() % rockTypeNum; //画出预览区初始化方块 //在初始位置和预览区显示方块形状 DisplayRock(curRockIndex, &initRockLocation, 1); DisplayRock(nextRockIndex, &preRockLocation, 1); bool moveAbled = false; while (true) { //判断当前方块是否落地(判断能否再下移):如果落地,判断是否满行,再判断是否结束游戏, 改变game_board ,画出下次初始化的方块,以及生成新的预览方块 // moveAbled = MoveAble(curRockIndex, &curRockLocation, DIRECT_DOWN); if (!moveAbled) //判断是否落地,不能下移表示落地 { //修改game_board的值 SetGameBoardFlag(curRockIndex, &curRockLocation); FullLine(); if (IsGameOver()) { MessageBox(NULL, _T("游戏结束"), _T("GAME OVER"), MB_OK); exit(0); } //为下次生成模块开始准备 DisplayRock(nextRockIndex, &preRockLocation, false);//擦除旧的方块 curRockIndex = nextRockIndex; nextRockIndex = rand() % rockTypeNum; //生成新的预览方块 DisplayRock(curRockIndex, &initRockLocation, 1); DisplayRock(nextRockIndex, &preRockLocation, 1); FlushBatchDraw(); //修改curRockLocation的值 curRockLocation.left = initRockLocation.left; curRockLocation.top = initRockLocation.top; } if (kbhit()) //如果敲击键盘了 就处理按键 { userHit = getch(); UserHitKeyBoard(userHit, &curRockIndex, &curRockLocation); } //没有 就自动下移一个单位 :不能用else,因为可能按键不是上下左右 DWORD newtime = GetTickCount(); if (newtime - oldtime >= (unsigned int)(300) && moveAbled == TRUE) { oldtime = newtime; DisplayRock(curRockIndex, &curRockLocation, false); curRockLocation.top += ROCK_SQUARE_WIDTH; //下落一格 } //AutomaticDownMove(curRockIndex, &curRockLocation); //画出新方块 DisplayRock(curRockIndex, &curRockLocation, 1); FlushBatchDraw(); Sleep(20); } } //响应键盘命令时间 void UserHitKeyBoard(char userHit, int* RockIndex, RockLocation_t* curRockLocation) { switch (userHit) { case 'W': case 'w'://↑ if (MoveAble(RockArray[*RockIndex].nextRockIndex, curRockLocation, DIRECT_UP)) { DisplayRock(*RockIndex, curRockLocation, false); *RockIndex = RockArray[*RockIndex].nextRockIndex; } break; case 'S': case 's'://↓ if (MoveAble(*RockIndex, curRockLocation, DIRECT_DOWN)) { DisplayRock(*RockIndex, curRockLocation, false); curRockLocation->top += 2 * (ROCK_SQUARE_WIDTH); if (!MoveAble(*RockIndex, curRockLocation, DIRECT_DOWN)) { curRockLocation->top -= ROCK_SQUARE_WIDTH; } } break; case 'A': case 'a': //← if (MoveAble(*RockIndex, curRockLocation, DIRECT_LEFT)) { DisplayRock(*RockIndex, curRockLocation, false); curRockLocation->left -= ROCK_SQUARE_WIDTH; } break; case 'D': case 'd': //→ if (MoveAble(*RockIndex, curRockLocation, DIRECT_RIGHT)) { DisplayRock(*RockIndex, curRockLocation, FALSE); curRockLocation->left += ROCK_SQUARE_WIDTH; } break; case ' ': //暂停 while (1) { userHit = getch(); if (userHit == ' ') break; } break; default: break; } } //判断是否满行,满行消除,然后计算得分 void FullLine() { bool linefull = true; int idx = yROCK_SQUARE_NUM;//从最后一行往上查找 22 int count = 0; while (count != xROCK_SQUARE_NUM ) //遇到空行 14 { linefull = true; count = 0; for (int i = 1; i <= xROCK_SQUARE_NUM; ++i) { if (game_board[idx][i] == 0) { linefull = false; count++; } } if (linefull) //满行,消除当前行,更新分数 { DelCurLine(idx); game_socres += 3; UpdateSocres(game_socres); idx++;//因为下面要减1 } idx--; } } void UpdateSocres(int scores) { setcolor(RED); TCHAR s[10]; _stprintf(s, _T("%d"), scores); outtextxy(GAME_WALL_WIDTH + 90, 200, s); } //消除当前行 void DelCurLine(int rowIdx) { //擦除当前行 setcolor(BLACK); setfillcolor(BLACK); for (int i = 1; i < xROCK_SQUARE_NUM; ++i) { fillrectangle(WALL_SQUARE_WIDTH + (i - 1)*ROCK_SQUARE_WIDTH, (rowIdx - 1)*ROCK_SQUARE_WIDTH + WALL_SQUARE_WIDTH, WALL_SQUARE_WIDTH + i*ROCK_SQUARE_WIDTH, rowIdx*ROCK_SQUARE_WIDTH + WALL_SQUARE_WIDTH); } //把上面的向下移 int cnt = 0; while (cnt != xROCK_SQUARE_NUM) //直到遇到是空行的为止 { cnt = 0; for (int i = 1; i <= xROCK_SQUARE_NUM; i++) { game_board[rowIdx][i] = game_board[rowIdx - 1][i]; //擦除上面的一行 setcolor(BLACK); setfillcolor(BLACK); fillrectangle(WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*i - ROCK_SQUARE_WIDTH , WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*(rowIdx - 1) - ROCK_SQUARE_WIDTH , WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*i, WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*(rowIdx - 1)); //显示下面的一行 if (game_board[rowIdx][i] == 1) { setcolor(WHITE); setfillcolor(RED); fillrectangle(WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*i - ROCK_SQUARE_WIDTH , WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*rowIdx - ROCK_SQUARE_WIDTH , WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*i, WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*rowIdx); } if (game_board[rowIdx][i] == 0) cnt++; //统计一行是不是 都是空格 }//for rowIdx--; } } //是否可以移动方块 bool MoveAble(int rockIndex, RockLocation_t* currentLocatePtr, int f_direction) { int mask; int rockX; int rockY; rockX = currentLocatePtr->left; rockY = currentLocatePtr->top; mask = (unsigned short)1 << 15; for (int i = 1; i <= 16; i++) { //与掩码相与为1的 即为方块上的点 if ((RockArray[rockIndex].rockShapeBits & mask) != 0) { //判断能否移动(即扫描即将移动的位置 是否与设置的围墙有重叠) //若是向上(即翻滚变形) if (f_direction == DIRECT_UP) { //因为此情况下传入的是下一个方块的形状,故我们直接判断此方块的位置是否已经被占 //判断下一个方块 if (game_board[(rockY - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] [(rockX - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] == 1) return false; } //如果是向下方向移动 else if (f_direction == DIRECT_DOWN) { if (game_board[(rockY - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 2] [(rockX - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] == 1) return false; } else //如果是左右方向移动 { //f_direction的DIRECT_LEFT为-1,DIRECT_RIGHT为1,故直接加f_direction即可判断。 if (game_board[(rockY - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] [(rockX - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1 + f_direction] == 1) return false; } } //每4次 换行 转到下一行继续 i % 4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = currentLocatePtr->left) : rockX += ROCK_SQUARE_WIDTH; mask >>= 1; } return true; } //给游戏game_board设置标记表示已经占了 void SetGameBoardFlag(int rockIdx, RockLocation_t* curRockLocation) { int mask; int rockX; int rockY; rockX = curRockLocation->left; rockY = curRockLocation->top; mask = (unsigned int)1 << 15; for (int i = 1; i <= 16; i++) { //与掩码相与为1的 即为方块上的点 if ((RockArray[rockIdx].rockShapeBits & mask) != 0) { game_board[(rockY - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] [(rockX - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] = 1; } //每4次 换行 转到下一行继续画 i % 4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = curRockLocation->left) : rockX += ROCK_SQUARE_WIDTH; mask >>= 1; } } //判断游戏是否结束 bool IsGameOver() { bool topLineHaveRock = false;//顶行是否有方块 bool bottomLineHaveRock = false; for (int i = 1; i < xROCK_SQUARE_NUM; ++i) { if (game_board[1][i] == 1) topLineHaveRock = true; if (game_board[yROCK_SQUARE_NUM][i] == 1) bottomLineHaveRock = true; } if (bottomLineHaveRock && topLineHaveRock) return true; else return false; }
main.cpp
#include"Head.h" #include"Draw.h" #include"Init.h" #include"game.h" int main() { initgraph(WINDOW_WIDTH,WINDOW_HIGH); DrawGameWindow(); //使用 API 函数修改窗口名称 HWND hWnd = GetHWnd(); SetWindowText(hWnd, _T("俄罗斯方块")); InitGame(); PlayGame(); getchar(); closegraph(); system("pause"); return 0; }
配置文件:RockShape.ini
@### @### @@## #### @@@# @### #### #### @@## #@## #@## #### ##@# @@@# #### #### **** #@## #@## @@## #### @### @@@# #### #### @@## @### @### #### @@@# ##@# #### #### **** #@## @@@# #### #### @### @@## @### #### @@@# #@## #### #### #@## @@## #@## #### **** #@## @@## @### #### @@## #@@# #### #### **** @### @@## #@## #### #@@# @@## #### #### **** @### @### @### @### @@@@ #### #### #### **** @@## @@## #### #### ****
更多关于俄罗斯方块的文章,请点击查看专题:《俄罗斯方块》