【C++项目实现】俄罗斯方块

简介: 【C++项目实现】俄罗斯方块

初始化页面

# include <stdio.h> 
# include <graphics.h> 
// 欢迎界面
void welcome() {
  // 初始化画布
  initgraph(550, 660);
  // 设置窗口标题
  HWND window = GetHWnd(); //获取窗口
  SetWindowText(window, _T("俄罗斯方块")); //设置窗口标题
  // 设置文本的字体样式
  setfont(40, 0, _T("微软雅黑")); //设置文字样式(0表示自适应)
  setcolor(WHITE);
  outtextxy(205, 200, _T("俄罗斯方块"));
  setfont(22, 0, _T("楷体"));
  outtextxy(150, 300, _T("适度游戏益脑,沉迷游戏伤身"));
  Sleep(3000); //睡眠(暂停)3000毫秒,3秒针
}

初始化游戏环境

int score = 0; // 总分
int rank = 0;  //等级
// 初始化游戏场景
void initGameScene()
{
  char str[16];
  cleardevice();
  setcolor(WHITE);
  rectangle(29, 29, 334, 633);
  rectangle(27, 27, 336, 635);
  rectangle(370, 50, 515, 195);
  setfont(24, 0, _T("楷体"));
  setcolor(LIGHTGRAY);
  outtextxy(405, 215, _T("下一个:"));
  setcolor(RED);
  outtextxy(405, 280, _T("分数:"));
  sprintf(str, "%d", score);
  outtextxy(415, 310, str);
  outtextxy(405, 375, _T("等级:"));
  sprintf(str, "%d", rank);
  outtextxy(425, 405, str);
  setfont(22, 0, _T("楷体"));
  setcolor(LIGHTBLUE);
  outtextxy(390, 475, _T("操作说明:"));
  outtextxy(390, 500, _T("↑: 旋转"));
  outtextxy(390, 525, _T("↓: 下降"));
  outtextxy(390, 550, _T("←: 左移"));
  outtextxy(390, 575, _T("→: 右移"));
  outtextxy(390, 600, _T("空格: 暂停"));
}

新方块

