<think>我们可以在控制台中实现俄罗斯方块游戏,使用Windows API控制控制台的光标位置和颜色,实现方块的下落和移动。
由于控制台中没有图形界面,我们将使用字符(如方块字符)来表示方块,并利用控制台的颜色属性来设置不同的颜色。
我们将使用Windows的API函数来设置控制台的光标位置和颜色。主要用到的函数有:
- GetStdHandle(STD_OUTPUT_HANDLE) 获取控制台句柄
- SetConsoleCursorPosition 设置光标位置
- SetConsoleTextAttribute 设置文本属性(颜色)
由于控制台中没有事件监听,我们将使用_kbhit()和_getch()来检测键盘输入。
游戏区域:用一个二维数组表示,每个位置可以是0(空)或1(有方块)。我们使用字符画的方式在控制台中绘制。
注意:控制台的行和列与我们游戏区域的对应关系。通常控制台的一个字符位置可以显示一个方块,所以游戏区域的行数对应控制台的行,列数对应控制台的列。
步骤:
1. 初始化控制台:隐藏光标,设置窗口大小等。
2. 定义方块形状,和之前类似,用4x4矩阵表示。
3. 游戏主循环:处理输入,更新游戏状态(下落、消行等),绘制界面。
由于控制台绘制效率较低,我们只更新变化的部分。
我们将使用以下颜色:
- 背景:黑色
- 当前方块:蓝色(或控制台中可用的颜色)
- 已固定的方块:黄色
控制台颜色属性值:
0=黑色,1=蓝色,2=绿色,4=红色,6=黄色,7=白色,等等。
具体实现:
我们将使用一个双缓冲区来减少闪烁。先在一个内存中的二维数组(缓冲区)中绘制,然后一次性输出到控制台。
但是为了简化,我们也可以直接控制光标位置,只更新有变化的位置。
由于控制台字符是矩形的,我们可以使用两个字符拼成一个方块(如两个空格,但设置背景色),或者使用全角字符(如方块字符■)。
这里我们使用全角字符■,这样每个方块用一个字符表示。
但是注意:全角字符在控制台占两个英文字符的位置,所以我们在绘制时要注意坐标。为了保持比例,我们可以将游戏区域的每个方块用两个英文字符宽度和一个高度表示,这样每个方块就是正方形。
然而,这样会使绘制变得复杂。另一种方法是使用两个半角字符并排(如两个空格)并设置背景色,这样每个方块就是正方形。
这里我们选择使用两个空格并设置背景色来绘制方块。这样每个方块在控制台占两个字符宽度,一个字符高度。
因此,游戏区域在控制台中的宽度为 COLS*2,高度为 ROWS。
绘制一个方块:在(x*2, y)位置设置背景色,输出两个空格。
具体步骤:
1. 初始化游戏区域(board)为0。
2. 初始化当前方块。
3. 主循环:
检测键盘输入(上:旋转,下:加速,左右:移动)
判断是否可以下落,否则固定方块并检查消行。
绘制:先清空整个游戏区域(或者只绘制变化的部分),然后绘制已固定的方块和当前方块。
由于控制台绘制速度慢,我们使用双缓冲:在内存中创建一个与控制台窗口一样大的字符缓冲区,先在这个缓冲区中绘制,然后一次性输出。
但为了简化,我们也可以只更新变化的位置。不过这里为了简单,我们每次重绘整个游戏区域。
我们将使用以下函数:
void drawBlock(int x, int y, int color); // 在游戏区域坐标(x,y)绘制一个方块(颜色为color)
void drawBoard(); // 绘制整个游戏区域(包括已固定的方块和当前方块)
注意:游戏区域在控制台中的起始位置(左上角坐标),比如从(0,0)开始。
由于控制台坐标的原点在左上角,y轴向下。
代码结构:
由于不使用图形库,我们只使用控制台API,所以代码将包含windows.h。
我们将创建一个控制台窗口,并设置其大小。
注意:控制台窗口大小需要足够大,以容纳游戏区域(高度ROWS,宽度COLS*2)。此外,还需要显示分数等信息。
因此,我们设置控制台窗口宽度为COLS*2+20(用于显示分数等),高度为ROWS+2。
我们分两个区域:左侧为游戏区域,右侧为信息区域。
由于时间关系,我们先实现基本游戏,信息区域可以稍后添加。
下面开始编写代码:</think># 控制台版俄罗斯方块游戏(C语言实现)
以下是一个不使用EasyX图形库的俄罗斯方块游戏实现,完全在控制台中运行,可以在Dev-C++上编译执行。
```c
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>
#include <string.h>
// 游戏区域尺寸
#define ROWS 20
#define COLS 10
#define BLOCK_SIZE 4
// 方块形状定义(7种形状,4种旋转状态)
const int shapes[7][4][4][4] = {
// 长条型
{
{{0,0,0,0}, {1,1,1,1}, {0,0,0,0}, {0,0,0,0}},
{{0,0,1,0}, {0,0,1,0}, {0,0,1,0}, {0,0,1,0}},
{{0,0,0,0}, {0,0,0,0}, {1,1,1,1}, {0,0,0,0}},
{{0,1,0,0}, {0,1,0,0}, {0,1,0,0}, {0,1,0,0}}
},
// Z字型
{
{{0,0,0,0}, {1,1,0,0}, {0,1,1,0}, {0,0,0,0}},
{{0,0,1,0}, {0,1,1,0}, {0,1,0,0}, {0,0,0,0}},
{{0,0,0,0}, {1,1,0,0}, {0,1,1,0}, {0,0,0,0}},
{{0,0,1,0}, {0,1,1,0}, {0,1,0,0}, {0,0,0,0}}
},
// 反Z字型
{
{{0,0,0,0}, {0,1,1,0}, {1,1,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,1,1,0}, {1,1,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,1,1,0}, {0,1,1,0}, {0,0,0,0}},
{{0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0}},
{{0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0}},
{{0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0}}
},
// 7字型
{
{{0,0,0,0}, {1,1,1,0}, {0,0,1,0}, {0,0,0,0}},
{{0,0,1,0}, {0,0,1,0}, {0,1,1,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,1,1,0}, {0,1,0,0}, {0,1,0,0}}
},
// 反7字型
{
{{0,0,0,0}, {1,1,1,0}, {1,0,0,0}, {0,0,0,0}},
{{0,1,1,0}, {0,0,1,0}, {0,0,1,0}, {0,0,0,0}},
{{0,0,0,0}, {0,0,1,0}, {1,1,1,0}, {0,0,0,0}},
{{0,1,0,0}, {0,1,0,0}, {0,1,1,0}, {0,0,0,0}}
},
// T字型
{
{{0,0,0,0}, {1,1,1,0}, {0,1,0,0}, {0,0,0,0}},
{{0,0,1,0}, {0,1,1,0}, {0,0,1,0}, {0,0,0,0}},
{{0,0,0,0}, {0,1,0,0}, {1,1,1,0}, {0,0,0,0}},
{{0,1,0,0}, {0,1,1,0}, {0,1,0,0}, {0,0,0,0}}
}
};
// 游戏区域
int board[ROWS][COLS] = {0};
// 当前方块信息
int curX, curY; // 位置
int curShape; // 形状索引
int curRotation; // 旋转状态
int score = 0; // 分数
int level = 1; // 等级
int gameOver = 0; // 游戏结束标志
int nextShape; // 下一个方块形状
// 设置控制台光标位置
void gotoxy(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
// 隐藏光标
void hideCursor() {
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
// 初始化新方块
void initBlock() {
curX = COLS / 2 - 2;
curY = 0;
curShape = nextShape;
nextShape = rand() % 7;
curRotation = 0;
}
// 碰撞检测
int checkCollision(int x, int y, int shape, int rotation) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (shapes[shape][rotation][i][j]) {
int boardX = x + j;
int boardY = y + i;
if (boardX < 0 || boardX >= COLS || boardY >= ROWS ||
(boardY >= 0 && board[boardY][boardX])) {
return 1;
}
}
}
}
return 0;
}
// 固定方块到游戏区域
void lockBlock() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (shapes[curShape][curRotation][i][j]) {
board[curY + i][curX + j] = 1;
}
}
}
}
// 消行处理
void clearLines() {
int linesCleared = 0;
for (int i = ROWS - 1; i >= 0; i--) {
int full = 1;
for (int j = 0; j < COLS; j++) {
if (!board[i][j]) {
full = 0;
break;
}
}
if (full) {
linesCleared++;
// 上移所有行
for (int k = i; k > 0; k--) {
for (int j = 0; j < COLS; j++) {
board[k][j] = board[k-1][j];
}
}
// 清空顶行
for (int j = 0; j < COLS; j++) {
board[0][j] = 0;
}
i++; // 重新检查当前行
}
}
// 更新分数
if (linesCleared > 0) {
score += linesCleared * linesCleared * 100;
// 更新等级
level = score / 2000 + 1;
}
}
// 绘制游戏界面
void draw() {
// 清屏
system("cls");
// 绘制游戏标题
gotoxy(15, 0);
printf("俄罗斯方块 - 等级: %d 分数: %d", level, score);
// 绘制游戏区域边框
gotoxy(0, 2);
for (int i = 0; i < COLS + 2; i++) printf("#");
// 绘制已固定的方块
for (int i = 0; i < ROWS; i++) {
gotoxy(0, i + 3);
printf("#");
for (int j = 0; j < COLS; j++) {
if (board[i][j]) {
printf("[]"); // 黄色方块
} else {
printf(" "); // 空格
}
}
printf("#");
}
gotoxy(0, ROWS + 3);
for (int i = 0; i < COLS + 2; i++) printf("#");
// 绘制当前方块
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (shapes[curShape][curRotation][i][j]) {
gotoxy((curX + j) * 2 + 1, curY + i + 3);
printf("[]");
}
}
}
// 绘制下一个方块预览
gotoxy(COLS * 2 + 5, 3);
printf("下一个方块:");
for (int i = 0; i < 6; i++) {
gotoxy(COLS * 2 + 5, 4 + i);
printf(" ");
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (shapes[nextShape][0][i][j]) {
gotoxy(COLS * 2 + 5 + j * 2, 5 + i);
printf("[]");
}
}
}
// 绘制操作说明
gotoxy(COLS * 2 + 5, 10);
printf("操作说明:");
gotoxy(COLS * 2 + 5, 11);
printf("↑: 旋转方块");
gotoxy(COLS * 2 + 5, 12);
printf("↓: 加速下落");
gotoxy(COLS * 2 + 5, 13);
printf("←→: 左右移动");
gotoxy(COLS * 2 + 5, 14);
printf("空格: 直接落下");
gotoxy(COLS * 2 + 5, 15);
printf("P: 暂停游戏");
gotoxy(COLS * 2 + 5, 16);
printf("Q: 退出游戏");
}
// 键盘控制
void handleInput() {
if (_kbhit()) {
int key = _getch();
switch (key) {
case 72: // 上键 - 旋转
if (!checkCollision(curX, curY, curShape, (curRotation+1)%4)) {
curRotation = (curRotation + 1) % 4;
}
break;
case 80: // 下键 - 加速下落
if (!checkCollision(curX, curY+1, curShape, curRotation)) {
curY++;
}
break;
case 75: // 左键
if (!checkCollision(curX-1, curY, curShape, curRotation)) {
curX--;
}
break;
case 77: // 右键
if (!checkCollision(curX+1, curY, curShape, curRotation)) {
curX++;
}
break;
case 32: // 空格键 - 直接落下
while (!checkCollision(curX, curY+1, curShape, curRotation)) {
curY++;
}
break;
case 'p':
case 'P': // 暂停
gotoxy(COLS * 2 + 5, 18);
printf("游戏暂停,按任意键继续...");
_getch();
gotoxy(COLS * 2 + 5, 18);
printf(" ");
break;
case 'q':
case 'Q': // 退出
gameOver = 1;
break;
}
}
}
// 玩家结构
typedef struct Player {
char name[20];
int score;
struct Player* next;
} Player;
// 全局排名链表
Player* ranking = NULL;
// 插入新玩家(按分数排序)
Player* insertPlayer(Player* head, char* name, int score) {
Player* newPlayer = (Player*)malloc(sizeof(Player));
strcpy(newPlayer->name, name);
newPlayer->score = score;
newPlayer->next = NULL;
// 如果链表为空或新玩家分数最高
if (head == NULL || score > head->score) {
newPlayer->next = head;
return newPlayer;
}
// 查找插入位置
Player* current = head;
while (current->next != NULL && current->next->score >= score) {
current = current->next;
}
newPlayer->next = current->next;
current->next = newPlayer;
return head;
}
// 保存排名到文件
void saveRanking() {
FILE* fp = fopen("ranking.dat", "wb");
if (fp == NULL) return;
Player* current = ranking;
while (current != NULL) {
fwrite(current, sizeof(Player), 1, fp);
current = current->next;
}
fclose(fp);
}
// 加载排名
void loadRanking() {
FILE* fp = fopen("ranking.dat", "rb");
if (fp == NULL) return;
Player player;
while (fread(&player, sizeof(Player), 1, fp) == 1) {
ranking = insertPlayer(ranking, player.name, player.score);
}
fclose(fp);
}
// 显示排名
void showRanking() {
system("cls");
printf("==== 玩家排名 ====\n");
Player* current = ranking;
int rank = 1;
while (current != NULL && rank <= 10) {
printf("%2d. %-20s %d\n", rank, current->name, current->score);
current = current->next;
rank++;
}
printf("\n按任意键返回...");
_getch();
}
// 游戏结束处理
void gameOverScreen() {
system("cls");
printf("游戏结束! 最终得分: %d\n", score);
char name[20];
printf("请输入你的名字: ");
scanf("%19s", name);
// 更新排名
ranking = insertPlayer(ranking, name, score);
saveRanking();
printf("\n1. 重新开始\n");
printf("2. 查看排名\n");
printf("3. 退出游戏\n");
printf("选择: ");
int choice;
scanf("%d", &choice);
if (choice == 1) {
// 重置游戏状态
memset(board, 0, sizeof(board));
score = 0;
level = 1;
gameOver = 0;
srand(time(0));
nextShape = rand() % 7;
initBlock();
} else if (choice == 2) {
showRanking();
} else {
exit(0);
}
}
int main() {
// 初始化
srand(time(0));
hideCursor();
loadRanking();
nextShape = rand() % 7;
initBlock();
while (!gameOver) {
// 处理键盘输入
handleInput();
// 方块下落
if (!checkCollision(curX, curY+1, curShape, curRotation)) {
curY++;
} else {
// 固定方块并检查游戏结束
lockBlock();
clearLines();
if (curY <= 0) {
gameOver = 1;
} else {
initBlock();
}
}
// 绘制界面
draw();
// 控制下落速度(根据等级调整)
Sleep(300 / level);
}
// 游戏结束处理
gameOverScreen();
// 递归调用main以重新开始游戏
main();
return 0;
}
```
## 代码说明
### 1. 游戏核心功能
- **方块表示**:使用4×4矩阵表示7种形状的4种旋转状态
- **碰撞检测**:`checkCollision()`函数检测边界和已固定方块
- **消行处理**:`clearLines()`函数检测满行并消除
- **分数系统**:消除行数越多得分越高,分数影响等级
- **等级系统**:等级越高方块下落速度越快
### 2. 控制台界面实现
- **光标控制**:使用Windows API函数`SetConsoleCursorPosition`控制光标位置
- **方块绘制**:使用字符`[]`表示方块,空格表示空白区域
- **游戏区域**:使用`#`字符绘制边框
- **信息显示**:在右侧显示分数、等级、下一个方块预览和操作说明
### 3. 玩家排名系统
- **链表实现**:使用链表存储玩家排名信息
- **文件存储**:将排名信息保存到`ranking.dat`文件中
- **插入逻辑**:
- 新玩家按分数插入到合适位置
- 同一玩家更新成绩时覆盖原成绩
- **排名显示**:显示前10名玩家信息
### 4. 游戏控制
- **键盘操作**:
- 上下左右键:旋转、加速、左右移动
- 空格键:方块直接落下
- P键:暂停游戏
- Q键:退出游戏
- **游戏流程**:
- 初始化游戏
- 主循环处理输入和更新状态
- 游戏结束处理(输入名字、更新排名)
### 5. 编译运行说明
1. 在Dev-C++中创建新项目
2. 复制上述代码到源文件
3. 编译并运行(确保在Windows环境下)
4. 游戏会自动创建`ranking.dat`文件保存排名数据
## 游戏特点
1. 完全在控制台中实现,无需图形库
2. 支持玩家排名系统并持久化存储
3. 实现方块预览功能
4. 包含等级和分数系统
5. 提供完整的操作说明