C语言实现俄罗斯方块

    技术2022-07-11  74

    C语言实现俄罗斯方块

    思路方块旋转光标定位方块的移动方块非法动作取消方块生命消亡检查消除一行 完整代码

    思路

    我设计时,思路历程:

    方块如何旋转光标定位方块的移动方块非法动作取消方块生命消亡检查消除一行

    方块旋转

    一开始想到的笨方法,就是将方块整体融入一个九宫格矩阵中,旋转即使行列进行数据调换,这就需要创建一个3*3的数组以及一个定位坐标作为成员变量的结构体数组,由于太笨,放弃。 观察到旋转变换与角度有关,于是将方块某一点,视为极点,进行坐标运算,旋转即是θ-π/2,计算结果很简单: 逆时针:x=y ,y=-x; 顺时针:x=-y,y=x; 然后再转换为控制台上坐标系上的坐标,即加上极点对应的横纵坐标即可。 ==注意:算的时候,要对应上数轴的正方向==

    光标定位

    本人也没过多理解,如何定位,直接拿来用了。大致就是获取控制台的标准输出句柄,然后传入一个坐标结构体参数给它。 代码片.

    //将光标设置跳转到具体坐标 void gotoxy(int x, int y) { COORD coord; coord.X = x; coord.Y = y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); }

    方块的移动

    前面的实现了,这个方块移动就不难了,就是消除前一个足迹,然后打印新的。然后要注意的是一个方块符"■",在很坐标上占两个字宽,所以坐标的偏移上要*2

    /* 方块动作执行 参数: flag int 类型 ANTICLOCKWISE :逆时针旋转 CLOCKWISE :顺时针旋转 MOVE_L :左移 MOVE_R : 右移 MOVE_U:上移 MOVE_D : 下移 */ void action(int flag) { //逆时针旋转 if (flag == ANTICLOCKWISE) { for (int i = 0; i < 4; i++) { if (i == 2)continue;//轴心,可有可无 int x = tetris[2].x + tetris[i].y - tetris[2].y; int y = tetris[2].x + tetris[2].y - tetris[i].x; tetris[i].x = x; tetris[i].y = y; } } //顺时针旋转 else if (flag == CLOCKWISE) { for (int i = 0; i < 4; i++) { if (i == 2)continue;//轴心,可有可无 int x = tetris[2].x + tetris[2].y - tetris[i].y; int y = tetris[i].x + tetris[2].y - tetris[2].x; tetris[i].x = x; tetris[i].y = y; } } //向左移动 else if (flag == MOVE_L) { for (int i = 0; i < 4; i++) { tetris[i].x += MOVE_L; } } //向右移动 else if (flag == MOVE_R) { for (int i = 0; i < 4; i++) { tetris[i].x += MOVE_R; } } else if (flag == MOVE_U) { //向上移动 for (int i = 0; i < 4; i++) { tetris[i].y += -1; } } else if (flag == MOVE_D) { //向下移动 for (int i = 0; i < 4; i++) { tetris[i].y += 1; } } }

    方块非法动作取消

    方块的非法动作行为如:超出活动边界以及碰触到“前人遗骸”。实现是先让动作执行,再检测方块是否越界,是的话,逆做动作还原。

    //行为异常进行反转、取消前动作 if (true ==sign) { switch (temp) { case UP: action(-1 * CLOCKWISE); break; case LEFT: action(-1 * MOVE_L); break; case RIGHT: action(-1 * MOVE_R); break; default: action(-1 * MOVE_D); break; } }

    有点不想写了

    方块生命消亡检查

    生命检查,就是通过先判断上一个方块动作是否为下移,在这个前提下,判断方块坐标是否触及地图的非活动区域(即已经有方块存在了)

    //死亡行为检查 if (map[tetris[i].y][tetris[i].x] == 1) { sign = true; if (temp != UP && temp != LEFT && temp != LEFT && temp != RIGHT) status = DEAD; break; }

    消除一行

    消除一行需要检查是否存在满足消除条件,需要对全地图遍历,同时消除后,还需对地图更正,填充消除的一行,集体下移。

    /* 检查是否可以消行 */ void updateMap() { int record[HEIGTH] = {0}; //记录满行 行下标 int top = 0,count; // top 记录栈顶,最小为0,0代表没有记录 ** count 计数 for (int i = HEIGTH - 2; i > 0; i--) { count = 0; for (int j = 1; j < WIDTH - 1; j++) { if (map[i][j] == 1) { count++; } else break; } if (count == WIDTH - 2) record[top++] = i; } score = score + top * 10; //计算分值 //top大于零,则取出栈顶元素 ---被消除的行下标 while (top > 0) { for (int i = record[top-1]; i > 1; i--) { for (int j = 0; j < WIDTH - 1; j++) { map[i][j] = map[i - 1][j]; } } system("cls"); //完全清屏 top--; } }

    完整代码

    #include <stdio.h> #include <stdlib.h> #include <Windows.h>//windows编程头文件 #include <time.h> #include <conio.h>//控制台输入输出头文件 #define CLOCKWISE 2 //顺时针旋转 #define ANTICLOCKWISE -2 //逆时针旋转 #define MOVE_L -1 // 左移 #define MOVE_R 1 // 右移 #define MOVE_U -3 //上移 #define MOVE_D 3 //下移 #define DEAD 0 //方块死亡 #define LIVE 1 //方块存活 #define UP 72 //向上键 ** 旋转方块 #define DOWN 80 //快速到底 #define LEFT 75 //向左移动 #define RIGHT 77 //向右移动 #define WIDTH 20 //地图长度 #define HEIGTH 30 //地图宽度 #define NORMAL_SPEED 200 //正常速度 #define GAME_OVER 0 //游戏结束 #define PLAY_GAME 1 //游戏开始 void action(int flag); void gotoxy(int x, int y); void draw_block(); void get_direction(); void play(); void update(int dir); void draw_map(); void init(); void createBlock(); bool check(); void clean(); void updateMap(); struct { int x; int y; }tetris[4] = { { 1,WIDTH/2 },{ 1,WIDTH / 2 },{ 1,WIDTH / 2 },{1,WIDTH / 2 } }; unsigned char direction=0; int map[HEIGTH][WIDTH]{0}; int speed= NORMAL_SPEED; bool status = DEAD; int score = 0; bool gameStatus = PLAY_GAME; int main() { //获取标准输入句柄 HANDLE fd = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cinfo; cinfo.bVisible = 0; //将光标设置为不可见 cinfo.dwSize = 1; SetConsoleCursorInfo(fd, &cinfo); //初始化 init(); while (1) { play(); if (GAME_OVER == gameStatus) { system("cls"); gotoxy(WIDTH / 2, HEIGTH / 2); printf("游戏结束,您的得分:%d", score); gotoxy(WIDTH / 2, HEIGTH / 2+1); printf("按回车结束"); getchar(); break; } } return 0; } /** *控制台按键所代表的数字 *“↑”:72 *“↓”:80 *“←”:75 *“→”:77 */ //将光标设置跳转到具体坐标 void gotoxy(int x, int y) { COORD coord; coord.X = x; coord.Y = y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); } /* 绘制方块 */ void draw_block() { for (int i = 0; i < 4; i++) { gotoxy(tetris[i].x*2, tetris[i].y); printf("■"); } gotoxy(0, 0); //将光标移到远处,防止输入其他字符影响 } /* 获取输入的方向 上:旋转 下:快速到底 左:左移 右:右移 */ void get_direction() { if (_kbhit())//如果用户按下了键盘中的某个键 { //清空缓冲区 rewind(stdin); //getch()读取方向键的时候,要读取两次,第一次调用返回0或者224,第二次调用返回的才是实际值 direction = _getch();//第一次调用返回的不是实际值 if (direction != 0 || direction != 224 || direction ==EOF) return; //非方向键按下会产生干扰 direction = _getch();//第二次调用返回实际值 rewind(stdin); } } /* 开始游戏 */ void play() { clean(); //清除方块足迹 get_direction(); //获取用户选择 if (!check()) { //检查方块状态行为 createBlock(); status = LIVE; draw_map(); } draw_map(); draw_block(); Sleep(speed); } /* 更新数据 */ void update(int dir) { switch (dir) { case UP: action(CLOCKWISE); break; case LEFT: action(MOVE_L); break; case RIGHT: action(MOVE_R); break; case DOWN: speed = 0; default: // 默认下移 action(MOVE_D); break; } direction = 0; } /* 绘制地图 */ void draw_map() { for (int i = 0; i < HEIGTH; i++) { for (int j = 0; j < WIDTH; j++) { if (map[i][j] == 1) { gotoxy(j*2, i); printf("■"); } } } } /* 初始化 设置边界 创建方块、地图数据模型 设置状态:live */ void init() { for (int j = 0; j < WIDTH; j++) { map[0][j] = 1; map[HEIGTH - 1][j] = 1; } for (int i = 0; i < HEIGTH; i++) { map[i][0] = 1; map[i][WIDTH - 1] = 1; } createBlock(); status = LIVE; } /* 创建方块 随机数生成:0-3 之间的数 赋值四种类型之一的数据模型 方向初始化为:0 速度初始化为:正常速度 NORMAL_SPEED 200 */ void createBlock() { srand((unsigned int)time(NULL)); int type = rand() % 4; switch (type) { case 0: tetris[0] = { WIDTH / 2 - 1 ,1}; tetris[1] = { WIDTH / 2 - 1 ,2}; tetris[2] = { WIDTH / 2 ,2}; tetris[3] = { WIDTH / 2+1 ,2 }; break; case 1: tetris[0] = { WIDTH / 2 ,1 }; tetris[1] = { WIDTH / 2 +1 ,1 }; tetris[2] = { WIDTH / 2 ,2 }; tetris[3] = { WIDTH / 2 +1 ,2 }; break; case 2: tetris[0] = { WIDTH / 2 - 1 , 2 }; tetris[1] = { WIDTH / 2 , 1 }; tetris[2] = { WIDTH / 2 , 2 }; tetris[3] = { WIDTH / 2 +1 , 2 }; break; case 3: tetris[0] = { WIDTH / 2 - 2 , 2 }; tetris[1] = { WIDTH / 2 - 1 , 2 }; tetris[2] = { WIDTH / 2 , 2 }; tetris[3] = { WIDTH / 2 + 1 , 2 }; break; default: break; } direction = 0; speed = NORMAL_SPEED; } /* 检查方块行为、检查游戏状态 参数:无 返回: true LIVE 方块存活; false DEAD 方块死亡; */ bool check() { int temp = direction; update(temp); bool sign=false;//行为取消标识 for (int i = 0; i < 4; i++) { //碰壁行为检查 if (tetris[i].y == 0 || tetris[i].x == 0 || tetris[i].x ==WIDTH-1) { sign = true; break; } //死亡行为检查 if (map[tetris[i].y][tetris[i].x] == 1) { sign = true; if (temp != UP && temp != LEFT && temp != LEFT && temp != RIGHT) status = DEAD; break; } } //行为异常进行反转、取消前动作 if (true ==sign) { switch (temp) { case UP: action(-1 * CLOCKWISE); break; case LEFT: action(-1 * MOVE_L); break; case RIGHT: action(-1 * MOVE_R); break; default: action(-1 * MOVE_D); break; } } //方块死亡,将数据加入地图数据模型 if (DEAD == status) { for (int i = 0; i < 4; i++) { map[tetris[i].y][tetris[i].x] = 1; } } updateMap(); //更新地图,检查是否存在消行 //检查是否游戏结束 for (int i = 1; i < WIDTH - 1; i++) { if (map[1][i] == 1) { gameStatus = GAME_OVER; break; } } return status; } /* 消除方块足迹 */ void clean() { for (int i = 0; i < 4; i++) { gotoxy(tetris[i].x * 2, tetris[i].y); printf(" "); } } /* 检查是否可以消行 */ void updateMap() { int record[HEIGTH] = {0}; //记录满行 行下标 int top = 0,count; // top 记录栈顶,最小为0,0代表没有记录 ** count 计数 for (int i = HEIGTH - 2; i > 0; i--) { count = 0; for (int j = 1; j < WIDTH - 1; j++) { if (map[i][j] == 1) { count++; } else break; } if (count == WIDTH - 2) record[top++] = i; } score = score + top * 10; //计算分值 //top大于零,则取出栈顶元素 ---被消除的行下标 while (top > 0) { for (int i = record[top-1]; i > 1; i--) { for (int j = 0; j < WIDTH - 1; j++) { map[i][j] = map[i - 1][j]; } } system("cls"); //完全清屏 top--; } } /* 方块动作执行 参数: flag int 类型 ANTICLOCKWISE :逆时针旋转 CLOCKWISE :顺时针旋转 MOVE_L :左移 MOVE_R : 右移 MOVE_U:上移 MOVE_D : 下移 */ void action(int flag) { //逆时针旋转 if (flag == ANTICLOCKWISE) { for (int i = 0; i < 4; i++) { if (i == 2)continue;//轴心,可有可无 int x = tetris[2].x + tetris[i].y - tetris[2].y; int y = tetris[2].x + tetris[2].y - tetris[i].x; tetris[i].x = x; tetris[i].y = y; } } //顺时针旋转 else if (flag == CLOCKWISE) { for (int i = 0; i < 4; i++) { if (i == 2)continue;//轴心,可有可无 int x = tetris[2].x + tetris[2].y - tetris[i].y; int y = tetris[i].x + tetris[2].y - tetris[2].x; tetris[i].x = x; tetris[i].y = y; } } //向左移动 else if (flag == MOVE_L) { for (int i = 0; i < 4; i++) { tetris[i].x += MOVE_L; } } //向右移动 else if (flag == MOVE_R) { for (int i = 0; i < 4; i++) { tetris[i].x += MOVE_R; } } else if (flag == MOVE_U) { //向上移动 for (int i = 0; i < 4; i++) { tetris[i].y += -1; } } else if (flag == MOVE_D) { //向下移动 for (int i = 0; i < 4; i++) { tetris[i].y += 1; } } }
    Processed: 0.011, SQL: 9