当前位置: 首页 > news >正文

用c语言完成俄罗斯方块小游戏

用c语言完成俄罗斯方块小游戏

这估计是你在编程学习过程中的第一个小游戏开发,怎么说呢,在这里只针对刚学程序设计的学生,就是说刚接触C语言没多久,有一点功底的学生看看,简陋的代码,简陋的实现,

by:BoBoo文睡不醒

注:该代码内注释加入个人理解,若有错误,多多包涵

由于不需要插件,绘制出来的玩意恐怕不够美观,多多理解

语言:C的不能再C。(没涉及啥很高深的玩意)

(高级一点的话用EasyX插件里面的玩意,那就很炫酷了)

涉及内容:光标移动,颜色,图形绘制、消去、覆盖和打印,键盘按键敲击的读取,计时器,文件的创建与读取

看似简单,实际上一点也不难

在这里插入图片描述

1. 游戏概述

俄罗斯方块是一款经典的益智游戏,主要功能包括:

  • 显示游戏界面
  • 随机生成方块并控制其移动和旋转
  • 消除完整行
  • 记录并保存最高分
  • 游戏结束判断

本代码使用控制台操作,并通过系统函数控制光标位置、打印字符实现图形效果。


2. 游戏开发所需知识

在实现类似游戏之前,需掌握以下知识:

  1. 控制台操作:控制光标位置 (CursorJump) 和隐藏光标 (HideCursor)。
  2. 颜色设置:通过SetConsoleTextAttribute改变文本颜色。
  3. 二维数组操作:表示游戏区域与方块信息。
  4. 随机数生成:使用rand()生成随机方块。
  5. 键盘事件:读取按键事件(如_kbhit_getch)。
  6. 文件操作:保存和读取游戏历史最高分。

3. 代码解读

3.1 数据结构

Face
  • 表示整个游戏界面。
  • data: 标记某处是否有方块(0无,1有)。
  • color: 存储方块颜色信息。
Block
  • 存储7种方块的4种形态。
  • 通过二维数组space表示方块形状。
c复制代码struct Face {int data[ROW][COL + 10];int color[ROW][COL + 10];
};struct Block {int space[4][4];
} block[7][4];

3.2 核心模块

(1) 界面初始化

使用InitInterface绘制边框与提示信息:

  • 通过CursorJump设置光标位置。
  • 使用printf("■")绘制边框。
  • 边框信息存入face.data
c复制代码void InitInterface() {color(7);for (int i = 0; i < ROW; i++) {for (int j = 0; j < COL + 10; j++) {if (j == 0 || j == COL - 1 || j == COL + 9) { face.data[i][j] = 1; CursorJump(2 * j, i);printf("■");}}}
}

(2) 方块初始化

InitBlockInfo存储方块的7种形状和4种旋转状态:

  • 每种形状的基本状态由block[shape][0]定义。
  • 使用二维旋转公式生成其余三种形态。
c复制代码for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {block[shape][form + 1].space[i][j] = temp[3 - j][i];}
}

(3) 方块绘制与移动
  • DrawBlock:在指定位置打印方块。
  • DrawSpace:用空格覆盖指定位置,实现“擦除”效果。
c复制代码void DrawBlock(int shape, int form, int x, int y) {for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (block[shape][form].space[i][j] == 1 && y + i > 0) {CursorJump(2 * (x + j), y + i);printf("■");}}}
}

(4) 键盘事件处理

通过_getch检测按键:

  • LEFT:左移
  • RIGHT:右移
  • DOWN:快速下落
  • SPACE:旋转
c复制代码switch (ch) {case LEFT:if (IsLegal(shape, form, x - 1, y)) x--;break;case RIGHT:if (IsLegal(shape, form, x + 1, y)) x++;break;case SPACE:if (IsLegal(shape, (form + 1) % 4, x, y)) form = (form + 1) % 4;break;
}

(5) 行清除与得分判断

JudeFunc实现:

  • 检查行是否满。
  • 若满,则消除行并将上方行下移。
  • 更新得分。
c复制代码if (sum == COL - 2) { grade += 10;for (int j = 1; j < COL - 1; j++) {face.data[i][j] = 0;CursorJump(2 * j, i);printf("  ");}
}

(6) 游戏逻辑与结束判断

StartGame控制方块下落、键盘响应与结束判断:

  • 随机生成方块并控制其运动。
  • 检测顶层是否有方块,若有则结束游戏。

(7) 文件操作

ReadGradeWriteGrade负责读取和保存最高分:

  • 通过fopen打开文件,保存最高分。
  • 使用二进制方式存储成绩,防止文本存储带来的额外处理。
c复制代码FILE* pf = fopen("record.txt", "r");
fread(&max, sizeof(int), 1, pf);

4. 编写俄罗斯方块的步骤

  1. 明确需求
    • 游戏逻辑(下落、移动、旋转、消除)。
    • 数据存储(方块形态、游戏区域)。
    • 图形展示(控制台或GUI)。
  2. 搭建基础框架
    • 初始化游戏区域、边框与方块。
    • 定义随机生成与旋转逻辑。
  3. 逐步完善
    • 实现消除与得分逻辑。
    • 增加键盘事件处理。
    • 添加文件操作功能。
  4. 优化与美化
    • 增加动画效果。
    • 提高界面美观度(如使用插件或图形库)。

通过本代码,你可以学习到俄罗斯方块的实现核心逻辑,同时可以在此基础上尝试更高级的图形化实现,如使用EasyX插件或其他图形库,进一步提升游戏的视觉体验和功能丰富性。