#define  BLOCK_COUNT      5
#define  BLOCK_WIDTH    5
#define  BLOCK_HEIGHT       5
#define  UNIT_SIZE        20  //小砖块的宽度和高度
int color[BLOCK_COUNT] = {
  GREEN,CYAN,MAGENTA,BROWN,YELLOW
};
int NextIndex = -1;
int block[BLOCK_COUNT * 4][BLOCK_HEIGHT][BLOCK_WIDTH] = {
  // | 形方块
  { 0,0,0,0,0,
  0,0,1,0,0,
  0,0,1,0,0,
  0,0,1,0,0,
  0,0,0,0,0 },
  { 0,0,0,0,0,
  0,0,0,0,0,
  0,1,1,1,0,
  0,0,0,0,0,
  0,0,0,0,0 },
  { 0,0,0,0,0,
  0,0,1,0,0,
  0,0,1,0,0,
  0,0,1,0,0,
  0,0,0,0,0 },
  { 0,0,0,0,0,
  0,0,0,0,0,
  0,1,1,1,0,
  0,0,0,0,0,
  0,0,0,0,0 },
  // L 形方块
  { 0,0,0,0,0,
  0,0,1,0,0,
  0,0,1,0,0,
  0,0,1,1,0,
  0,0,0,0,0 },
  { 0,0,0,0,0,
  0,0,0,0,0,
  0,1,1,1,0,
  0,1,0,0,0,
  0,0,0,0,0 },
  { 0,0,0,0,0,
  0,1,1,0,0,
  0,0,1,0,0,
  0,0,1,0,0,
  0,0,0,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,0 },
  // 田 形方块
  { 0,0,0,0,0,
  0,1,1,0,0,
  0,1,1,0,0,
  0,0,0,0,0,
  0,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,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,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,0,0,0 },
  // T 形方块
  { 0,0,0,0,0,
  0,1,1,1,0,
  0,0,1,0,0,
  0,0,0,0,0,
  0,0,0,0,0 },
  { 0,0,0,0,0,
  0,0,0,1,0,
  0,0,1,1,0,
  0,0,0,1,0,
  0,0,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,0,0 },
  { 0,0,0,0,0,
  0,1,0,0,0,
  0,1,1,0,0,
  0,1,0,0,0,
  0,0,0,0,0 },
  // Z 形方块
  { 0,0,0,0,0,
  0,1,1,0,0,
  0,0,1,1,0,
  0,0,0,0,0,
  0,0,0,0,0 },
  { 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,0,0,0,
  0,1,1,0,0,
  0,0,1,1,0,
  0,0,0,0,0,
  0,0,0,0,0 },
  { 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 },
};
//清除右上角提示区方块
void clearBlock() {
  setcolor(BLACK);
  setfont(23, 0, "楷体");
  for (int i = 0; i < BLOCK_HEIGHT; i++) {
    for (int j = 0; j < BLOCK_WIDTH; j++) {
      //"■"
      int x = 391 + j * UNIT_SIZE;
      int y = 71 + i * UNIT_SIZE;
      outtextxy(x, y, "■");
    }
  }
}
//绘制方块
void drawBlock(int x, int y) {
  setcolor(color[NextIndex]);
  setfont(23, 0, "楷体");
  for (int i = 0; i < BLOCK_HEIGHT; i++) {
    for (int j = 0; j < BLOCK_WIDTH; j++) {
      //"■"
      if (block[NextIndex * 4][i][j] == 1) {
        int x2 = x + j * UNIT_SIZE;
        int y2 = y + i * UNIT_SIZE;
        outtextxy(x2, y2, "■");
      }
    }
  }
}
//右上角提示区显示下一个方块
void nextblock() {
  clearBlock(); //清除右上角区域
  //随机选择一种方块
  srand(time(NULL)); //使用时间函数的返回值,来作为随机种子
  NextIndex = rand() % BLOCK_COUNT;
  drawBlock(391, 71);
}


降落方块

使用访问数组来确定是否有方块

int visit[30][15], Color[30][15]; // visit[i][j] == 1 表示该位置有方块


新方块降落

int BlockIndex = -1; //当前方块的种类
//新方块降落
void newblock() {
  //确定即将使用的方块的类别
  BlockIndex = NextIndex;
  //绘制刚从顶部下降的方块
  drawBlock(START_X, START_Y);
  //让新出现的方块暂停一会,让用户识别到
  Sleep(100); //0.1秒
  //在右上角区域,绘制下一个方块
  nextblock();
  //方块降落
  move();
}


方块降落

