【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();
}
目录
相关文章
|
6月前
|
编译器 C++ 开发者
【Conan 入门教程 】使用Conan 2.X和Autotools高效构建C/C++项目
【Conan 入门教程 】使用Conan 2.X和Autotools高效构建C/C++项目
354 1
|
6月前
|
算法 测试技术 数据处理
【C/C++ 面试技巧】如何在简单的项目里突出自己的价值?
【C/C++ 面试技巧】如何在简单的项目里突出自己的价值?
177 1
|
6月前
|
算法 测试技术 开发工具
编写高效技术文档的艺术:C++项目实践指南
编写高效技术文档的艺术:C++项目实践指南
167 0
|
6月前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
229 7
WK
|
23天前
|
机器学习/深度学习 人工智能 算法
那C++适合开发哪些项目
C++ 是一种功能强大、应用广泛的编程语言,适合开发多种类型的项目。它在游戏开发、操作系统、嵌入式系统、科学计算、金融、图形图像处理、数据库管理、网络通信、人工智能、虚拟现实、航空航天等领域都有广泛应用。C++ 以其高性能、内存管理和跨平台兼容性等优势,成为众多开发者的选择。
WK
51 1
|
1月前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
310 3
|
2月前
|
C++
【C++案例】一个项目掌握C++基础-通讯录管理系统
这篇文章通过一个通讯录管理系统的C++项目案例,详细介绍了如何使用C++实现添加、显示、删除、查找、修改和清空联系人等功能。
44 3
|
4月前
|
Rust 测试技术 编译器
Rust与C++的区别及使用问题之Rust项目中组织目录结构的问题如何解决
Rust与C++的区别及使用问题之Rust项目中组织目录结构的问题如何解决
|
3月前
|
编译器 C++ 开发者
Visual Studio属性表:在新项目中加入已配置好的C++库
通过以上步骤可以确保Visual Studio中新项目成功地加入了之前已配置好的C++库。这个过程帮助开发者有效地管理多个项目中共享的库文件,提升开发效率。
103 0
|
4月前
|
Java C++ 开发者
如何根据项目需求选择使用C++还是Python进行内存管理?
【7月更文挑战第2天】如何根据项目需求选择使用C++还是Python进行内存管理?
48 0