俄罗斯方块(简易实现)

简介: 俄罗斯方块(简易实现)
#include <stdio.h>
#include <graphics.h>
#include <Windows.h>
#include <time.h>
#include <conio.h>
int score=0;//总分
int rank=0;//等级
#define BLOCK_COUNT 5
#define BLOCK_WIDTH 5
#define BLOCK_HEIGHT 5
#define UNIT_SIZE 20
#define START_X 130
#define START_Y 30
#define KEY_UP 72
#define KEY_RIGHT 77
#define KEY_LEFT 75
#define KEY_DOWN 80
#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];//需要定义这个,因为要判断什么颜色的方块停留在这里(表示对应位置的颜色)
int block[BLOCK_COUNT*4][BLOCK_HEIGHT][BLOCK_WIDTH]=
{
  // | 形方块markColor
  { 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 welcome(void)
{ 
  //初始化画布
  initgraph(550,660);
  //设置窗口标题
  HWND window=GetHWnd();//获取窗口
  SetWindowText(window,"俄罗斯方块");//设置窗口标题
  //设置文本的字体样式
  setfont(40,0,"微软雅黑");
  setcolor(WHITE);
  outtextxy(205,200,"俄罗斯方块");
  setfont(22,0,"楷体");
  outtextxy(175,300,"编程,从俄罗斯方块开始!");
  Sleep(3000);
}
//初始化游戏场景
void initGameScene(void)
{
  char str[16];
  //清除屏幕
  cleardevice();
  setcolor(WHITE);
  rectangle(27,27,336,635);
  rectangle(29,29,334,633);
  rectangle(370,50,515,195);
  setfont(24,0,"楷体");
  setcolor(LIGHTGRAY);
  outtextxy(405,215,"下一个");
  setcolor(RED);
  outtextxy(405,280,"分数");
  sprintf(str,"%d",score);
  outtextxy(415,310,str);
  outtextxy(405,375,"等级");
  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,"■");
    }
  }
}
//清除指定位置指定方向的方块
//参数x:方块的左上角的x坐标
//参数y:方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离
void clearBlock(int x,int y,block_dir_t dir)
{
  setcolor(BLACK);
  int id=BlockIndex*4+dir;
  y=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+j*20,y+i*20,"■");
      }
    }
  }
}
//在右上角区域中,绘制下一个方块
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)
      {
        outtextxy(x+j*UNIT_SIZE,y+i*UNIT_SIZE,"■");//这点注意不要从新定义个x和y,否则会叠加值,致使方块位置不对
      }
    }
  }
}
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,"■");
      }
    }
  }
}
void nextblock()
{
  int x=391;
  int y=71;
  clearBlock();//清除右上角区域
  //随机选择一种方块
  srand(time(NULL));//使用时间函数的返回值,来作为随机种子
  NextIndex=rand()%BLOCK_COUNT;
  drawBlock(x,y);
}
//如果在指定位置可以向指定方向移动,就返回1,否则就返回0
int moveable(int x0,int y0,move_dir_t moveDir,block_dir_t blockDir)
{
  //计算当前方块的左上角在30*15的游戏区中的位置(第多少行,第多少列)
  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");
    exit(0);
  }
}
//判断当前方块是否可以转向到指定方向
//注意,此时还没有转到该方向!!!
int rotatable(int x,int y,block_dir_t dir)
{
  int xIndex=(y-minY)/20;
  int yIndex=(x-minX)/20;
  if(!moveable(x,y,MOVE_DOWN,dir))
  {
    return 0;
  }
  int id=BlockIndex*4+dir;
  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))//同时下降,所以要+20
        {
          x=x-20;
        }
      }else if(key==KEY_RIGHT)
      {
        if(moveable(x,y+k+20,MOVE_RIGHT,blockDir))
        {
          x=x+20;
        }
      }
    }
    k=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();
}
void down(int x)
{
  for(int i=x;i>0;i--)//这里i不能>=0,因为总是判断上一行,不能为-1
  {
    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的那一行),因为不能为-1,所有最顶行要单独判断
  setcolor(BLACK);
  for(int j=0;j<15;j++)
  {
    visit[0][j]=0;
    outtextxy(20*j+minX,minY,"■");
  }
}
void addScore(int lines)
{
  char str[32];
  setcolor(RED);
  score=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(void)
{
  int i,j;
  int clearLines=0;
  for(i=29;i>=0;i--)
  {
    for(j=0;j<15&&visit[i][j];j++); 
      //执行到此处时,有两种情况:
      //2.第i行已经满了,此时j>=15
      //1.第i行没有满,即表示有空位 此时就j<15
      if(j>=15)
      {
        //此时,第i行已经满了,就需要消除第i行
        down(i);//消除第i行,并把上面的行都下移
        i++;//因为最外层的循环中有i--,所以我们先i++,使得下次循环时,再把这一行检查以下
        clearLines++;
      }
  }
  //更新分数
  addScore(clearLines);
  //更新等级
  updateGrade();
}
int main(void)
{
  //设置游戏窗口
  welcome();
  //初始化游戏界面
  initGameScene();
  //产生新方块
  nextblock();
  Sleep(500);//在刚开始的时候等一会儿,让用户体验感更好
  //初始化访问数组
  memset(visit,0,sizeof(visit));
  while(1)
  {
    newblock();
    check();
  }
  system("pause");
  closegraph();
  return 0;
}

需要安装esayX,vc2010或者2019

 6cf33391300947b3a74996ba04b291c6.png


相关文章
|
8月前
|
安全 C++ Python
小游戏实战-Python实现石头剪刀布+扫雷小游戏
小游戏实战-Python实现石头剪刀布+扫雷小游戏
116 0
飞机大战
飞机大战完整版。
91 1
飞机大战-我机是否被击中(7)
编写飞机大战,完成我机是否被击中功能。
52 0
|
7月前
俄罗斯方块【附源码】
俄罗斯方块【附源码】
52 1
俄罗斯方块【附源码】
|
6月前
|
算法 定位技术 C语言
俄罗斯方块游戏编程
设计了一款基于AT89C51单片机的俄罗斯方块游戏,使用LCD12864作为显示界面,并通过按键控制方块移动与变形。系统包括电源、单片机最小系统、显示模块和按键模块。实现了方块随机生成、匀速下落及玩家交互控制等功能。游戏支持得分与计时显示,当方块堆至顶端无法消除时游戏结束。通过Proteus仿真验证了系统的功能性和稳定性,满足设计需求。提供的C代码片段展示了方块碰撞检测与地图检查逻辑。
|
8月前
|
算法 JavaScript Java
html+css+js实现打砖块小游戏
html+css+js实现打砖块小游戏
144 0
|
程序员 C语言
趣味益智小游戏 三子棋+五子棋 优化版(可任意选择棋盘大小)
趣味益智小游戏 三子棋+五子棋 优化版(可任意选择棋盘大小)
130 0
|
C++
【C++项目实现】俄罗斯方块
【C++项目实现】俄罗斯方块
111 0
|
存储 定位技术 开发工具
坦克大战小游戏的实现
坦克大战小游戏的实现
推箱子小游戏(c++实现)
推箱子小游戏(c++实现)

相关实验场景

更多