C++实现俄罗斯方块

 更新时间:2020年4月25日 17:24  点击:1764

本文实例为大家分享了C++实现俄罗斯方块的具体代码,供大家参考,具体内容如下

工具:vc++2010,图库:EasyX

先看效果图片

纯手写,没有面向对象思想,看全部源码

#include <stdio.h>
#include <graphics.h>
#include <time.h>
#include <conio.h>

#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_SPACE 32
#define KEY_DOWN  76
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 speed = 500;
int NextIndex = -1;//下一个方块种类
int BlockIndex = -1;//当前方块种类
int score = 0;//分数
int rank = 0;//等级
int visit[30][15];//访问数组
int markcolor[30][15];//表示颜色
int minX = 30;
int minY = 30;
int color[BLOCK_COUNT]={
 GREEN,CYAN,MAGENTA,BROWN,YELLOW
};
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,0,1,0,0,
 0,0,1,1,0,
 0,0,1,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,0,1,0,
 0,0,1,1,0,
 0,0,1,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,0,1,0,
 0,0,1,1,0,
 0,0,1,0,0,
 0,0,0,0,0}
};

//欢迎界面
void welcome(){
 //初始化画布
 initgraph(550,660);

 //设置窗口标题
 HWND window = GetHWnd();//获取窗口
 SetWindowText(window,_T("俄罗斯方块 GWF"));//设置窗口标题


 //设置文本字体样式
 setfont(0,30,_T("微软雅黑"));
 setcolor(YELLOW);
 outtextxy(150,200,_T("俄罗斯方块"));
 setfont(0,10,_T("微软雅黑"));
 outtextxy(175,300,_T("IT编程从俄罗斯方块开始"));
 Sleep(3000);
 
}
//初始化游戏屏幕
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(415,405,str);
 setcolor(LIGHTBLUE);
 outtextxy(390,475,"操作说明");
 outtextxy(390,500,"↑:旋转");
 outtextxy(390,525,"↓:下降");
 outtextxy(390,550,"→:向右");
 outtextxy(390,575,"←:向左");
 outtextxy(390,600,"空格:暂停");



}
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+UNIT_SIZE*j,y+UNIT_SIZE*i,"■");
 }
  }
 }
}
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){
 outtextxy(x+UNIT_SIZE*j,y+UNIT_SIZE*i,"■");
 }
  }
 }
}

