俄罗斯方块

目录

前言


前言

本篇文章作为笔记来记录Linux系统的初步学习成果,如有更好的代码请在评论区留言。欢迎各位中老登对此代码的批判。

项目简介

本项目是基于C语言在Linux环境下的俄罗斯方块,使用模块化设计和ncurses库实现终端图形界面。

技术使用

  • 编程语言: C语言

  • 图形库: ncurses终端图形库

  • 开发环境: Linux + Vim + GCC

  • 构建工具: Makefile

项目结构

复制代码
tetris_game/
├── tetris.h          # 头文件 
├── main.c            # 主程序 
├── game.c            # 逻辑模块
├── block.c           # 方块模块
|── display.c         # 显示模块

核心模块功能

1. 游戏逻辑模块 (game.c)

  • 游戏状态初始化

  • 消行检测与处理

  • 游戏结束判断

  • #include "tetris.h"

    void init_game(GameState *game) {
    for (int y = 0; y < HEIGHT; y++) {
    for (int x = 0; x < WIDTH; x++) {
    game->board[y][x] = 0;
    }
    }
    game->score = 0;
    game->game_over = 0;
    }

    int clear_lines(GameState *game) {
    int lines_cleared = 0;

    复制代码
      for (int y = HEIGHT - 1; y >= 0; y--) {
          int line_full = 1;
          
          for (int x = 0; x < WIDTH; x++) {
              if (!game->board[y][x]) {
                  line_full = 0;
                  break;
              }
          }
          
          if (line_full) {
              lines_cleared++;
              for (int ny = y; ny > 0; ny--) {
                  for (int x = 0; x < WIDTH; x++) {
                      game->board[ny][x] = game->board[ny-1][x];
                  }
              }
              for (int x = 0; x < WIDTH; x++) {
                  game->board[0][x] = 0;
              }
              y++;
          }
      }
      
      return lines_cleared;

    }

    int is_game_over(const GameState *game, const Block *block) {
    return check_collision(game, block) && block->y == 0;
    }

2. 方块处理模块 (block.c)

  • 方块形状定义

  • 方块旋转

  • 碰撞检测

  • 方块移动控制

复制代码
#include "tetris.h"

const int SHAPES[7][4][4] = {
    {{0,0,0,0}, {1,1,1,1}, {0,0,0,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,0,0}, {1,1,1,0}, {0,0,0,0}},
    {{0,0,0,0}, {0,1,1,0}, {1,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,0,0}, {1,0,0,0}, {1,1,1,0}, {0,0,0,0}},
    {{0,0,0,0}, {0,0,1,0}, {1,1,1,0}, {0,0,0,0}}
};

void init_block(Block *block) {
    block->type = rand() % 7;
    block->x = WIDTH / 2 - 2;
    block->y = 0;
    
    for (int i = 0; i < BLOCK_SIZE; i++) {
        for (int j = 0; j < BLOCK_SIZE; j++) {
            block->shape[i][j] = SHAPES[block->type][i][j];
        }
    }
}

int check_collision(const GameState *game, const Block *block) {
    for (int i = 0; i < BLOCK_SIZE; i++) {
        for (int j = 0; j < BLOCK_SIZE; j++) {
            if (block->shape[i][j]) {
                int board_x = block->x + j;
                int board_y = block->y + i;
                
                if (board_x < 0 || board_x >= WIDTH || board_y >= HEIGHT) {
                    return 1;
                }
                
                if (board_y >= 0 && game->board[board_y][board_x]) {
                    return 1;
                }
            }
        }
    }
    return 0;
}

int move_block(GameState *game, Block *block, int dx, int dy) {
    block->x += dx;
    block->y += dy;
    
    if (check_collision(game, block)) {
        block->x -= dx;
        block->y -= dy;
        return 0;
    }
    return 1;
}

