教你如何用c语言做俄罗斯方块小游戏

  #ifndef _HEAD_H_

  #define _HEAD_H_

  #include<graphics.h>

  #include<stdio.h>

  #include<conio.h>

  #include<stdlib.h>

  #include<time.h>

  #include<string.h>

  #define _CRT_SECURE_NO_WARNINGS 1

  //界面的相关的参数

  #define WALL_SQUARE_WIDTH 10 //围墙方块的宽度

  #define xWALL_SQUARE_NUM 30 //x轴方向的方块的数目

  #define yWALL_SQUARE_WIDTH 46 //y轴方向的方块的数目

  #define GAME_WALL_WIDTH? (WALL_SQUARE_WIDTH*xWALL_SQUARE_NUM) //游戏区域的宽度 300

  #define GAME_WALL_HTGH (WALL_SQUARE_WIDTH*yWALL_SQUARE_WIDTH) //游戏区域的高度 460

  #define WINDOW_WIDTH 480 // 游戏总窗口宽度 480

  #define WINDOW_HIGH 460? // 游戏总窗口高度 460

  //定义俄罗斯方块的相关参数

  #define ROCK_SQUARE_WIDTH (2*WALL_SQUARE_WIDTH) //俄罗斯方块的大小是围墙的两倍 20

  #define xROCK_SQUARE_NUM? ((GAME_WALL_WIDTH -20)/ROCK_SQUARE_WIDTH) // 游戏区x轴放的方块数目:14

  #define yROCK_SQUARE_NUM? ((GAME_WALL_HTGH -20)/ROCK_SQUARE_WIDTH)? // 游戏区y轴放的方块数目:22

  //定义移动方块的相关操作

  #define DIRECT_UP? ? 3

  #define DIRECT_DOWN? 2

  #define DIRECT_LEFT? -1

  #define DIRECT_RIGHT 1

  /*数据结构-线性表(结构体数组)*/

  typedef struct ROCK

  {

  //用来表示方块的形状(每一个字节是8位,用每4位表示方块中的一行)

  unsigned short rockShapeBits;

  int? ? ? ? ? nextRockIndex;? //下一个方块,在数组中的下标

  } RockType;

  //方块在图形窗口中的位置(即定位4*4大块的左上角坐标)

  typedef struct LOCATE

  {

  int left;

  int top;

  } RockLocation_t;

  //全局变量-游戏板的状态描述(即表示当前界面哪些位置有方块)

  //0表示没有,1表示有(多加了两行和两列,形成一个围墙,便于判断方块是否能够移动)

  int game_board[yROCK_SQUARE_NUM + 2][xROCK_SQUARE_NUM + 2] = { 0 };

  int game_socres = 0; //全局分数

  // 把俄罗斯方块的19种类放到数组中

  int rockTypeNum = 19;

  RockType RockArray[19] = { (0, 0) };

  //预览区的方块的位置

  RockLocation_t preRockLocation = {GAME_WALL_WIDTH+70,70};

  //每次生成初始化方块的位置

  RockLocation_t initRockLocation = { (WALL_SQUARE_WIDTH + 100), WALL_SQUARE_WIDTH };

  //分数显示的位置

  //各个文件中的函数

  // 画出界面以及画出方块Draw.h中

  void DrawGameWindow();

  void DisplayRock(int rockIdx, RockLocation_t*? LocatePtr, bool displayed);

  //初始化Init源文件

  void InitGame();

  //game.h

  void PlayGame();

  bool IsGameOver();

  #endif

  #include"Head.h"

  //画出游戏界面

  void DrawGameWindow()

  {

  //先画出围墙

  setcolor(BLUE);

  setlinestyle(PS_SOLID,NULL,0);

  setfillcolor(BLUE);

  //画出上下围墙

  for (int x = WALL_SQUARE_WIDTH; x <= GAME_WALL_WIDTH; x += WALL_SQUARE_WIDTH)

  {

  fillrectangle(x - WALL_SQUARE_WIDTH, 0, x, WALL_SQUARE_WIDTH); //上

  fillrectangle(x - WALL_SQUARE_WIDTH, GAME_WALL_HTGH - WALL_SQUARE_WIDTH, x, GAME_WALL_HTGH);//下

  }

  //画出左右围墙

  for (int y = WALL_SQUARE_WIDTH; y <= GAME_WALL_HTGH; y += WALL_SQUARE_WIDTH)

  {

  fillrectangle(0, y, WALL_SQUARE_WIDTH, y + WALL_SQUARE_WIDTH);//左

  fillrectangle(GAME_WALL_WIDTH - WALL_SQUARE_WIDTH, y, GAME_WALL_WIDTH, y + WALL_SQUARE_WIDTH);//右

  }

  //画出右边统计分数及相关东西

  //画出分割线

  setcolor(WHITE);

  setlinestyle(PS_DASH,2);

  line(GAME_WALL_WIDTH + 20, 0, GAME_WALL_WIDTH + 20, GAME_WALL_HTGH);

  //设置字体

  LOGFONT font;

  gettextstyle(&font);

  settextstyle(18, 0, _T("宋体"));

  font.lfQuality = ANTIALIASED_QUALITY;//设置输出效果为抗锯齿

  //1显示预览形状

  outtextxy(GAME_WALL_WIDTH + 80, 30, _T("预览"));

  outtextxy(GAME_WALL_WIDTH + 80, 170, _T("分数"));

  outtextxy(GAME_WALL_WIDTH + 65, 250, _T("操作说明"));

  outtextxy(GAME_WALL_WIDTH + 40, 290, _T("w a s d控制方向"));

  outtextxy(GAME_WALL_WIDTH + 40, 335, _T("空格 暂停"));

  //显示分数

  setcolor(RED);

  outtextxy(GAME_WALL_WIDTH + 90, 200, '0');

  }

  //在游戏区显示编号为rockIdx的方块

  void DisplayRock(int rockIdx,? RockLocation_t*? LocatePtr, bool displayed)

  {

  int color;//方块的填充颜色

  int lineColor = WHITE;//线的颜色

  int boardFalg = 0;

  int xRock = 0;

  int yRock = 0;

  unsigned short rockCode = RockArray[rockIdx].rockShapeBits;

  //如果displayed为true的话,将方块块颜色设置为white,game_board对应的位置设置为1;

  //如果displayed为false的话,将方块块颜色设置为black,game_board对应的位置设置为0;

  displayed ? (color = RED, boardFalg = 1) : (color = BLACK,lineColor = BLACK, boardFalg = 0);

  setcolor(lineColor);

  setfillcolor(color);

  setlinestyle(PS_SOLID);//设置为实线,

  xRock = LocatePtr->left;

  yRock = LocatePtr->top;

  int count = 0;//每4个换行,记录坐标偏移量

  unsigned short mask = 1;

  for (int i = 1; i <= 16; ++i)

  {

  mask = 1 << (16 - i);

  if ((rockCode & mask) != 0) //如果不为0的话,就画出小方块

  {

  fillrectangle(xRock , yRock, xRock + ROCK_SQUARE_WIDTH, yRock + ROCK_SQUARE_WIDTH);

  }

  if (i % 4 == 0) //换行

  {

  yRock = yRock + ROCK_SQUARE_WIDTH;

  xRock = xRock = LocatePtr->left;

  }

  else

  {

  xRock += ROCK_SQUARE_WIDTH;

  }

  }

  }

  #include"Head.h"

  static void ShapeStrToBit(unsigned char *rockShapeStr, unsigned short& rockShapeBit);

  static void ReadRcok();

  void InitGame()

  {

  //把全局游戏游戏版初始化,边界初始化为1

  for (int i = 0; i < xROCK_SQUARE_NUM + 2; i++)

  {

  game_board[0][i] = 1;? //上边界

  game_board[yROCK_SQUARE_NUM + 1][i] = 1; //下边界

  }

  for (int i = 0; i < yROCK_SQUARE_NUM + 2; i++)

  {

  game_board[i][0] = 1 ; //左边界

  game_board[i][xROCK_SQUARE_NUM + 1] = 1; //右边界

  }

  //读取俄罗斯方块

  ReadRcok();

  }

  //从文件中读取方块的形状存储到rockArray中

  void ReadRcok()

  {

  FILE* fp = fopen("RockShape.ini","r");

  if (NULL == fp)

  {

  printf("打开文件失败

  ");

  return;

  }

  unsigned char readBuf[1024]; //fp读取到字符串readbuf中

  unsigned short rockShapeBit = 0;//存放方块形状,占16比特位

  unsigned char rockShapeStr[16];//存放方块字符串

  int ShapeStrIdx = 0;

  int rockNum = 0;//统计方块的个数以及存放方块数组RockArray的下标

  int rocknext = 0;//方块数组中下一个形状

  int rockShapeStart = 0;//同一类型的形状

  while (true)

  {

  size_t readSize = fread(readBuf, 1, 1024, fp);

  if (readSize == 0)

  break;

  //处理readbuf

  for (size_t idx = 0; idx < readSize; ++idx)

  {

  //将字符存放到rockShapeStr中

  while (ShapeStrIdx < 16 && idx < readSize)

  {

  if (readBuf[idx] == '@' || readBuf[idx] == '#')

  {

  rockShapeStr[ShapeStrIdx] = (unsigned char)readBuf[idx];

  ++ShapeStrIdx;

  }

  ++idx; //可能idx == readSize了

  if (readBuf[idx] == '*')//修改上一次方块的next值

  {

  idx += 5;

  RockArray[--rockNum].nextRockIndex = rockShapeStart;

  rockNum++;

  rockShapeStart = rockNum;

  rocknext = rockShapeStart ;

  }

  }

  //可能没有填满

  if (ShapeStrIdx < 16)

  {

  break;

  }

  else //填满shapestr

  {

  ShapeStrIdx = 0;//置0

  //将rockShapeStr 转为rockShapeBit

  ShapeStrToBit(rockShapeStr, rockShapeBit);

  rocknext++;

  RockArray[rockNum].rockShapeBits = rockShapeBit;

  RockArray[rockNum].nextRockIndex = rocknext;

  rockNum++;

  }

  }

  }

  fclose(fp);

  }

  //将从文件中读取的字符串(长度默认为16)转换成 unsigned short

  void ShapeStrToBit(unsigned char *rockShapeStr, unsigned short& rockShapeBit)

  {

  rockShapeBit = 0;

  for (size_t idx = 0; idx < 16; ++idx)

  {

  if (rockShapeStr[idx] == '@') //1

  {

  rockShapeBit |= (1 << (16 - idx - 1));

  }

  // #为0 不需要处理

  }

  }

  #include"Head.h"

  #define _CRT_SECURE_NO_WARNINGS 1

  bool MoveAble(int rockIndex, RockLocation_t* currentLocatePtr, int f_direction);

  void SetGameBoardFlag(int rockIdx, RockLocation_t* curRockLocation);

  void UserHitKeyBoard(char userHit, int* RockIndex, RockLocation_t* curRockLocation);

  void FullLine();

  void UpdateSocres(int scores);

  void DelCurLine(int rowIdx);

  bool IsGameOver();

  void PlayGame()

  {

  char userHit = 0;//用户敲击键盘

  int curRockIndex = 0;//当前方块的rockArray下标

  int nextRockIndex = 0;//下次

  RockLocation_t curRockLocation;

  curRockLocation.left = initRockLocation.left;

  curRockLocation.top = initRockLocation.top;

  DWORD oldtime = 0;

  srand((unsigned int)time(NULL));

  curRockIndex = rand() % rockTypeNum;

  nextRockIndex = rand() % rockTypeNum;

  //画出预览区初始化方块

  //在初始位置和预览区显示方块形状

  DisplayRock(curRockIndex, &initRockLocation, 1);

  DisplayRock(nextRockIndex, &preRockLocation, 1);

  bool moveAbled = false;

  while (true)

  {

  //判断当前方块是否落地(判断能否再下移):如果落地,判断是否满行,再判断是否结束游戏, 改变game_board ,画出下次初始化的方块,以及生成新的预览方块

  //

  moveAbled = MoveAble(curRockIndex, &curRockLocation, DIRECT_DOWN);

  if (!moveAbled) //判断是否落地,不能下移表示落地

  {

  //修改game_board的值

  SetGameBoardFlag(curRockIndex, &curRockLocation);

  FullLine();

  if (IsGameOver())

  {

  MessageBox(NULL, _T("游戏结束"), _T("GAME OVER"), MB_OK);

  exit(0);

  }

  //为下次生成模块开始准备

  DisplayRock(nextRockIndex, &preRockLocation, false);//擦除旧的方块

  curRockIndex = nextRockIndex;

  nextRockIndex = rand() % rockTypeNum; //生成新的预览方块

  DisplayRock(curRockIndex, &initRockLocation, 1);

  DisplayRock(nextRockIndex, &preRockLocation, 1);

  FlushBatchDraw();

  //修改curRockLocation的值

  curRockLocation.left = initRockLocation.left;

  curRockLocation.top = initRockLocation.top;

  }

  if (kbhit()) //如果敲击键盘了 就处理按键

  {

  userHit = getch();

  UserHitKeyBoard(userHit, &curRockIndex, &curRockLocation);

  }

  //没有 就自动下移一个单位 :不能用else,因为可能按键不是上下左右

  DWORD newtime = GetTickCount();

  if (newtime - oldtime >= (unsigned int)(300) && moveAbled == TRUE)

  {

  oldtime = newtime;

  DisplayRock(curRockIndex, &curRockLocation, false);

  curRockLocation.top += ROCK_SQUARE_WIDTH; //下落一格

  }

  //AutomaticDownMove(curRockIndex, &curRockLocation);

  //画出新方块

  DisplayRock(curRockIndex, &curRockLocation, 1);

  FlushBatchDraw();

  Sleep(20);

  }

  }

  //响应键盘命令时间

  void UserHitKeyBoard(char userHit, int* RockIndex, RockLocation_t* curRockLocation)

  {

  switch (userHit)

  {

  case 'W':

  case 'w'://↑

  if (MoveAble(RockArray[*RockIndex].nextRockIndex, curRockLocation, DIRECT_UP))

  {

  DisplayRock(*RockIndex, curRockLocation, false);

  *RockIndex = RockArray[*RockIndex].nextRockIndex;

  }

  break;

  case 'S':

  case 's'://↓

  if (MoveAble(*RockIndex, curRockLocation, DIRECT_DOWN))

  {

  DisplayRock(*RockIndex, curRockLocation, false);

  curRockLocation->top += 2 * (ROCK_SQUARE_WIDTH);

  if (!MoveAble(*RockIndex, curRockLocation, DIRECT_DOWN))

  {

  curRockLocation->top -= ROCK_SQUARE_WIDTH;

  }

  }

  break;

  case 'A':

  case 'a': //←

  if (MoveAble(*RockIndex, curRockLocation, DIRECT_LEFT))

  {

  DisplayRock(*RockIndex, curRockLocation, false);

  curRockLocation->left -= ROCK_SQUARE_WIDTH;

  }

  break;

  case 'D':

  case 'd': //→

  if (MoveAble(*RockIndex, curRockLocation, DIRECT_RIGHT))

  {

  DisplayRock(*RockIndex, curRockLocation, FALSE);

  curRockLocation->left += ROCK_SQUARE_WIDTH;

  }

  break;

  case ' ': //暂停

  while (1)

  {

  userHit = getch();

  if (userHit == ' ')

  break;

  }

  break;

  default:

  break;

  }

  }

  //判断是否满行,满行消除,然后计算得分

  void FullLine()

  {

  bool linefull = true;

  int idx = yROCK_SQUARE_NUM;//从最后一行往上查找 22

  int count = 0;

  while (count != xROCK_SQUARE_NUM ) //遇到空行 14

  {

  linefull = true;

  count = 0;

  for (int i = 1; i <= xROCK_SQUARE_NUM; ++i)

  {

  if (game_board[idx][i] == 0)

  {

  linefull = false;

  count++;

  }

  }

  if (linefull) //满行,消除当前行,更新分数

  {

  DelCurLine(idx);

  game_socres += 3;

  UpdateSocres(game_socres);

  idx++;//因为下面要减1

  }

  idx--;

  }

  }

  void UpdateSocres(int scores)

  {

  setcolor(RED);

  TCHAR s[10];

  _stprintf(s, _T("%d"), scores);

  outtextxy(GAME_WALL_WIDTH + 90, 200, s);

  }

  //消除当前行

  void DelCurLine(int rowIdx)

  {

  //擦除当前行

  setcolor(BLACK);

  setfillcolor(BLACK);

  for (int i = 1; i < xROCK_SQUARE_NUM; ++i)

  {

  fillrectangle(WALL_SQUARE_WIDTH + (i - 1)*ROCK_SQUARE_WIDTH, (rowIdx - 1)*ROCK_SQUARE_WIDTH + WALL_SQUARE_WIDTH,

  WALL_SQUARE_WIDTH + i*ROCK_SQUARE_WIDTH, rowIdx*ROCK_SQUARE_WIDTH + WALL_SQUARE_WIDTH);

  }

  //把上面的向下移

  int cnt = 0;

  while (cnt != xROCK_SQUARE_NUM) //直到遇到是空行的为止

  {

  cnt = 0;

  for (int i = 1; i <= xROCK_SQUARE_NUM; i++)

  {

  game_board[rowIdx][i] = game_board[rowIdx - 1][i];

  //擦除上面的一行

  setcolor(BLACK);

  setfillcolor(BLACK);

  fillrectangle(WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*i - ROCK_SQUARE_WIDTH ,

  WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*(rowIdx - 1) - ROCK_SQUARE_WIDTH ,

  WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*i,

  WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*(rowIdx - 1));

  //显示下面的一行

  if (game_board[rowIdx][i] == 1)

  {

  setcolor(WHITE);

  setfillcolor(RED);

  fillrectangle(WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*i - ROCK_SQUARE_WIDTH ,

  WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*rowIdx - ROCK_SQUARE_WIDTH ,

  WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*i,

  WALL_SQUARE_WIDTH + ROCK_SQUARE_WIDTH*rowIdx);

  }

  if (game_board[rowIdx][i] == 0)

  cnt++;? ? ? ? ? ? //统计一行是不是 都是空格

  }//for

  rowIdx--;

  }

  }

  //是否可以移动方块

  bool MoveAble(int rockIndex, RockLocation_t* currentLocatePtr, int f_direction)

  {

  int mask;

  int rockX;

  int rockY;

  rockX = currentLocatePtr->left;

  rockY = currentLocatePtr->top;

  mask = (unsigned short)1 << 15;

  for (int i = 1; i <= 16; i++)

  {

  //与掩码相与为1的 即为方块上的点

  if ((RockArray[rockIndex].rockShapeBits & mask) != 0)

  {

  //判断能否移动(即扫描即将移动的位置 是否与设置的围墙有重叠)

  //若是向上(即翻滚变形)

  if (f_direction == DIRECT_UP)

  {

  //因为此情况下传入的是下一个方块的形状,故我们直接判断此方块的位置是否已经被占

  //判断下一个方块

  if (game_board[(rockY - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1]

  [(rockX - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] == 1)

  return false;

  }

  //如果是向下方向移动

  else if (f_direction == DIRECT_DOWN)

  {

  if (game_board[(rockY - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 2]

  [(rockX - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] == 1)

  return false;

  }

  else //如果是左右方向移动

  {? ?//f_direction的DIRECT_LEFT为-1,DIRECT_RIGHT为1,故直接加f_direction即可判断。

  if (game_board[(rockY - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1]

  [(rockX - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1 + f_direction] == 1)

  return false;

  }

  }

  //每4次 换行 转到下一行继续

  i % 4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = currentLocatePtr->left)

  : rockX += ROCK_SQUARE_WIDTH;

  mask >>= 1;

  }

  return true;

  }

  //给游戏game_board设置标记表示已经占了

  void SetGameBoardFlag(int rockIdx, RockLocation_t* curRockLocation)

  {

  int mask;

  int rockX;

  int rockY;

  rockX = curRockLocation->left;

  rockY = curRockLocation->top;

  mask = (unsigned int)1 << 15;

  for (int i = 1; i <= 16; i++)

  {

  //与掩码相与为1的 即为方块上的点

  if ((RockArray[rockIdx].rockShapeBits & mask) != 0)

  {

  game_board[(rockY - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1]

  [(rockX - WALL_SQUARE_WIDTH) / ROCK_SQUARE_WIDTH + 1] = 1;

  }

  //每4次 换行 转到下一行继续画

  i % 4 == 0 ? (rockY += ROCK_SQUARE_WIDTH, rockX = curRockLocation->left)

  : rockX += ROCK_SQUARE_WIDTH;

  mask >>= 1;

  }

  }

  //判断游戏是否结束

  bool IsGameOver()

  {

  bool topLineHaveRock = false;//顶行是否有方块

  bool bottomLineHaveRock = false;

  for (int i = 1; i < xROCK_SQUARE_NUM; ++i)

  {

  if (game_board[1][i] == 1)

  topLineHaveRock = true;

  if (game_board[yROCK_SQUARE_NUM][i] == 1)

  bottomLineHaveRock = true;

  }

  if (bottomLineHaveRock && topLineHaveRock)

  return true;

  else

  return false;

  }

  #include"Head.h"

  #include"Draw.h"

  #include"Init.h"

  #include"game.h"

  int main()

  {

  initgraph(WINDOW_WIDTH,WINDOW_HIGH);

  DrawGameWindow();

  //使用 API 函数修改窗口名称

  HWND hWnd = GetHWnd();

  SetWindowText(hWnd, _T("俄罗斯方块"));

  InitGame();

  PlayGame();

  getchar();

  closegraph();

  system("pause");

  return? 0;

  }

  @###

  @###

  @@##

  ####

  @@@#

  @###

  ####

  ####

  @@##

  #@##

  #@##

  ####

  ##@#

  @@@#

  ####

  ####

  ****

  #@##

  #@##

  @@##

  ####

  @###

  @@@#

  ####

  ####

  @@##

  @###

  @###

  ####

  @@@#

  ##@#

  ####

  ####

  ****

  #@##

  @@@#

  ####

  ####

  @###

  @@##

  @###

  ####

  @@@#

  #@##

  ####

  ####

  #@##

  @@##

  #@##

  ####

  ****

  #@##

  @@##

  @###

  ####

  @@##

  #@@#

  ####

  ####

  ****

  @###

  @@##

  #@##

  ####

  #@@#

  @@##

  ####

  ####

  ****

  @###

  @###

  @###

  @###

  @@@@

  ####

  ####

  ####

  ****

  @@##

  @@##

  ####

  ####

  ****