void clearBlock(int x, int y){
 setcolor(BLACK);
 setfont(23,0,"楷体");
 for(int i= 0;i<BLOCK_HEIGHT;i++){
 for(int j= 0;j<BLOCK_WIDTH;j++){
 outtextxy(x+UNIT_SIZE*j,y+UNIT_SIZE*i,"■");
  }
 }
}
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<BLOCK_HEIGHT;i++){
 for(int j= 0;j<BLOCK_WIDTH;j++){
 if(block[id][i][j] == 1){
 outtextxy(x+UNIT_SIZE*j,y+UNIT_SIZE*i,"■");
 }
  }
 }
}
void nextblock(){
 clearBlock(391,71);//清除右上角方块
 //随机选择一种方块
 srand(time(NULL));//时间函数的返回值产生随机数种子
 NextIndex = rand()%BLOCK_COUNT;
 drawBlock(391,71);//画出方块

}
//如果在指定位置可以向方向移动
int moveable(int x0,int y0,move_dir_t moveDir,block_dir_t blockDir){
 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 wait(int interval){//等待
 int count = interval/5;
 for(int i = 0;i<count;i++){
 Sleep(5);
 if(kbhit()){
 return 0;
 }
 }
}//判断当前方向是否可以转到指定方向
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 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 clear_down(int x){//消除第x行,并把上面的行都下移
 for(int i = x;i>0;i--){
 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,"■");
 }
}
void updataGrade(){
//更新等级提示
//假设:50分一级
 char str[10];
 rank = score/50;
 sprintf(str,"%d",rank);
 outtextxy(425,405,str);
 //更新速度,等级越高速度越快,speed越小
 //最慢是500,最快是50
 speed = 500-rank*50;
 if(speed<=0){
 speed = 50;
 }
}
void addScore(int lines){//更新分数,line表示消除的行数
 char str[32];
 setcolor(RED);
 score+=lines*10;
 sprintf(str,"%d",score);
 outtextxy(415,310,str);
}
void check(){//消去方块
 int i,j;
 int clearLines =0;
 for(i=29;i>=0;i--){
 for(j=0;j<15 && visit[i][j];j++);
 //执行到此处有两种情况,
 //1.第I行没有满,即表示有空位,此时j<15
 //2.第i行已经满了,j就大于等于15
 if(j>=15){
 //此时第i行已经满了,就需要消除第i行
 clear_down(i);//清除第i行,并把上面的行都下移动
 i++;
 clearLines++;
 }
 }
 //更新分数
 addScore(clearLines);
 //更新等级
 updataGrade();
}
void move(){
 int x = START_X;
 int y = START_Y;
 int k = 0;
 int curSpeed = speed;
 block_dir_t blockDir = BLOCK_UP;

 //检查游戏是否结束
 failCheck();
 while(1){
 if(kbhit()){
 int key = getch();
 if(KEY_SPACE == key){
 getch();
 }
 }
 //清除当前方块
 clearBlock(x,k,blockDir);
 if(kbhit()){
 int key = getch();
 
 if(KEY_UP == key){//变形
 block_dir_t nextDir = (block_dir_t)((blockDir+1)%4);
 if(rotatable(x,y+k,nextDir)){
  blockDir= nextDir;
 }
 }else if(KEY_DOWN == key){//乡下加速
 curSpeed = 50;
 }else if(KEY_RIGHT == key){//右移动
 if(moveable(x,y+k+20,MOVE_RIGHT,blockDir)){
  x+=20;
 }
 }else if(KEY_LEFT == key){//左移动
 if(moveable(x,y+k+20,MOVE_LEFT,blockDir)){
  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);
 //右上角绘制下一个方块
 nextblock();
 //向下降落的动作
 move();
}

int main (void){
 welcome(); 
 initGameScene();
 //产生新方块 
 nextblock();
 Sleep(500);
 memset(visit,0,sizeof(visit));
 while(1){
 newblock();
 //消除满行,并更新分数和速度
 check();
 }
 
 system("pause");
 closegraph();
 
 return 0;
}

分析项目:

1.必须要有欢迎界面
2.搭建合理的边界,就是游戏范围
3.逻辑1:先出现右上方的方块样式,等待一段时间,将右上方的样式在游戏区打印出
4.逻辑2,方块降落,要擦除原先印记,将1改为0
5.逻辑3,热键控制移动方向,暂停及变形,且不能移动出界,判断方块是否还能移动
6.逻辑4,方块凝固在下方不出界
7.逻辑5,最下面一行方块叠满了,消去它并把上面的行都下移(分两种情况),并且再次检查这一行
8.逻辑6,计算消除行数次数,统计分数,控制休眠时间长度
9.逻辑7,每次移动先判断是否能移动,默认结束程序,在合理游戏区返回false,结束界面

总结:从界面开始到完整的架构,功能后实现,一步一步,逻辑严谨,思路清晰,注释在代码里。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持猪先飞。

[!--infotagslink--]

相关文章

  • C++ STL标准库std::vector的使用详解

    vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
  • C++中取余运算的实现

    这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ string常用截取字符串方法

    这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • C++中四种加密算法之AES源代码

    本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
  • C++ 整数拆分方法详解

    整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
  • C++中 Sort函数详细解析

    这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
  • C++万能库头文件在vs中的安装步骤(图文)

    这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ bitset用法

    这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 浅谈C++中的string 类型占几个字节

    本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
  • C++ Eigen库计算矩阵特征值及特征向量

    这篇文章主要为大家详细介绍了C++ Eigen库计算矩阵特征值及特征向量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • C++ pair的用法实例详解

    这篇文章主要介绍了C++ pair的用法实例详解的相关资料,需要的朋友可以参考下...2020-04-25
  • VSCode C++多文件编译的简单使用方法

    这篇文章主要介绍了VSCode C++多文件编译的简单使用方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
  • C++中的循环引用

    虽然C++11引入了智能指针的,但是开发人员在与内存的斗争问题上并没有解放,如果我门实用不当仍然有内存泄漏问题,其中智能指针的循环引用缺陷是最大的问题。下面通过实例代码给大家介绍c++中的循环引用,一起看看吧...2020-04-25
  • C++随机点名生成器实例代码(老师们的福音!)

    这篇文章主要给大家介绍了关于C++随机点名生成器的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++如何删除map容器中指定值的元素详解

    map容器是C++ STL中的重要一员,删除map容器中value为指定元素的问题是我们经常与遇到的一个问题,下面这篇文章主要给大家介绍了关于利用C++如何删除map容器中指定值的元素的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。...2020-04-25
  • C++ 约瑟夫环问题案例详解

    这篇文章主要介绍了C++ 约瑟夫环问题案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...2021-08-15
  • C++中cin的用法详细

    这篇文章主要介绍了C++中cin的用法详细,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 基于C++中常见编译错误的总结详解

    本篇文章是对C++中的常见编译错误进行了详细的分析介绍,需要的朋友参考下...2020-04-25
  • C++实现递归函数的方法

    在本篇内容里小编给大家分享了关于C++实现递归函数的教学步骤,需要的朋友跟着参考下。...2020-04-25