int rotate_block(GameState *game, Block *block) {
    int temp_shape[BLOCK_SIZE][BLOCK_SIZE];
    
    for (int i = 0; i < BLOCK_SIZE; i++) {
        for (int j = 0; j < BLOCK_SIZE; j++) {
            temp_shape[i][j] = block->shape[i][j];
        }
    }
    
    for (int i = 0; i < BLOCK_SIZE; i++) {
        for (int j = 0; j < BLOCK_SIZE; j++) {
            block->shape[j][BLOCK_SIZE-1-i] = temp_shape[i][j];
        }
    }
    
    if (check_collision(game, block)) {
        for (int i = 0; i < BLOCK_SIZE; i++) {
            for (int j = 0; j < BLOCK_SIZE; j++) {
                block->shape[i][j] = temp_shape[i][j];
            }
        }
        return 0;
    }
    return 1;
}

void lock_block(GameState *game, const Block *block) {
    for (int i = 0; i < BLOCK_SIZE; i++) {
        for (int j = 0; j < BLOCK_SIZE; j++) {
            if (block->shape[i][j]) {
                int board_x = block->x + j;
                int board_y = block->y + i;
                
                if (board_y >= 0 && board_x >= 0 && board_x < WIDTH) {
                    game->board[board_y][board_x] = 1;
                }
            }
        }
    }
}

3. 显示模块 (display.c)

  • 图形界面展示

  • 游戏板绘制

  • 用户界面设计

  • #include "tetris.h"

    void init_display() {
    initscr();
    cbreak();
    noecho();
    curs_set(0);
    keypad(stdscr, TRUE);
    timeout(100);
    }

    void cleanup_display() {
    endwin();
    }

    void draw_block(const Block *block) {
    for (int i = 0; i < BLOCK_SIZE; i++) {
    for (int j = 0; j < BLOCK_SIZE; j++) {
    if (block->shape[i][j]) {
    int x = (block->x + j) * 2 + 2;
    int y = block->y + i + 1;

    复制代码
                  if (y >= 1 && x >= 2) {
                      mvprintw(y, x, "[]");
                  }
              }
          }
      }

    }

    void draw_board(const GameState *game) {
    for (int y = 0; y <= HEIGHT; y++) {
    mvprintw(y, 0, "#");
    mvprintw(y, (WIDTH + 1) * 2, "#");
    }
    for (int x = 0; x <= WIDTH * 2 + 2; x += 2) {
    mvprintw(HEIGHT, x, "#");
    }

    复制代码
      for (int y = 0; y < HEIGHT; y++) {
          for (int x = 0; x < WIDTH; x++) {
              if (game->board[y][x]) {
                  mvprintw(y + 1, x * 2 + 2, "[]");
              }
          }
      }

    }

    void draw_game(const GameState *game, const Block *block) {
    clear();

    复制代码
      draw_board(game);
      draw_block(block);
      
      mvprintw(2, WIDTH * 2 + 6, "Controls:");
      mvprintw(3, WIDTH * 2 + 6, "A - Move Left");
      mvprintw(4, WIDTH * 2 + 6, "D - Move Right");
      mvprintw(5, WIDTH * 2 + 6, "S - Move Down");
      mvprintw(6, WIDTH * 2 + 6, "W - Rotate");
      mvprintw(7, WIDTH * 2 + 6, "Q - Quit");
      
      mvprintw(9, WIDTH * 2 + 6, "Status: %s", 
               game->game_over ? "game over" : "playing");
      
      refresh();

    }