#define  START_X      130
#define  START_Y      30
#define KEY_UP      72
#define KEY_RIGHT   77
#define KEY_DOWN    80
#define KEY_LEFT    75
#define KEY_SPACE   32
int speed = 500;
int minX = 30;
int minY = 30;
int markColor[30][15]; //表示对应位置的颜色
typedef enum {
  BLOCK_UP,
  BLOCK_RIGHT,
  BLOCK_DOWN,
  BLOCK_LEFT
} block_dir_t;
typedef enum {
  MOVE_DOWN,
  MOVE_LEFT,
  MOVE_RIGHT
} move_dir_t;
//调整下降速度
void wait(int interval) {
  int count = interval / 10;
  for (int i = 0; i < count; i++) {
    Sleep(10);
    if (kbhit()) {
      return;
    }
  }
}
//标记已到位的方块
void mark(int x, int y, int blockIndex, block_dir_t dir) {
  int id = blockIndex * 4 + dir;
  int x2 = (y - minY) / 20;
  int y2 = (x - minX) / 20;
  for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
      if (block[id][i][j] == 1) {
        visit[x2 + i][y2 + j] = 1;
        markColor[x2 + i][y2 + j] = color[blockIndex];
      }
    }
  }
}
//如果在指定位置可以向指定方向移动,就返回1, 否则就返回0
int moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) {
  //计算当前方块的左上角在30x15的游戏区中的位置(第多少行,第多少列)
  int x = (y0 - minY) / UNIT_SIZE;
  int y = (x0 - minX) / UNIT_SIZE;
  int id = BlockIndex * 4 + blockDir;
  int ret = 1;
  if (moveDir == MOVE_DOWN) {
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 5; j++) {
        if (block[id][i][j] == 1 &&
          (x + i + 1 >= 30 || visit[x + i + 1][y + j] == 1)) {
          ret = 0;
        }
      }
    }
  }
  else if (moveDir == MOVE_LEFT) {
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 5; j++) {
        if (block[id][i][j] == 1 &&
          (y + j == 0 || visit[x + i][y + j - 1] == 1)) {
          ret = 0;
        }
      }
    }
  }
  else if (moveDir == MOVE_RIGHT) {
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 5; j++) {
        if (block[id][i][j] == 1 &&
          (y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) {
          ret = 0;
        }
      }
    }
  }
  return ret;
}
//判断当前方块是否可以转向到指定方向
//注意,此时还没有转到该方向!!!
int rotatable(int x, int y, block_dir_t dir) {
  int id = BlockIndex * 4 + dir;
  int xIndex = (y - minY) / 20;
  int yIndex = (x - minX) / 20;
  if (!moveable(x, y, MOVE_DOWN, dir)) {
    return 0;
  }
  for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
      if (block[id][i][j] == 1 &&
        (yIndex + j < 0 || yIndex + j >= 15 || visit[xIndex + i][yIndex + j] == 1)) {
        return 0;
      }
    }
  }
  return 1;
}
//方块降落
void move() {
  int x = START_X;
  int y = START_Y;
  int k = 0;
  block_dir_t  blockDir = BLOCK_UP;
  int curSpeed = speed;
  //检测游戏是否结束
  failCheck();
  //持续向下降落
  while (1) {
    if (kbhit()) {
      int key = getch();
      if (key == KEY_SPACE) {
        getch();
      }
    }
    //清除当前方块
    clearBlock(x, k, blockDir);
    if (kbhit()) {
      int key = getch();
      if (key == KEY_UP) {
        block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4);
        if (rotatable(x, y + k, nextDir)) {
          blockDir = nextDir;
        }
      }
      else if (key == KEY_DOWN) {
        curSpeed = 50;
      }
      else if (key == KEY_LEFT) {
        if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) {
          x -= 20;
        }
      }
      else if (key == KEY_RIGHT) {
        if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) {
          x += 20;  //x = x + 20;
        }
      }
    }
    k += 20;
    //绘制当前方块
    drawBlock(x, y + k, BlockIndex, blockDir);
    wait(curSpeed); //下降速度
    //方块的“固化”处理
    if (!moveable(x, y + k, MOVE_DOWN, blockDir)) {
      mark(x, y + k, BlockIndex, blockDir);
      break;
    }
  }
}


检测结果

