俄罗斯方块 c++实现

一、实验实习目的及要求

目的:实现俄罗斯方块游戏的基本功能。

要求:Windows界面实现,设计比较简洁美观的游戏界面,可以实现方块的预览和控制,分数显示及更新,游戏暂停等基本功能,利用对话框应用程序形式。

二、实验实习内容简介:

(一)系统应实现的主要功能:

  1. 图形预览功能
  2. 分数显示功能
  3. 方向键控制游戏
  4. 暂停与继续游戏功能

(二)各模块的具体功能和简单算法:

  1. 该程序的设计,结合了有关c++语言图形界面设计的内容,本人选用了适用于新手的ege图形库来辅助完成本次设计。

(1)配置环境:将ege15.04配置到devc++中,这里要注意的是,要将devc++的“编译选项”中的“设定编译器配”菜单设置成“TDM-GCC4.8.132-bitRelease”,并且在“项目属性”对话框里点击“参数”标签,在“连接”里加入如下内容(这里一定不要错,否则会导致ege库导入失败):

-lgraphics -lgdi32 -limm32 -lmsimg32 -lole32 -loleaut32 -lwinmm -luuid -mwindows

(2)使用的函数:本次实验会使用到ege图形库的以下函数,如表1。

函数名

功能

setinitmode

设置初始化图形的选项和模型

initgraph

初始化图形系统

setfillcolor

用于设置当前填充颜色

bar

用于画无边框填充矩形。其中,填充颜色由setfillstyle函数决定

kbmsg

用于检测当前是否有键盘消息

xyprintf

用于在指定位置格式化输出字符串

getch

用于获取键盘消息,如果当前没有消息,则等待

getkey

用于获取键盘消息,如果当前没有消息,则等待

key_msg

用于保存键盘消息

closegraph

关闭图形环境

line

用于画线

rectangle

用于画空心矩形

                                        表1 ege图形库部分函数及功能

(3)本实验的程序简易流程图如图

俄罗斯方块 c++实现_第1张图片

(4)UML图

俄罗斯方块 c++实现_第2张图片

(三)实验代码

1. 头文件ablocks.h 用来描绘方块

#ifndef _ABLOCKS_H_    
#define _ABLOCKS_H_  

#include  
using namespace std;  
class ablocks{  
public:  
    COORD block[4]; 	//COORD是Windows API中定义的一种结构,表示一个字符在控制台屏幕上的坐标,方块的位置   
    COORD center;		 //旋转中心   
    color_t color;  
    static int zt[10][21];     	//每个方格状态0未填充1已填充  
    static int score,xh;      	//分数和消行  
    static char scores[50],xhs[50];		//分数显示字符串,消行显示字符串      
    ablocks(int x1,int y1,int index1);  
    ablocks(ablocks &a);  
    void displayBlock();    		//显示块   
    void movew(int where);          //移动  
    void spin();           		 //旋转  
    void clearone(int i);   	//清除一行  
    void ztupdate();        		//方格状态更新   
    void display();         		//输出信息  
    void initgame();  
    int pdzs();        		 //判断下方是否有障碍  
    int pdz();          		//判断左方是否有障碍  
    int pdy();          		//判断右方是否有障碍  
private:   
    int x,y,index;  
};  
  
#endif 

2.ablocks.cpp   用来描绘方块

