C++实现俄罗斯方块

所属分类: 软件编程 / C 语言 阅读数: 118
收藏 0 赞 0 分享

本文实例为大家分享了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,结束界面

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

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

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多