//消除第x行,并把上面的行都下移
void down(int x) {
  for (int i = x; i > 0; i--) {
    //消除第i行,第j列的方格消除
    for (int j = 0; j < 15; j++) {
      if (visit[i - 1][j]) {
        visit[i][j] = 1;
        markColor[i][j] = markColor[i - 1][j];
        setcolor(markColor[i][j]);
        outtextxy(20 * j + minX, 20 * i + minY, "■");
      }
      else {
        visit[i][j] = 0;
        setcolor(BLACK);
        outtextxy(20 * j + minX, 20 * i + minY, "■");
      }
    }
  }
  //清除最顶上的哪一行(就是行标为0的那一行)
  setcolor(BLACK);
  for (int j = 0; j < 15; j++) {
    visit[0][j] = 0;
    outtextxy(20 * j + minX, minY, "■");
  }
}
//更新分数,参数lines表示消除的行数
void addScore(int lines) {
  char str[32];
  setcolor(RED);
  score += lines * 10;
  sprintf(str, "%d", score);
  outtextxy(415, 310, str);
}
//更新等级的提示
void updateGrade() {
  //假设:50分一级
  rank = score / 50;
  char str[16];
  sprintf(str, "%d", rank);
  outtextxy(425, 405, str);
  //更新速度, 等级越高,速度越快,speed越小!
  //最慢:500, 最快是100
  speed = 500 - rank * 100;
  if (speed <= 100) {
    speed = 100;
  }
}
//检查是否有行可以消除
void check() {
  int i, j;
  int clearLines = 0;
  for (i = 29; i >= 0; i--) {
    //检查第i行有没有满
    for (j = 0; j < 15 && visit[i][j]; j++);
    //执行到此处时,有两种情况:
    // 1. 第i行没有满,即表示有空位 此时 j<15
    // 2. 第i行已满了,此时 j>=15
    if (j >= 15) {
      //此时,第i行已经满了,就需要消除第i行
      down(i);  //消除第i行,并把上面的行都下移
      i++;  //因为最外层的循环中有 i--, 所以我们先i++, 使得下次循环时,再把这一行检查一下
      clearLines++;
    }
  }
  //更新分数
  addScore(clearLines);
  //更新等级(更新等级提示,更新速度)
  updateGrade();
}


全部代码

info.h

#pragma once
#include "block.h"
#define BLOCK_COUNT   5
#define BLOCK_WIDTH   5
#define BLOCK_HEIGHT  5
int block[BLOCK_COUNT * 4][BLOCK_HEIGHT][BLOCK_WIDTH] = {
  // |  型方块
  {
    0,0,0,0,0,
    0,0,1,0,0,
    0,0,1,0,0,
    0,0,1,0,0,
    0,0,0,0,0 },
  {
    0,0,0,0,0,
    0,0,0,0,0,
    0,1,1,1,0,
    0,0,0,0,0,
    0,0,0,0,0 },
  {  0,0,0,0,0,
    0,0,1,0,0,
    0,0,1,0,0,
    0,0,1,0,0,
    0,0,0,0,0 },
  { 0,0,0,0,0,
     0,0,0,0,0,
     0,1,1,1,0,
     0,0,0,0,0,
     0,0,0,0,0 },
     // L 形方块
     { 0,0,0,0,0,
     0,0,1,0,0,
     0,0,1,0,0,
     0,0,1,1,0,
     0,0,0,0,0 },
     { 0,0,0,0,0,
     0,0,0,0,0,
     0,1,1,1,0,
     0,1,0,0,0,
     0,0,0,0,0 },
     { 0,0,0,0,0,
     0,1,1,0,0,
     0,0,1,0,0,
     0,0,1,0,0,
     0,0,0,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,0 },
     // 田 形方块
     { 0,0,0,0,0,
     0,1,1,0,0,
     0,1,1,0,0,
     0,0,0,0,0,
     0,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,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,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,0,0,0 },
     // T 形方块
     { 0,0,0,0,0,
     0,1,1,1,0,
     0,0,1,0,0,
     0,0,0,0,0,
     0,0,0,0,0 },
     { 0,0,0,0,0,
     0,0,0,1,0,
     0,0,1,1,0,
     0,0,0,1,0,
     0,0,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,0,0 },
     { 0,0,0,0,0,
     0,1,0,0,0,
     0,1,1,0,0,
     0,1,0,0,0,
     0,0,0,0,0 },
     // Z 形方块
     { 0,0,0,0,0,
     0,1,1,0,0,
     0,0,1,1,0,
     0,0,0,0,0,
     0,0,0,0,0 },
     { 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,0,0,0,
     0,1,1,0,0,
     0,0,1,1,0,
     0,0,0,0,0,
     0,0,0,0,0 },
     { 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 }
};