//ablocks.cpp
#include
#include
#include
#include"ablocks.h"
using namespace std;
int ablocks::zt[10][21];
int ablocks::score=0;
int ablocks::xh=0;
char ablocks::scores[50];
char ablocks::xhs[50];
ablocks::ablocks(int x1,int y1,int index1):index(index1){
	x=x1-10;
	y=y1-10;
    switch(index)
    {
    case 1:	//L
        block[0]={x-20,y-20};
        block[1]={x-20,y};
        block[2]={x,y};
        block[3]={x+20,y};
		color=0xFFA500;
		center=block[1];
        break;
    case 2://I
        block[0]={x-40,y-20};
        block[1]={x-20,y-20};
        block[2]={x,y-20};
        block[3]={x+20,y-20};
		x-=10;
		y-=10;
		color=0x0000ee;
		center={211,121};
        break;
    case 3://J
        block[0]={x-20,y};
        block[1]={x,y};
        block[2]={x+20,y};
        block[3]={x+20,y-20};
        color=0x00ffff;
		center=block[1];
        break;
    case 4://田 
        block[0]={x-20,y-20};
        block[1]={x,y-20};
        block[2]={x-20,y};
        block[3]={x,y};
        x-=10;
        y-=10;
		color=YELLOW;
		center={211,131};
        break;
    case 5://S 
        block[0]={x-20,y};
        block[1]={x,y};
        block[2]={x,y-20};
        block[3]={x+20,y-20};
		color=0xff0000;
		center=block[1];
        break;
    case 6://Z
        block[0]={x-20,y-20};
        block[1]={x,y-20};
        block[2]={x,y};
        block[3]={x+20,y};
		color=0xff00ff;
		center=block[1];
        break;
    case 7://T
        block[0]={x-20,y};
        block[1]={x,y};
        block[2]={x,y-20};
        block[3]={x+20,y};
		color=0x00ff00;
		center=block[1];
        break;
    }

}
ablocks::ablocks(ablocks &a){
	x=a.x;
	y=a.y;
	index=a.index;
}
void ablocks::displayBlock(){
	setfillcolor(color);
	for(int i=0;i<4;i++){
		if(block[i].X>400)
   		{
        	bar(block[0].X,block[0].Y,block[0].X+19,block[0].Y+19);
        	bar(block[1].X,block[1].Y,block[1].X+19,block[1].Y+19);
        	bar(block[2].X,block[2].Y,block[2].X+19,block[2].Y+19);
        	bar(block[3].X,block[3].Y,block[3].X+19,block[3].Y+19);
    	}
    	else//:不要超出方格
		{
			for(int j=0;j<4;j++)
        	if(block[j].Y>=141)
				bar(block[j].X,block[j].Y,block[j].X+19,block[j].Y+19);
    	}
    }
} 
void ablocks::movew(int where){
    switch(where)
    {
    case 1://下
        block[0].Y+=20;
        block[1].Y+=20;
        block[2].Y+=20;
        block[3].Y+=20;
        y+=20;
        break;
    case 3://左
        block[0].X-=20;
        block[1].X-=20;
        block[2].X-=20;
        block[3].X-=20;
        x-=20;
        break;
    case 4://右
        block[0].X+=20;
        block[1].X+=20;
        block[2].X+=20;
        block[3].X+=20;
        x+=20;
        break;
    }	
}
void ablocks::spin(){           //旋转
    for(int i=0; i<4; i++)  //如果旋转之后导致重叠或超出方格,则不旋转
    {
        if((x-(block[i].Y-y)-20>321)||(x-(block[i].Y-y)-20<141))return;
        if(zt[((x-(block[i].Y-y)-20)-141)/20][(y+block[i].X-x-141)/20]==1)return;
    }
    if(index!=4) 	//避免田字型图案发生“旋转” 
    for(int i=0; i<4; i++)
    {
        int temp_xz=block[i].X;//顺时针旋转90度的坐标处理
        block[i].X=x-((block[i].Y)-y)-20;
        block[i].Y=y+(temp_xz)-x;
    }
}
void ablocks::clearone(int i){	//  消行
    setfillcolor(EGERGB(0,0,0));
    for(int k=0; k<=10; k++)
    {
        bar(20*k+141,141+20*i,20*k+141+19,141+20*i+19);
    }
}
void ablocks::ztupdate(){	//方格状态更新 
    int xhgs=0;//消行个数
    for(int i=0; i<4; i++)
    {
        zt[(block[i].X-141)/20][(block[i].Y-141)/20]=1;
    }
    for(int i=0; i<10; i++)
    {
        zt[i][20]=1;
    }
    for(int i=0; i<20; i++)
    {
        int j;
        for(j=0; j<10; j++)
        {
            if(zt[j][i]==0)break;
        }
        if(j==10)       //一行全部被填充
        {
            clearone(i);
            xh++;
            xhgs++;
            for(int down=i-1; down>0; down--)   //逐层下移
            {
                for(int k=0; k<10; k++)
                {
                    if(zt[k][down]==1)
                    {
                        color_t ys=getpixel(k*20+141+10,10+down*20+141);//获取颜色
                        setfillcolor(EGERGB(0,0,0));
                        bar(k*20+141,down*20+141,k*20+141+19,down*20+141+19);
                        setfillcolor(ys);//为了下移之后可以保持颜色不变,使用上一块颜色填充下一块
                        bar(k*20+141,20+down*20+141,k*20+141+19,20+down*20+141+19);
                    }
                    zt[k][down+1]=zt[k][down];  //下移
                }
            }

        }
    }
    if(xhgs==1)
		score=score+1;//一次消行越多,分数越高
    if(xhgs==2)
		score=score+3;
    if(xhgs==3)
		score=score+5;
    if(xhgs==4)
		score=score+8;
    display();
}
void ablocks::display(){	//输出信息
    sprintf(scores,"Score: %d",score);
    sprintf(xhs,"Kill: %d",xh);
    xyprintf(500,250,scores);
    xyprintf(500,280,xhs);
    xyprintf(500,350,"GAMING...");
    xyprintf(500,450,"Directiion key to move");
    xyprintf(500,500,"Space key to pause");
}
int ablocks::pdzs(){	//判断下方是否有障碍
    for(int i=0; i<4; i++)
    {
        if(zt[(block[i].X-141)/20][(block[i].Y-141)/20+1]==1)
		return 1;
    }
    return 0;
}
int ablocks::pdz(){	//判断左方是否有障碍
    for(int i=0; i<4; i++)
    {
        if(zt[(block[i].X-141)/20-1][(block[i].Y-141)/20]==1)return 1;
    }
    return 0;
}
int ablocks::pdy(){	//判断右方是否有障碍
    for(int i=0; i<4; i++)
    {
        if(zt[(block[i].X-141)/20+1][(block[i].Y-141)/20]==1)return 1;
    }
    return 0;
}
//游戏初始化,gameover之后调用的
void ablocks::initgame()
{
    for(int i=0; i<10; i++)//超出方格的一行置一,为了统一判断是否碰撞
    {
        zt[i][20]=1;
    }
    for(int i=0; i<20; i++)//状态 分数 消行数清零,清除整个方格
    {
        int j;
        for(j=0; j<10; j++)
        {
            zt[j][i]=0;
        }
        clearone(i);
    }
    score=0;
    xh=0;
}