您说:

对每一步进行详细的介绍

ChatGPT 说:

ChatGPT

编写俄罗斯方块游戏的详细步骤与说明

本文将从代码结构与逻辑出发,逐步拆解如何实现一个俄罗斯方块游戏,帮助理解并指导如何从零开始构建类似的游戏。


1. 明确需求与设计思路

俄罗斯方块的基本玩法包括以下几部分:

  1. 随机生成方块:从7种形状中随机生成一个方块,并在游戏区域顶部下落。
  2. 方块运动控制:通过键盘实现左右移动、加速下落和旋转操作。
  3. 碰撞检测与消行:检测方块是否碰到其他方块或到达底部,并判断是否需要消行。
  4. 得分与游戏结束:计算得分,判断是否达到游戏结束条件。
  5. 数据持久化:保存和读取最高分记录。

设计中还需考虑:

  • 游戏的界面绘制。
  • 使用二维数组存储游戏区域状态。
  • 持续的方块生成与处理逻辑。

2. 搭建基础框架

2.1 确定游戏区域和方块结构

俄罗斯方块的核心在于使用二维数组存储数据状态:

  • Face
    

    结构体:存储整个游戏区域状态。

    • data数组:记录每个位置是否有方块。
    • color数组:记录每个位置的颜色。
  • Block结构体:存储所有方块的形状及其4种旋转状态。

c复制代码#define ROW 29 // 游戏区域行数
#define COL 20 // 游戏区域列数struct Face {int data[ROW][COL + 10]; // 游戏区域方块标记int color[ROW][COL + 10]; // 方块颜色
};struct Block {int space[4][4]; // 存储方块形状
} block[7][4]; // 7种形状,每种4种状态

2.2 初始化游戏区域与边框

绘制游戏界面的边框,展示基本信息(如得分、提示):

  1. 利用双重循环,遍历所有格子。
  2. 使用特定符号(如)绘制边框。
  3. 在右侧区域打印提示文字。

在这里插入图片描述
在这里插入图片描述

代码实现:

c复制代码void InitInterface() {color(7); // 设置边框颜色为白色for (int i = 0; i < ROW; i++) {for (int j = 0; j < COL + 10; j++) {if (j == 0 || j == COL - 1 || j == COL + 9) {face.data[i][j] = 1; // 边框标记为有方块CursorJump(2 * j, i);printf("■");} else if (i == 0 || i == ROW - 1) { face.data[i][j] = 1; // 顶部和底部边框printf(" ■");} else {face.data[i][j] = 0; // 非边框区域标记为空}}}// 打印右侧提示信息CursorJump(2 * COL, 1);printf("下一个方块:");CursorJump(2 * COL + 4, ROW - 3);printf("当前分数:%d", grade);
}

3. 初始化方块形状与逻辑

3.1 定义方块形状

俄罗斯方块共有7种形状,每种形状有4种旋转状态。通过二维数组定义每种形状的初始状态,并通过旋转公式生成其他状态。

代码实现:

c复制代码void InitBlockInfo() {// 定义“T”形for (int i = 0; i <= 2; i++) block[0][0].space[1][i] = 1;block[0][0].space[2][1] = 1;// 定义其他形状...// 通过旋转公式生成每种形状的其他3种状态for (int shape = 0; shape < 7; shape++) {for (int form = 0; form < 3; form++) {for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {block[shape][form + 1].space[i][j] = block[shape][form].space[3 - j][i];}}}}
}

4. 方块的运动控制

4.1 键盘事件检测

通过_kbhit检测键盘输入,并用_getch捕获按键值:

  • LEFT:左移。
  • RIGHT:右移。
  • DOWN:加速下落。
  • SPACE:旋转。

实现示例:

c复制代码char ch = getch();
switch (ch) {case LEFT:if (IsLegal(shape, form, x - 1, y)) x--;break;case RIGHT:if (IsLegal(shape, form, x + 1, y)) x++;break;case SPACE:if (IsLegal(shape, (form + 1) % 4, x, y)) form = (form + 1) % 4;break;case ESC:exit(0); // 退出游戏
}

4.2 碰撞检测

使用IsLegal函数判断方块是否可以移动或旋转:

  • 检查方块每个格子是否会越界或与已有方块重叠。
  • 返回1表示合法,0表示不合法。