block.h

#pragma once
#include <stdio.h>
#include <graphics.h>
#include <time.h>
#include <conio.h> //kbhit()使用
#include "info.h"
int score = 0; //总分
int rank = 0;  //等级
#define UNIT_SIZE       20
#define START_X     130
#define START_Y     30
#define KEY_UP      72
#define KEY_RIGHT   77
#define KEY_DOWN    80
#define KEY_LEFT    75
#define KEY_SPACE   32
int speed = 500;
int minX = 30;
int minY = 30;
typedef enum {
  BLOCK_UP,
  BLOCK_RIGHT,
  BLOCK_DOWN,
  BLOCK_LEFT
} block_dir_t;
typedef enum {
  MOVE_DOWN,
  MOVE_LEFT,
  MOVE_RIGHT
} move_dir_t;
int NextIndex = -1;  //下一个方块的种类
int BlockIndex = -1; //当前方块的种类
int color[BLOCK_COUNT] = {
  GREEN, CYAN,  MAGENTA, BROWN, YELLOW
};
int visit[30][15]; //访问数组
int markColor[30][15]; //表示对应位置的颜色
//欢迎界面
void welcome();
//初始化游戏场景
void initGameScene();
//绘制方块
void drawBlock(int x, int y);
//绘制方块:  在指定位置绘制指定方块的指定方向
void drawBlock(int x, int y, int blockIndex, block_dir_t dir);
//清除指定位置指定方向的方块
//参数x: 方块的左上角的x坐标
//参数y: 方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离
void clearBlock(int x, int y, block_dir_t dir);
//右上角提示区显示下一个方块
void nextblock();
//如果在指定位置可以向指定方向移动,就返回1, 否则就返回0
int moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir);
//检测游戏是否结束
void failCheck();
//判断当前方块是否可以转向到指定方向
//注意,此时还没有转到该方向!!!
int rotatable(int x, int y, block_dir_t dir);
//调整下降速度
void wait(int interval);
//标记已到位的方块
void mark(int x, int y, int blockIndex, block_dir_t dir);
//方块降落
void move();
//新方块降落
void newblock();
//消除第x行,并把上面的行都下移
void down(int x);
//更新分数,参数lines表示消除的行数
void addScore(int lines);
//更新等级的提示
void updateGrade();
//检查是否有行可以消除
void check();

block.cpp