3.ege.cpp 


#include 
#include 
#include 
#include "ablocks.h"
#define SCRW 680
#define SCRH 640
#define DELAY 50    //延时时间

int g_live=1;         //块是否还活着
void frame(void);
int main()
{
    srand( (unsigned)time( NULL ) );	//随机数发生器 
    setinitmode(0);
    initgraph(SCRW,SCRH);
    int x1,y1,x2,y2;
	ablocks	testbl(0,0,0);
	ablocks nextbl(0,0,0);
    testbl.display();
	frame(); 
    int rk1=1+rand()%7;         //随机产生1到7
    for(int i=0; i<10; i++)
		testbl.zt[i][20]=1;
    for(int n=0;; Sleep(10),n++)    //主循环
    {
        if(g_live==1)
        {
            g_live=0;
            x1=241+10-20;     //生成两个块当前的和下一个
            y1=161+10-20;
            x2=500+10;
            y2=100+10;
            ablocks t(x1,y1,rk1);
            testbl=t;
            rk1=1+rand()%7;
            ablocks t1(x2,y2,rk1);
            nextbl=t1;
            testbl.displayBlock();
            setfillcolor(BLACK);
            bar(400,50,600,150);
            nextbl.displayBlock();

        }
        int k=kbmsg();                  //按键处理
        key_msg whatfx;
        if(k)
        {
            whatfx=getkey();
            if (whatfx.msg == key_msg_down)
            {
                if(whatfx.key==VK_SPACE)
                {
                    setfillcolor(BLACK);
                    bar(500,350,500+100,380);
                    xyprintf(500,350,"PAUSE... ");
                    getch();
                    getch();
                    bar(500,350,500+100,380);
                    xyprintf(500,350,"GAMING... ");
                }
                color_t temp=testbl.color;          //清除当前块,因为要改变形状
                testbl.color=BLACK;
                testbl.displayBlock();
                testbl.color=temp;

                if(whatfx.key==VK_LEFT&&(testbl.pdz()!=1)&&testbl.block[0].X>=161&&testbl.block[1].X>=161&&testbl.block[2].X>=161&&testbl.block[3].X>=161)
					testbl.movew(3);
                if(whatfx.key==VK_RIGHT&&(testbl.pdy()!=1)&&testbl.block[0].X<=301&&testbl.block[1].X<=301&&testbl.block[2].X<=301&&testbl.block[3].X<=301)
					testbl.movew(4);
                if(whatfx.key==VK_DOWN)
                {
                    n=DELAY;
                }
                if(whatfx.key==VK_UP)
                {
                    testbl.spin();
                }
                testbl.displayBlock();      //绘制旋转后的块
            }
        }
        if(n==DELAY)                                //处理自由下落
        {
            n=0;
            if(testbl.pdzs()==1)                    //是否已经碰撞
            {
                testbl.ztupdate();                  //碰撞,更新方格状态
                //game over判定
                if(testbl.block[0].Y<141||testbl.block[1].Y<141||testbl.block[2].Y<141||testbl.block[3].Y<141)
                {
                    xyprintf(500,350,"GAME OVER !");
					break; 
                }
                g_live=1;
                continue;
            }
            color_t temp=testbl.color;      //清除当前块整体下移后显示
            testbl.color=BLACK;
            testbl.displayBlock();
            testbl.color=temp;
            testbl.movew(1);
            testbl.displayBlock();
        }

    }
    getch();
    closegraph();
    return 0;
}
//绘制表格框 
void frame(void)
{
	setfillcolor(EGERGB(255,0,0));
    setcolor(EGERGB(80,80,80));	//方格线的颜色 

    for(int i=160; i<540; i=i+20)    //画方格的横线 
    {
        line(140,i,340,i);
    }

    for(int i=160; i<340; i=i+20)	// 画方格的竖线 
    {
        line(i,140,i,540);
    }
    setcolor(EGERGB(180,180,180)); //矩形框的颜色 
    rectangle(140,140,341,541); 

}