c复制代码int IsLegal(int shape, int form, int x, int y) {for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (block[shape][form].space[i][j] == 1 &&(x + j < 0 || x + j >= COL || y + i >= ROW || face.data[y + i][x + j] == 1)) {return 0; // 非法}}}return 1; // 合法
}

5. 方块落地与消行

5.1 方块落地

每次方块下落时:

  • 检查其下一步是否越界或碰到其他方块。
  • 如果不能继续下落,则将当前方块写入Face结构。

5.2 行满判断与消除

当某一行填满时:

  1. 删除该行并将上方所有行下移。
  2. 更新分数。

代码实现:

c复制代码int JudeFunc() {for (int i = ROW - 2; i > 4; i--) {int sum = 0;for (int j = 1; j < COL - 1; j++) sum += face.data[i][j];if (sum == COL - 2) { // 满行grade += 10;for (int j = 1; j < COL - 1; j++) {face.data[i][j] = 0;CursorJump(2 * j, i);printf("  ");}for (int m = i; m > 1; m--) { // 上方行下移for (int n = 1; n < COL - 1; n++) {face.data[m][n] = face.data[m - 1][n];face.color[m][n] = face.color[m - 1][n];}}}}return 0;
}

6. 游戏结束与得分记录

6.1 游戏结束判断

若方块到达顶层,则游戏结束,提示玩家是否重开一局。

6.2 保存与读取最高分

通过文件操作保存最高分:

  • 使用fopen以只读或写模式打开文件。
  • 利用fwrite保存分数。

代码实现:

c复制代码void ReadGrade() {FILE* pf = fopen("record.txt", "r");if (pf == NULL) {pf = fopen("record.txt", "w");fwrite(&grade, sizeof(int), 1, pf);}fread(&max, sizeof(int), 1, pf);fclose(pf);
}

7. 整合与优化

  1. 游戏主循环: 控制方块生成、运动、碰撞和消除,维护游戏逻辑流。
  2. 视觉优化: 增加更多颜色和图案设计,使游戏界面更美观。

以上是详细的游戏开发步骤,你可以在此基础上进行个性化扩展,比如增加背景音乐、提升图形效果,或者引入难度等级功能。

c(下面的代码经过了详细的注释,希望大家慢慢研究)

//作品:tetris(带注释)
// by:BoBoo文睡不醒
//注:该代码内注释加入个人理解,若有错误,多多包涵
//由于不需要插件,绘制出来的玩意恐怕不够美观,多多理解
//语言:C的不能再C。(没涉及啥很高深的玩意)(高级一点的话用EasyX插件里面的玩意,那就很炫酷了)
//涉及内容:光标移动,颜色,图形绘制、消去、覆盖和打印,键盘按键敲击的读取,计时器,文件的创建与读取
//看似简单,实际上一点也不难//头文件部分:
#include <stdio.h>
#include <Windows.h>
//提供了一些在控制台程序中进行输入输出、控制光标位置、设置文本属性等方面的函数和常量
//在代码中使用了其中的一些函数比如SetConsoleCursorPosition()、SetConsoleTextAttribute()来控制光标位置和设置文本颜色
#include <stdlib.h>
//这个头文件包含了一些常用的函数和类型定义,用于进行内存分配、进程控制、字符串转换等操作
#include <time.h>
//这个头文件包含了一些与时间相关的函数和类型定义
#include <conio.h>
//于检测键盘输入字符,以及在控制台上进行一些简单的控制,比如隐藏光标等
//在代码中用于于检测键盘输入,比如判断是否按下了特定的方向键或者空格键,从而控制俄罗斯方块的移动、旋转等操作。//宏定义
#define ROW 29 //游戏区行数
#define COL 20 //游戏区列数
//接下来说明敲击键盘该按键的值
#define DOWN 80 //方向键:下
#define LEFT 75 //方向键:左
#define RIGHT 77 //方向键:右
#define SPACE 32 //空格键
#define ESC 27 //Esc键//两个结构体
struct Face
{int data[ROW][COL + 10]; //用于标记指定位置是否有方块(1为有,0为无)int color[ROW][COL + 10]; //用于记录指定位置的方块颜色编码
}face;struct Block
{int space[4][4];
}block[7][4]; //用于存储7种基本形状方块的各自的4种形态的信息,共28种//函数部分?函数。
//隐藏光标
void HideCursor();
//光标跳转
void CursorJump(int x, int y);
//初始化界面
void InitInterface();
//初始化方块信息
void InitBlockInfo();
//颜色设置
void color(int num);
//画出方块
void DrawBlock(int shape, int form, int x, int y);
//空格覆盖
void DrawSpace(int shape, int form, int x, int y);
//合法性判断
int IsLegal(int shape, int form, int x, int y);
//判断得分与结束
int JudeFunc();
//游戏主体逻辑函数
void StartGame();
//从文件读取最高分
void ReadGrade();
//更新最高分到文件
void WriteGrade();//全局变量
int max, grade;//主函数
int main()
{
#pragma warning (disable:4996) //消除警告max = 0, grade = 0; //初始化变量system("title 俄罗斯方块"); //设置cmd窗口的名字system("mode con lines=29 cols=60"); //设置cmd窗口的大小HideCursor(); //隐藏光标ReadGrade(); //从文件读取最高分到max变量	InitInterface(); //初始化界面InitBlockInfo(); //初始化方块信息srand((unsigned int)time(NULL)); //设置随机数生成的起点StartGame(); //开始游戏return 0;
}//关于隐藏光标的操作(不懂他在干啥)(估计就是隐藏光标)
void HideCursor()
{CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量curInfo.dwSize = 1;  //如果没赋值的话,隐藏光标无效curInfo.bVisible = FALSE; //将光标设置为不可见HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
}
//为了解清晰其中,关键结构CONSOLE_CURSOR_INFO在其头文件当中的内容如下:
// (自己去查,我也不知道)//光标跳转(在屏幕上进行输出时,我们需要光标先移动到目标位置再进行输出,因此,光标跳转函数也是必不可少的)
void CursorJump(int x, int y)
{COORD pos; //定义光标位置的结构体变量pos.X = x; //横坐标设置pos.Y = y; //纵坐标设置HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄SetConsoleCursorPosition(handle, pos); //设置光标位置
}//初始化界面
//主界面的外层的边框打印
void InitInterface()
{color(7); //颜色设置为白色for (int i = 0; i < ROW; i++){for (int j = 0; j < COL + 10; j++){if (j == 0 || j == COL - 1 || j == COL + 9){face.data[i][j] = 1; //标记该位置有方块CursorJump(2 * j, i);printf("■");}else if (i == ROW - 1 || i == 0)//首末两行边框的打印{face.data[i][j] = 1; //标记该位置有方块printf(" ■");//此处予以说明,为了整齐起见,每行方块之间加入空格。下方代码同理}elseface.data[i][j] = 0; //标记该位置无方块}}for (int i = COL; i < COL + 10; i++)//右侧边框分隔线{face.data[8][i] = 1; //标记该位置有方块CursorJump(2 * i, 8);printf("■");}//从这里开始就是写右侧边框中的文字与提示语句(无需细看,无脑打印,想整什么活都可以整)CursorJump(2 * COL, 1);printf("下一个方块:");CursorJump(2 * COL + 4, ROW - 19);printf("左移:←");CursorJump(2 * COL + 4, ROW - 17);printf("右移:→");CursorJump(2 * COL + 4, ROW - 15);printf("加速:↓");CursorJump(2 * COL + 4, ROW - 13);printf("旋转:空格");CursorJump(2 * COL + 4, ROW - 11);printf("暂停: S");CursorJump(2 * COL + 4, ROW - 9);printf("退出: Esc");CursorJump(2 * COL + 4, ROW - 7);printf("重新开始:R");CursorJump(2 * COL + 4, ROW - 5);printf("最高纪录:%d", max);CursorJump(2 * COL + 4, ROW - 3);printf("当前分数:%d", grade);
}//初始化方块信息
//存储各种形状的俄罗斯方块
void InitBlockInfo()
{//“T”形for (int i = 0; i <= 2; i++)block[0][0].space[1][i] = 1;block[0][0].space[2][1] = 1;//“L”形for (int i = 1; i <= 3; i++)block[1][0].space[i][1] = 1;block[1][0].space[3][2] = 1;//“J”形for (int i = 1; i <= 3; i++)block[2][0].space[i][2] = 1;block[2][0].space[3][1] = 1;for (int i = 0; i <= 1; i++){//“Z”形block[3][0].space[1][i] = 1;block[3][0].space[2][i + 1] = 1;//“S”形block[4][0].space[1][i + 1] = 1;block[4][0].space[2][i] = 1;//“O”形block[5][0].space[1][i + 1] = 1;block[5][0].space[2][i + 1] = 1;}//“I”形for (int i = 0; i <= 3; i++)block[6][0].space[i][1] = 1;int temp[4][4];for (int shape = 0; shape < 7; shape++) //7种形状{for (int form = 0; form < 3; form++) //4种形态(已经有了一种,这里每个还需增加3种){//获取第form种形态for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){temp[i][j] = block[shape][form].space[i][j];}}//将第form种形态顺时针旋转,得到第form+1种形态for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){block[shape][form + 1].space[i][j] = temp[3 - j][i];}}}}
}//颜色设置
void color(int c)
{switch (c){case 0:c = 13; //“T”形方块设置为紫色break;case 1:case 2:c = 12; //“L”形和“J”形方块设置为红色break;case 3:case 4:c = 10; //“Z”形和“S”形方块设置为绿色break;case 5:c = 14; //“O”形方块设置为黄色break;case 6:c = 11; //“I”形方块设置为浅蓝色break;default:c = 7; //其他默认设置为白色(若需将颜色重置可把color变为7)break;}SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置//注:SetConsoleTextAttribute是一个API(应用程序编程接口)(from百度,自行了解)
}//画出方块
void DrawBlock(int shape, int form, int x, int y)
{for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){if (block[shape][form].space[i][j] == 1 && y + i > 0) //如果该位置有方块{CursorJump(2 * (x + j), y + i); //光标跳转到指定位置printf("■"); //输出方块}}}//由于不怎么会如何让打印的图形不影响边框(上边框),于是愚蠢的我决定再打印一遍上边框//大家有更好的方法自行尝试color(7);//不加的话第一行的颜色会与图形的颜色相同for (int j = 0; j < COL + 10; j++){if (j == 0 || j == COL - 1 || j == COL + 9){CursorJump(2 * j, 0);printf("■");}else{printf(" ■");}}
}//空格覆盖!!!
void DrawSpace(int shape, int form, int x, int y)
{for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){if (block[shape][form].space[i][j] == 1) //如果该位置有方块{CursorJump(2 * (x + j), y + i); //光标跳转到指定位置printf("  "); //打印空格覆盖(两个空格)}}}
}//合法性判断
int IsLegal(int shape, int form, int x, int y)
{for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){//如果方块落下的位置本来就已经有方块了,则不合法if ((block[shape][form].space[i][j] == 1) && (face.data[y + i][x + j] == 1))return 0; //不合法}}return 1; //合法
}//判断得分与结束
int JudeFunc()
{//判断是否得分for (int i = ROW - 2; i > 4; i--){int sum = 0; //记录第i行的方块个数for (int j = 1; j < COL - 1; j++){sum += face.data[i][j]; //统计第i行的方块个数}if (sum == 0) //该行没有方块,无需再判断其上的层次(无需再继续判断是否得分)break; //跳出循环if (sum == COL - 2) //该行全是方块,可得分{grade += 10; //满一行加10分color(7); //颜色设置为白色CursorJump(2 * COL + 4, ROW - 3); //光标跳转到显示当前分数的位置printf("当前分数:%d", grade); //更新当前分数for (int j = 1; j < COL - 1; j++) //清除得分行的方块信息{face.data[i][j] = 0; //该位置得分后被清除,标记为无方块CursorJump(2 * j, i); //光标跳转到该位置printf("  "); //打印空格覆盖(两个空格)}//把被清除行上面的行整体向下挪一格for (int m = i; m > 1; m--){sum = 0; //记录上一行的方块个数for (int n = 1; n < COL - 1; n++){sum += face.data[m - 1][n]; //统计上一行的方块个数face.data[m][n] = face.data[m - 1][n]; //将上一行方块的标识移到下一行face.color[m][n] = face.color[m - 1][n]; //将上一行方块的颜色编号移到下一行if (face.data[m][n] == 1) //上一行移下来的是方块,打印方块{CursorJump(2 * n, m); //光标跳转到该位置color(face.color[m][n]); //颜色设置为还方块的颜色printf("■"); //打印方块}else //上一行移下来的是空格,打印空格{CursorJump(2 * n, m); //光标跳转到该位置printf("  "); //打印空格(两个空格)}}if (sum == 0) //上一行移下来的全是空格,无需再将上层的方块向下移动(移动结束)return 1; //返回1,表示还需调用该函数进行判断(移动下来的可能还有满行)}}}//判断游戏是否结束for (int j = 1; j < COL - 1; j++){if (face.data[1][j] == 1) //顶层有方块存在(以第1行为顶层,不是第0行){Sleep(1000); //留给玩家反应时间system("cls"); //清空屏幕color(7); //颜色设置为白色CursorJump(2 * (COL / 3), ROW / 2 - 9);if (grade > max)//接下来就是曼妙的人机交互部分{printf("恭喜你打破最高记录,最高记录更新为%d", grade);WriteGrade();}else if (grade == max){printf("与最高记录持平,加油再创佳绩", grade);}else{printf("请继续加油,当前与最高记录相差%d", max - grade);}CursorJump(2 * (COL / 3), ROW / 2 - 6);printf("GAME OVER");while (1){char ch;CursorJump(2 * (COL / 3), ROW / 2 - 3);printf("再来一局?");CursorJump(2 * (COL / 3), ROW / 2);printf("输入y并敲击回车(y/n)进入下一局;");CursorJump(2 * (COL / 3), ROW / 2 + 3);printf("输入n并敲击回车(n/n)结束游戏;");CursorJump(2 * (COL / 3), ROW / 2 + 6);printf("敲击esc则退出游戏;");if (kbhit() != 0){printf("Y");if (getch() == 27){printf("Y");system("cls"); //清空屏幕color(7);CursorJump(COL, ROW / 2);printf("  游戏结束  ");CursorJump(COL, ROW / 2 + 2);exit(0); //结束程序}//疑点:上面的if判断和执行代码无法实现,还未研究明白******}scanf("%c", &ch);if (ch == 'y' || ch == 'Y'){system("cls");main();}else if (ch == 'n' || ch == 'N'){CursorJump(2 * (COL / 3), ROW / 2 + 5);exit(0);}else{CursorJump(2 * (COL / 3), ROW / 2 + 4);printf("选择错误,请再次选择");}}}}return 0; //判断结束,无需再调用该函数进行判断
}//游戏主体逻辑函数!!!
void StartGame()
{int shape = rand() % 7, form = rand() % 4; //随机获取方块的形状和形态while (1){int t = 0;int nextShape = rand() % 7, nextForm = rand() % 4; //随机获取下一个方块的形状和形态int x = COL / 2 - 2, y = 0; //方块初始下落位置的横纵坐标color(nextShape); //颜色设置为下一个方块的颜色DrawBlock(nextShape, nextForm, COL + 3, 3); //将下一个方块显示在右上角while (1){color(shape); //颜色设置为当前正在下落的方块DrawBlock(shape, form, x, y); //将该方块显示在初始下落位置if (t == 0){t = 15000; //这里t越小,方块下落越快(可以根据此设置游戏难度,我的评价是可以搞事)}while (--t){if (kbhit() != 0) //若键盘被敲击,则退出循环break;}if (t == 0) //键盘未被敲击{if (IsLegal(shape, form, x, y + 1) == 0) //方块再下落就不合法了(已经到达底部){//将当前方块的信息录入face当中//face:记录界面的每个位置是否有方块,若有方块还需记录该位置方块的颜色。for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){if (block[shape][form].space[i][j] == 1){face.data[y + i][x + j] = 1; //将该位置标记为有方块face.color[y + i][x + j] = shape; //记录该方块的颜色数值}}}while (JudeFunc()); //判断此次方块下落是否得分以及游戏是否结束break; //跳出当前死循环,准备进行下一个方块的下落}else //未到底部{DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置y++; //纵坐标自增(下一次显示方块时就相当于下落了一格了)}}else //键盘被敲击{//对敲击键盘的按键的判断//(不理解为什么这个对,上面那个ESC的判断就有问题)******char ch = getch(); //读取keycodeswitch (ch){case DOWN: //方向键:下if (IsLegal(shape, form, x, y + 1) == 1) //判断方块向下移动一位后是否合法{//方块下落后合法才进行以下操作DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置y++; //纵坐标自增(下一次显示方块时就相当于下落了一格了)}break;case LEFT: //方向键:左if (IsLegal(shape, form, x - 1, y) == 1) //判断方块向左移动一位后是否合法{//方块左移后合法才进行以下操作DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置x--; //横坐标自减(下一次显示方块时就相当于左移了一格了)}break;case RIGHT: //方向键:右if (IsLegal(shape, form, x + 1, y) == 1) //判断方块向右移动一位后是否合法{//方块右移后合法才进行以下操作DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置x++; //横坐标自增(下一次显示方块时就相当于右移了一格了)}break;case SPACE: //空格键if (IsLegal(shape, (form + 1) % 4, x, y + 1) == 1) //判断方块旋转后是否合法{//方块旋转后合法才进行以下操作DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置y++; //纵坐标自增(总不能原地旋转吧)form = (form + 1) % 4; //方块的形态自增(下一次显示方块时就相当于旋转了)}break;case ESC: //Esc键system("cls"); //清空屏幕color(7);CursorJump(COL, ROW / 2);printf("  游戏结束  ");CursorJump(COL, ROW / 2 + 2);exit(0); //结束程序case 's':case 'S':  //暂停system("pause>nul"); //暂停(按任意键继续)break;case 'r':case 'R': //重新开始system("cls"); //清空屏幕main(); //重新执行主函数}}}shape = nextShape, form = nextForm; //获取下一个方块的信息DrawSpace(nextShape, nextForm, COL + 3, 3); //将右上角的方块信息用空格覆盖}
}
//从文件读取最高分
void ReadGrade()
{FILE* pf = fopen("俄罗斯方块最高得分记录.txt", "r"); //以只读方式打开文件if (pf == NULL) //打开文件失败{pf = fopen("俄罗斯方块最高得分记录.txt", "w"); //以只写方式打开文件(文件不存在可以自动创建该文件)fwrite(&grade, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高历史得分初始化为0}fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头fread(&max, sizeof(int), 1, pf); //读取文件中的最高历史得分到max当中fclose(pf); //关闭文件pf = NULL; //文件指针及时置空
}
//更新最高分到文件
void WriteGrade()
{FILE* pf = fopen("俄罗斯方块最高得分记录.txt", "w"); //以只写方式打开文件if (pf == NULL) //打开文件失败{printf("保存最高得分记录失败\n");exit(0);}fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中(更新最高历史得分)fclose(pf); //关闭文件pf = NULL; //文件指针及时置空
}
//over
// //还是那句话
//看似简单,实际上一点也不难

相关文章:

用c语言完成俄罗斯方块小游戏

用c语言完成俄罗斯方块小游戏 这估计是你在编程学习过程中的第一个小游戏开发&#xff0c;怎么说呢&#xff0c;在这里只针对刚学程序设计的学生&#xff0c;就是说刚接触C语言没多久&#xff0c;有一点功底的学生看看&#xff0c;简陋的代码&#xff0c;简陋的实现&#xff0…...

SpringBoot整合Retry详细教程

问题背景 在现代的分布式系统中&#xff0c;服务间的调用往往需要处理各种网络异常、超时等问题。重试机制是一种常见的解决策略&#xff0c;它允许应用程序在网络故障或临时性错误后自动重新尝试失败的操作。Spring Boot 提供了灵活的方式来集成重试机制&#xff0c;这可以通过…...

JS API事件监听(绑定)

事件监听 语法 元素对象.addEventListener(事件监听,要执行的函数) 事件监听三要素 事件源&#xff1a;那个dom元素被事件触发了&#xff0c;要获取dom元素 事件类型&#xff1a;用说明方式触发&#xff0c;比如鼠标单击click、鼠标经过mouseover等 事件调用的函数&#x…...

ceph手动部署

ceph手动部署 一、 节点规划 主机名IP地址角色ceph01.example.com172.18.0.10/24mon、mgr、osd、mds、rgwceph02.example.com172.18.0.20/24mon、mgr、osd、mds、rgwceph03.example.com172.18.0.30/24mon、mgr、osd、mds、rgw 操作系统版本&#xff1a; Rocky Linux release …...

superset load_examples加载失败解决方法

如果在执行load_examples命令后,出现上方图片情况,或是相似报错(url error\connection error),大概率原因是python程序请求github数据,无法访问. 因此我们可以将数据下载在本地来解决. 1.下载zip压缩文件,存放到本地 官方示例地址:GitHub - apache-superset/examples-data …...

wareshark分析mysql协议的数据包

使用wareshark 分析mysql协议的数据包&#xff0c;是每个dba都应该掌握的技能&#xff0c;掌握以后&#xff0c;就可以通过tcpdump抓包分析&#xff0c;得到连接报错的信息了。 tcpdump抓包命令&#xff1a; tcpdump -nn -i bond0 dst 10.21.6.72 and port 4002 -w 1129_tcpdu…...

HarmonyOS4+NEXT星河版入门与项目实战(25)------UIAbility启动模式(文档编辑案例)

文章目录 1、启动模式2、Specified启动模式实现步骤3、文档编辑案例1、文件创建2代码实现3、Statge 创建4、添加配置1、启动模式 Singleton启动模式: 每个 UIAbility 只存在一个实例,是默认的启动模式,任务列表中只会存在一个相同的 UIAbilityStandard启动模式: 每次启动 U…...

webpack 项目访问静态资源

使用 webpack dev serve 启动 react 项目后&#xff0c;发现无法使用 http://localhost:8080/1.png 访问到项目的 /static 目录下的 1.png 文件。我的 webpack-dev.js 配置如下&#xff1a; const webpack require(webpack) const webpackMerge require(webpack-merge) cons…...

‌UNION和UNION ALL区别

文章目录 结果集的处理方式‌&#xff1a;‌对重复记录的处理‌&#xff1a;‌排序处理‌&#xff1a;‌执行效率‌&#xff1a; ‌UNION和UNION ALL的主要区别在于结果集的处理方式、对重复记录的处理、排序处理以及执行效率。‌‌ 结果集的处理方式‌&#xff1a; ‌UNION‌…...

Rook入门:打造云原生Ceph存储的全面学习路径(下)

文章目录 六.Rook部署云原生CephFS文件系统6.1 部署cephfs storageclass6.2 创建容器所需cephfs文件系统6.3创建容器pod使用rook-cephfs提供pvc6.4 查看pod是否使用rook-cephfs 七.Ceph Dashboard界面7.1 启用dashboard开关7.2 ceph-dashboard配置外部访问7.3 Dashboard web ad…...

RabbitMQ消息可靠性保证机制6--可靠性分析

在使用消息中间件的过程中&#xff0c;难免会出现消息错误或者消息丢失等异常情况。这个时候就需要有一个良好的机制来跟踪记录消息的过程&#xff08;轨迹溯源&#xff09;&#xff0c;帮助我们排查问题。 在RabbitMQ中可以使用Firehose实现消息的跟踪&#xff0c;Firehose可…...

k8s容器存储接口 CSI 相关知识

容器存储接口 CSI 相关知识 参考&#xff1a; https://blog.csdn.net/lovely_nn/article/details/122880876 https://developer.aliyun.com/article/783464 https://www.cnblogs.com/varden/p/15139819.html存储商需实现 CSI 插件的 NodeGetVolumeStats 接口&#xff0c;Kube…...

jmeter基础_打开1个jmeter脚本(.jmx文件)

课程大纲 方法1.菜单栏“打开” 菜单栏“文件” - “打开” &#xff08;或快捷键&#xff0c;mac为“⌘ O”&#xff09;&#xff0c;打开文件选择窗口 - 选择脚本文件&#xff0c;点击“open”&#xff0c;即可打开脚本。 方法2.工具栏“打开”图标 工具栏点击“打开”图标&…...

Linux---对时/定时服务

文章目录 目录 文章目录 前言 一.对时服务 服务端配置 客户端配置 二.定时服务 单次定时任务 循环定时任务 前言 在当今信息化高速发展的时代&#xff0c;时间的准确性和任务的定时执行对于各种系统和服务来说至关重要。Linux操作系统&#xff0c;凭借其强大的功能和灵活的…...

Agent

Agent核心 1、自主性 2、交互性 3、适应性 4、目的性 ReAct Reasoning and Acting范式 模型的推理过程分为 推理 Reason 和行动 Action 两个步骤&#xff0c;交替执行&#xff0c;直至获得最终结果。 推理 Reason 生成分析步骤&#xff0c;解释当前任务的上下文或状态…...

Oracle 数据库执行增删改查命令的原理与过程

摘要&#xff1a; 本文深入探讨当向 Oracle 数据库发送一个增删改查&#xff08;CRUD&#xff09;命令时&#xff0c;数据库内部的执行机制与详细过程。从用户发起命令开始&#xff0c;逐步剖析命令在 Oracle 数据库体系结构各组件中的流转、解析、优化以及执行路径&#xff0c…...

HBase难点

查询优化 一次Scan会返回大量数据&#xff0c;客户端向HBase发送一次Scan请求&#xff0c;实际上并不会将所有数据加载到本地&#xff0c;而是通过多次RPC请求进行加载&#xff0c;防止客户端OOM。禁止缓存优化&#xff1a;批量读取数据时会全表扫描一次业务表&#xff0c;这种…...

Y20030023 PHP+thinkphp+MYSQL+LW+基于PHP的健身房管理系统的设计与实现 源代码 配置 初稿

基于PHP的健身房管理系统 1.项目摘要2. 系统开发的背景及意义3.项目功能4.界面展示5.源码获取 1.项目摘要 近年来&#xff0c;随着社会发展和科技进步&#xff0c;人们越来越重视健康养生并关注电子商务对日常交流方式的影响。随着健身行业消费人群的增加&#xff0c;竞争变得…...

mongodb下载与使用

下载地址&#xff1a; Install MongoDB Community Kubernetes Operator | MongoDB 1、安装MongoDB (5.0.30) 将压缩包移动到C:\Program Files 下&#xff0c;然后解压创建C:\data\db目录&#xff0c;mongodb 会将数据默认保存在这个文件夹以mongodb 中 bin目录作为工作目录&…...

【Linux基础】Linux基本指令

目录 1、pwd 指令 2、clear 指令 3、ls 指令 1、ls 显示当前路径下的文件或者目录名称 2、ls -l 显示当前路径下的文件或者目录的更详细的属性信息 3、ls -a &#xff1a;显示所有文件&#xff0c;包括隐藏文件 4、ls -d 将目录像文件一样显示&#xff0c;而不是显示其…...

【RISC-V CPU debug 专栏 3 -- Debugging RISC-V Cores】

文章目录 RISC-V 调试规范开源与多样性挑战调试规范的重要性外部调试支持的主要组件调试功能Lauterbach 的贡献RISC-V 调试规范 调试 RISC-V 内核涉及许多独特的挑战,这是由 RISC-V 的开源特性和多样化的生态系统所决定的。为了避免专有调试接口的泛滥,RISC-V 基金会内的工作…...

思科实现网络地址转换(NAT)和访问控制列表(ACL)和动态路由配置并且区分静态路由和动态路由配置。

实验拓扑(分为静态路由和动态路由两种) 静态路由互通 动态路由互通 实验背景 这个是想实现外网与内网的连接跟网络的探讨&#xff0c;最终实现互通以及使用并且在网络地址转换后能使用网络然后再这个基础上再配置访问控制列表和网络地址转换的的学习过程。 实验需了解的知识…...

基于 Python、OpenCV 和 PyQt5 的人脸识别上课打卡系统

大家好&#xff0c;我是Java徐师兄&#xff0c;今天为大家带来的是基于 Python、OpenCV 和 PyQt5 的人脸识别上课签到系统。该系统采用 Python 语言开发&#xff0c;开发过程中采用了OpenCV框架&#xff0c;Sqlite db 作为数据库&#xff0c;系统功能完善 &#xff0c;实用性强…...

MySQL 复合查询

实际开发中往往数据来自不同的表&#xff0c;所以需要多表查询。本节我们用一个简单的公司管理系统&#xff0c;有三张表EMP,DEPT,SALGRADE 来演示如何进行多表查询。表结构的代码以及插入的数据如下&#xff1a; DROP database IF EXISTS scott; CREATE database IF NOT EXIST…...

【Leetcode】19. 删除链表的第N个节点

【Leetcode】19. 删除链表的第N个节点 1. 题目介绍2. 方法一&#xff1a;计算链表长度逻辑流程:代码复杂度分析 1. 题目介绍 题目描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,…...

flutter底部导航栏中间按钮凸起,导航栏中间部分凹陷效果

关键代码: Scaffold中设置floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked和extendBody: true&#xff0c; BottomAppBar中设置shape: const CircularNotchedRectangle() Scaffold(extendBody: true,//body是否延伸脚手架底部&#xff0c;在底部导航…...

<项目代码>YOLOv8 红绿灯识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…...

HTMLHTML5革命:构建现代网页的终极指南 - 4. 使用元素

HTML&HTML5革命&#xff1a;构建现代网页的终极指南 4. 使用元素 大家好&#xff0c;我是莫离老师。 到目前为止&#xff0c;我们已经了解了 HTML 和 HTML5 的基础概念&#xff0c;并且选择了适合自己的开发工具。 今天&#xff0c;我们将迈出实际开发的第一步&#xff0c…...

使用 Selenium 和 Python 爬取腾讯新闻:从基础到实践

使用 Selenium 和 Python 爬取腾讯新闻&#xff1a;从基础到实践 在这篇博客中&#xff0c;我们将介绍如何利用 Selenium 和 Python 爬取腾讯新闻的内容&#xff0c;并将结果保存到 CSV 文件中。本教程包含以下内容&#xff1a; 项目简介依赖安装实现功能的代码实现中的关键技…...

CAD深度清理工具-AVappsDrawingPurge9.0.0(2024.8.27版本) 支持版本CAD2022-2025-供大家学习研究参考

图形文件DWG体积很大&#xff1a;通常没有明显的数据。同时&#xff0c;还其他症状包括&#xff1a; &#xff08;1&#xff09;无法复制和粘贴图元。 &#xff08;2&#xff09;悬挂较长时间选择文本与 “特性”选项板上打开。 &#xff08;3&#xff09;图形文件需要很长时间…...

网上哪个网站做的系统好用吗/百度一下你就知道手机版

一、事务的传播行为 1.介绍 当事务方法被另一个事务方法调用时&#xff0c;必须指定事务应该如何传播。例如&#xff1a;方法可能继续在现有事务中运行&#xff0c;也可能开启一个新事务&#xff0c;并在自己的事务中运行。 2.属性 事务的传播行为可以由传播属性指定。Spri…...

做网站制作较好的公司/成都疫情最新消息

一&#xff0e;简介 0. 页面的生命周期。 1. WebForm后台页面类继承于Page类&#xff0c;Page类实现了IHttpHandler接口。 2. 前台页面类继承于后台页面类。 3. 先调用PageLoad方法&#xff0c;再调用Render方法生成html代码。 二. 加密安全 互联网没有绝对的安全&#xff0c;登…...

贵阳的网站建设公司/长沙seo推广外包

在IOS开发中&#xff0c;要做字典转模型一般情况如下&#xff1a; 1 /**2 * 声明方法3 */4 - (instancetype) initWithDictionary:(NSDictionary *)dict;5 (instancetype) carWithDictionary:(NSDictionary *)dict;6 7 /**8 * 实现方法9 */ 10 - (instancetype)initWith…...

厦门好的做网站公司/b站刺激战场视频

P1307 [NOIP2011 普及组] 数字反转 输入输出样例 输入 123 输出 321输入 -380 输出 -83#include<bits/stdc.h> using namespace std; int main() {int b0,a,n;cin>>n;if(n<0) {cout<<"-";n-n;}if(n%100) nn/10;while(n!0){an%10;bb*10a;nn/…...

怎么做微网站/免费制作网页平台

一个类型允许定义多个实例构造器&#xff0c;在使用过程中确实是十分方便的。但是&#xff0c;在定义这些构造器时&#xff0c;如果稍不留神&#xff0c;可能就使你的代码编译后产生了好多不必要的垃圾&#xff0c;增加了程序集的大小&#xff0c;也不够简洁。 例如&#xff1a…...

洛阳平台公司/seo成都培训

可以使用 IN 子句代替许多 OR 条件。要想理解 IN 子句&#xff0c;还以表 employee_tbl 为例&#xff0c;它的所有记录如下所示&#xff1a;mysql> SELECT * FROM employee_tbl;--------------------------------------------| id | name | work_date | daily_typing_pages …...