#include "block.h"
int main() {
  welcome();
  initGameScene();
  //产生新方块
  nextblock();
  Sleep(500);
  //初始化访问数组
  memset(visit, 0, sizeof(visit));
  while (1) {
    newblock();
    //消除满行,并更新分数和速度
    check();
  }
  system("pause");
  closegraph();
  return 0;
}
//欢迎界面
void welcome() {
  //初始化画布
  initgraph(550, 660);
  //设置窗口标题
  HWND window = GetHWnd(); //获取窗口
  SetWindowText(window, _T("俄罗斯方块")); //设置窗口标题
  //设置文本的字体样式
  setfont(40, 0, _T("微软雅黑"));
  setcolor(WHITE);
  outtextxy(205, 200, _T("俄罗斯方块"));
  setfont(22, 0, _T("楷体"));
  outtextxy(150, 300, _T("适度游戏益脑,沉迷游戏伤身"));
  Sleep(3000); //睡眠(暂停)3000毫秒,3秒针
}
//初始化游戏场景
void initGameScene() {
  char str[16];
  //清除屏幕
  cleardevice();
  rectangle(27, 27, 336, 635);
  rectangle(29, 29, 334, 633);
  rectangle(370, 50, 515, 195);
  setfont(24, 0, _T("楷体"));
  setcolor(LIGHTGRAY);
  outtextxy(405, 215, _T("下一个"));
  setcolor(RED);
  outtextxy(405, 280, _T("分数"));
  sprintf(str, "%d", score);
  outtextxy(415, 310, str);
  outtextxy(405, 375, _T("等级"));
  sprintf(str, "%d", rank);
  outtextxy(425, 405, str);
  //操作说明  ↑  ↓ ← →
  setcolor(LIGHTBLUE);
  outtextxy(390, 475, "操作说明");
  outtextxy(390, 500, "↑:旋转");
  outtextxy(390, 525, "↓: 下降");
  outtextxy(390, 550, "←: 左移");
  outtextxy(390, 575, "→: 右移");
  outtextxy(390, 600, "空格:暂停");
}
//清除右上角提示区方块
void clearBlock() {
  setcolor(BLACK);
  setfont(23, 0, "楷体");
  for (int i = 0; i < BLOCK_HEIGHT; i++) {
    for (int j = 0; j < BLOCK_WIDTH; j++) {
      //"■"
      int x = 391 + j * UNIT_SIZE;
      int y = 71 + i * UNIT_SIZE;
      outtextxy(x, y, "■");
    }
  }
}
//绘制方块
void drawBlock(int x, int y) {
  setcolor(color[NextIndex]);
  setfont(23, 0, "楷体");
  for (int i = 0; i < BLOCK_HEIGHT; i++) {
    for (int j = 0; j < BLOCK_WIDTH; j++) {
      //"■"
      if (block[NextIndex * 4][i][j] == 1) {
        int x2 = x + j * UNIT_SIZE;
        int y2 = y + i * UNIT_SIZE;
        outtextxy(x2, y2, "■");
      }
    }
  }
}
//绘制方块:  在指定位置绘制指定方块的指定方向
void drawBlock(int x, int y, int blockIndex, block_dir_t dir) {
  setcolor(color[blockIndex]);
  setfont(23, 0, "楷体");
  int id = blockIndex * 4 + dir;
  for (int i = 0; i < BLOCK_HEIGHT; i++) {
    for (int j = 0; j < BLOCK_WIDTH; j++) {
      //"■"
      if (block[id][i][j] == 1) {
        int x2 = x + j * UNIT_SIZE;
        int y2 = y + i * UNIT_SIZE;
        outtextxy(x2, y2, "■");
      }
    }
  }
}
//清除指定位置指定方向的方块
//参数x: 方块的左上角的x坐标
//参数y: 方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离
void clearBlock(int x, int y, block_dir_t dir) {
  setcolor(BLACK);
  int id = BlockIndex * 4 + dir;
  y += START_Y;
  for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
      if (block[id][i][j] == 1) {
        //擦除该方块的第i行的第j列
        outtextxy(x + 20 * j, y + i * 20, "■");
      }
    }
  }
}
//右上角提示区显示下一个方块
void nextblock() {
  clearBlock(); //清除右上角区域
  //随机选择一种方块
  srand(time(NULL)); //使用时间函数的返回值,来作为随机种子
  NextIndex = rand() % BLOCK_COUNT;
  drawBlock(391, 71);
}
//如果在指定位置可以向指定方向移动,就返回1, 否则就返回0
int moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) {
  //计算当前方块的左上角在30x15的游戏区中的位置(第多少行,第多少列)
  int x = (y0 - minY) / UNIT_SIZE;
  int y = (x0 - minX) / UNIT_SIZE;
  int id = BlockIndex * 4 + blockDir;
  int ret = 1;
  if (moveDir == MOVE_DOWN) {
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 5; j++) {
        if (block[id][i][j] == 1 &&
          (x + i + 1 >= 30 || visit[x + i + 1][y + j] == 1)) {
          ret = 0;
        }
      }
    }
  }
  else if (moveDir == MOVE_LEFT) {
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 5; j++) {
        if (block[id][i][j] == 1 &&
          (y + j == 0 || visit[x + i][y + j - 1] == 1)) {
          ret = 0;
        }
      }
    }
  }
  else if (moveDir == MOVE_RIGHT) {
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 5; j++) {
        if (block[id][i][j] == 1 &&
          (y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) {
          ret = 0;
        }
      }
    }
  }
  return ret;
}
//检测游戏是否结束
void failCheck() {
  if (!moveable(START_X, START_Y, MOVE_DOWN, BLOCK_UP)) {
    setcolor(WHITE);
    setfont(45, 0, "隶体");
    outtextxy(75, 300, "GAME OVER!");
    Sleep(1000);
    system("pause");
    closegraph();
    exit(0);
  }
}
//判断当前方块是否可以转向到指定方向
//注意,此时还没有转到该方向!!!
int rotatable(int x, int y, block_dir_t dir) {
  int id = BlockIndex * 4 + dir;
  int xIndex = (y - minY) / UNIT_SIZE;
  int yIndex = (x - minX) / UNIT_SIZE;
  if (!moveable(x, y, MOVE_DOWN, dir)) {
    return 0;
  }
  for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
      if (block[id][i][j] == 1 &&
        (yIndex + j < 0 || yIndex + j >= 15 || visit[xIndex + i][yIndex + j] == 1)) {
        return 0;
      }
    }
  }
  return 1;
}
//调整下降速度
void wait(int interval) {
  int count = interval / 10;
  for (int i = 0; i < count; i++) {
    Sleep(10);
    if (kbhit()) {
      return;
    }
  }
}
//标记已到位的方块
void mark(int x, int y, int blockIndex, block_dir_t dir) {
  int id = blockIndex * 4 + dir;
  int x2 = (y - minY) / 20;
  int y2 = (x - minX) / 20;
  for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
      if (block[id][i][j] == 1) {
        visit[x2 + i][y2 + j] = 1;
        markColor[x2 + i][y2 + j] = color[blockIndex];
      }
    }
  }
}
//方块降落
void move() {
  int x = START_X;
  int y = START_Y;
  int k = 0;
  block_dir_t  blockDir = BLOCK_UP;
  int curSpeed = speed;
  //检测游戏是否结束
  failCheck();
  //持续向下降落
  while (1) {
    if (kbhit()) {
      int key = getch();
      if (key == KEY_SPACE) {
        getch();
      }
    }
    //清除当前方块
    clearBlock(x, k, blockDir);
    if (kbhit()) {
      int key = getch();
      if (key == KEY_UP) {
        block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4);
        if (rotatable(x, y + k, nextDir)) {
          blockDir = nextDir;
        }
      }
      else if (key == KEY_DOWN) {
        curSpeed = 50;
      }
      else if (key == KEY_LEFT) {
        if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) {
          x -= 20;
        }
      }
      else if (key == KEY_RIGHT) {
        if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) {
          x += 20;  //x = x + 20;
        }
      }
    }
    k += 20;
    //绘制当前方块
    drawBlock(x, y + k, BlockIndex, blockDir);
    wait(curSpeed);
    //方块的“固化”处理
    if (!moveable(x, y + k, MOVE_DOWN, blockDir)) {
      mark(x, y + k, BlockIndex, blockDir);
      break;
    }
  }
}
//新方块降落
void newblock() {
  //确定即将使用的方块的类别
  BlockIndex = NextIndex;
  //绘制刚从顶部下降的方块
  drawBlock(START_X, START_Y);
  //让新出现的方块暂停一会,让用户识别到
  Sleep(100); //0.1秒
  //在右上角区域,绘制下一个方块
  nextblock();
  //方块降落
  move();
}
//消除第x行,并把上面的行都下移
void down(int x) {
  for (int i = x; i > 0; i--) {
    //消除第i行,第j列的方格消除
    for (int j = 0; j < 15; j++) {
      if (visit[i - 1][j]) {
        visit[i][j] = 1;
        markColor[i][j] = markColor[i - 1][j];
        setcolor(markColor[i][j]);
        outtextxy(20 * j + minX, 20 * i + minY, "■");
      }
      else {
        visit[i][j] = 0;
        setcolor(BLACK);
        outtextxy(20 * j + minX, 20 * i + minY, "■");
      }
    }
  }
  //清除最顶上的哪一行(就是行标为0的那一行)
  setcolor(BLACK);
  for (int j = 0; j < 15; j++) {
    visit[0][j] = 0;
    outtextxy(20 * j + minX, minY, "■");
  }
}
//更新分数,参数lines表示消除的行数
void addScore(int lines) {
  char str[32];
  setcolor(RED);
  score += lines * 10;
  sprintf(str, "%d", score);
  outtextxy(415, 310, str);
}
//更新等级的提示
void updateGrade() {
  //假设:50分一级
  rank = score / 50;
  char str[16];
  sprintf(str, "%d", rank);
  outtextxy(425, 405, str);
  //更新速度, 等级越高,速度越快,speed越小!
  //最慢:500, 最快是100
  speed = 500 - rank * 100;
  if (speed <= 100) {
    speed = 100;
  }
}
//检查是否有行可以消除
void check() {
  int i, j;
  int clearLines = 0;
  for (i = 29; i >= 0; i--) {
    //检查第i行有没有满
    for (j = 0; j < 15 && visit[i][j]; j++);
    //执行到此处时,有两种情况:
    // 1. 第i行没有满,即表示有空位 此时 j<15
    // 2. 第i行已满了,此时 j>=15
    if (j >= 15) {
      //此时,第i行已经满了,就需要消除第i行
      down(i);  //消除第i行,并把上面的行都下移
      i++;  //因为最外层的循环中有 i--, 所以我们先i++, 使得下次循环时,再把这一行检查一下
      clearLines++;
    }
  }
  //更新分数
  addScore(clearLines);
  //更新等级(更新等级提示,更新速度)
  updateGrade();
}
目录
相关文章
|
8天前
|
编译器 C++ 开发者
【Conan 入门教程 】使用Conan 2.X和Autotools高效构建C/C++项目
【Conan 入门教程 】使用Conan 2.X和Autotools高效构建C/C++项目
90 1
|
8天前
|
算法 测试技术 数据处理
【C/C++ 面试技巧】如何在简单的项目里突出自己的价值?
【C/C++ 面试技巧】如何在简单的项目里突出自己的价值?
59 1
|
8天前
|
算法 测试技术 开发工具
编写高效技术文档的艺术:C++项目实践指南
编写高效技术文档的艺术:C++项目实践指南
81 0
|
8天前
|
开发工具 C语言 C++
CMake构建大型C/C++项目:跨平台设计与高级应用(二)
CMake构建大型C/C++项目:跨平台设计与高级应用
50 0
|
8天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
69 7
|
8天前
|
存储 C++ UED
【C++】飞机大战项目记录
通过上面的设计,我们实现来看飞机大战的主要功能
27 4
|
8天前
|
存储 安全 测试技术
【C++】string学习 — 手搓string类项目
C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。
19 0
【C++】string学习 — 手搓string类项目
|
8天前
|
C++
C++入门项目——通讯管理系统
C++入门项目——通讯管理系统
|
8天前
|
设计模式 存储 算法
C++从入门到精通:3.7阅读和理解开源项目——在实际项目中应用C++编程技巧和规范
C++从入门到精通:3.7阅读和理解开源项目——在实际项目中应用C++编程技巧和规范
|
8天前
C/C++test两步完成CMake项目静态分析
通过将C/C++test集成到CMake项目中,并根据项目的需要进行配置,可以在两步内完成CMake项目的静态分析。这样可以帮助开发人员及时发现并修复潜在的代码问题,提高代码质量和可靠性。
10 0