三、源程序调试过程

1.编译时发生initializer lists only avaliable -std=c++11…的错误

俄罗斯方块 c++实现_第3张图片

                                                                               编译错误extended

解决方案:在ablocks.h中加入windows.h头文件即可

2.其他的不想写了。。。

四、总结

本学期学习了c++,第一次学习面向对象的编程语言,发现该类语言有很多的好处,比如易维护,质量高,效率高,易扩充等,对于我而言,我觉得它最好的一点就是可重用,使代码的利用率更高,整体效果更加美观,每个模块都很都有自己的含义,通过这次实验发现,图形界面是很神奇的东西,今后的学习中,我应该花更多的时间去学习它,领略它的魅力。实践是认知的来源,实践是认知发展的动力,学习编程,就是要多动手,动手敲打代码,就好比如,好记性不如烂笔头,打代码毅然,在打代码的同时,动脑去想去学。当然适当的借鉴和对案例的充分分析也至关重要,CS的学习范围广,学习难度大,利用王伦,利用各种论坛也很有利于我们学习,要学好代码,也要学好如何阅读代码。当然独立的思考也是一切成功的基础。本次实验的不足就是,在“编译运行”的时候,会产生一些warning,但也排除不了,是我的知识欠缺。但是,分开“编译”再“运行”又不会产生,编程果然很神奇。

你可能感兴趣的:(课程实验)