4. 主控制模块 (main.c)

  • 游戏主循环

  • 输入处理

  • 游戏流程管理

  • #include "tetris.h"

    int main() {
    GameState game;
    Block current;
    int ch;
    int fall_counter = 0;
    const int fall_speed = 20;

    复制代码
      srand(time(NULL));
      
      init_game(&game);
      init_block(&current);
      init_display();
      
      if (has_colors()) {
          start_color();
          init_pair(1, COLOR_CYAN, COLOR_BLACK);
          init_pair(2, COLOR_YELLOW, COLOR_BLACK);
          init_pair(3, COLOR_MAGENTA, COLOR_BLACK);
          init_pair(4, COLOR_GREEN, COLOR_BLACK);
          init_pair(5, COLOR_RED, COLOR_BLACK);
          init_pair(6, COLOR_BLUE, COLOR_BLACK);
          init_pair(7, COLOR_WHITE, COLOR_BLACK);
      }
      
      while (!game.game_over) {
          ch = getch();
          switch (ch) {
              case 'a': case 'A': move_block(&game, &current, -1, 0); break;
              case 'd': case 'D': move_block(&game, &current, 1, 0); break;
              case 's': case 'S': move_block(&game, &current, 0, 1); break;
              case 'w': case 'W': rotate_block(&game, &current); break;
              case 'q': case 'Q': game.game_over = 1; break;
          }
          
          fall_counter++;
          if (fall_counter >= fall_speed) {
              fall_counter = 0;
              
              if (!move_block(&game, &current, 0, 1)) {
                  lock_block(&game, &current);
                  clear_lines(&game);
                  init_block(&current);
                  
                  if (is_game_over(&game, &current)) {
                      game.game_over = 1;
                  }
              }
          }
          
          draw_game(&game, &current);
          usleep(10000);
      }
      
      draw_game(&game, &current);
      mvprintw(HEIGHT + 2, 2, "游戏结束!");
      refresh();
      timeout(-1);
      getch();
      
      cleanup_display();
      return 0;

    }

如何使用

编译方法
复制代码
make claen/make
运行游戏
复制代码
./tetris
操作示例:
最初版本

这是项目的第一个可运行版本,只具备了基本的游戏框架,但是尚未实现方块的旋转操作,碰撞检测不够完善,导致方块落下后不能正常消除,让我很难绷,于是我参考了哔哩哔哩教学博主的写法添加了一些模块,使他看起来更像游戏。

现在版本

使用ncurses库处理终端图形显示界面和图形,是不是显得更像一个游戏了?而且我参考了其他俄罗斯方块,他们的每一个方块都是随机出现的,于是我便引入了随机变量使其具有随机效果。

不足之处分析

在最初项目中,碰撞检测模块让我很头疼,尤其是边界碰撞(左右墙、底部)与已锁定方块的碰撞,我很难精确判断方块在各种情况下的可移动性,所以这个项目的bug之一就是在下落的一瞬间旋转方块就会卡住,这个问题我还没有解决。除此之外,操作的流畅性和准确性使连续按键的处理,输入响应的即时反馈导致输出的反馈不太理想,经常出现先向左按下D在向右按下A,方块出现抽搐的效果.......最后就是ncurses库,因为第一次使用ncurses库进行页面的渲染,字符没有进行有效的渲染它不识别中文,中文会变成乱码......

总结

本次项目对我来说有点复杂,我的基础知识并不牢固,在进行项目的过程中遇见了很多的困难,有的解决了,有的并没有,让我很郁闷,总之还需要勤加练习。

相关推荐
Ronin3052 小时前
【Linux网络】应用层协议HTTP
linux·网络·http·应用层协议
合作小小程序员小小店2 小时前
web安全开发,在线%服务器日志入侵检测%系统安全开发,基于Python,flaskWeb,正则表达式检测,mysql数据库
服务器·python·安全·web安全·flask·安全威胁分析·安全架构
chen_note2 小时前
Kubernetes1.23版本搭建(三台机器)
运维·容器·kubernetes·云计算·kubepi
花落已飘2 小时前
openEuler WSL2容器化开发实战:Docker Desktop集成与应用部署
运维·docker·容器
伊卡洛斯az3 小时前
vim的跳转看头文件与分屏
linux·编辑器·vim
paopao_wu4 小时前
DeepSeek-OCR实战(01):基础运行环境搭建-Ubuntu
linux·人工智能·ubuntu·ai·ocr
betazhou4 小时前
基于Linux环境使用ogg19版本从oracle 19c ADG备库远程同步数据
linux·运维·oracle·goldengate·adg·远程抽取
路由侠内网穿透.4 小时前
本地部署消息代理软件 RabbitMQ 并实现外部访问( Windows 版本 )
linux·运维·服务器·远程工作
wanhengidc4 小时前
海外云手机是指什么
运维·服务器·游戏·智能手机·云计算