Qt小游戏开发:俄罗斯方块

作为一个即将步入游戏行业的新人,手写经典小游戏是必备技能哦。

预览

由于录屏软件的问题,颜色和帧率与实际有所出入,,不过不影响。

步骤

1 新建工程

建一个基类为QWidget的QT gui工程,实际过程中所有gui代码包括界面布局都是手巧的,所以其实不需要简历ui文件。

2定义游戏数据结构

游戏场景和方块都用二维数组存储,有方块的存1,无方块的存0

场景数据

const int BLOCK_SIZE=25; //单个方块单元的边长const int MARGIN=5; //场景边距const int AREA_ROW=20; //场景行数const int AREA_COL=12; //场景列数

图案数据

//定义图案代码和边界//田字int item1[4][4]={{0,0,0,0},{0,1,1,0},{0,1,1,0},{0,0,0,0}};//右Lint item2[4][4]={{0,1,0,0},{0,1,0,0},{0,1,1,0},{0,0,0,0}};//左Lint item3[4][4]={{0,0,1,0},{0,0,1,0},{0,1,1,0},{0,0,0,0}};//右Sint item4[4][4]={{0,1,0,0},{0,1,1,0},{0,0,1,0},{0,0,0,0}};//左Sint item5[4][4]={{0,0,1,0},{0,1,1,0},{0,1,0,0},{0,0,0,0}};//山形int item6[4][4]={{0,0,0,0},{0,0,1,0},{0,1,1,1},{0,0,0,0}};//长条int item7[4][4]={{0,0,1,0},{0,0,1,0},{0,0,1,0},{0,0,1,0}};

由于涉及到碰撞检测,所以要确定方块图案的上下左右边界

void Widget::GetBorder(int block[4][4],Border &border){//计算上下左右边界for(int i=0;i<4;i++)for(int j=0;j<4;j++)if(block[i][j]==1){border.dbound=i;break; //直到计算到最后一行有1}for(int i=3;i>=0;i–)for(int j=0;j<4;j++)if(block[i][j]==1){border.ubound=i;break;}for(int j=0;j<4;j++)for(int i=0;i<4;i++)if(block[i][j]==1){border.rbound=j;break;}for(int j=3;j>=0;j–)for(int i=0;i<4;i++)if(block[i][j]==1){border.lbound=j;break;}// qDebug()<<cur_border.ubound<<cur_border.dbound<<cur_border.lbound<<cur_border.rbound;}

在Widget类里面定义好游戏场景和当前方块以及下一个方块的变量

int game_area[AREA_ROW][AREA_COL]; //场景区域,1表示活动的方块,2表示稳定的方块,0表示空block_point block_pos; //当前方块坐标int cur_block[4][4]; //当前方块形状Border cur_border; //当前方块边界3添加渲染循环和计时器事件以及键盘监听

游戏的场景是经过每一帧刷新实现界面变化的

void Widget::paintEvent(QPaintEvent *event){QPainter painter(this);//画游戏场景边框painter.setBrush(QBrush(Qt::white,Qt::SolidPattern));painter.drawRect(MARGIN,MARGIN,AREA_COL*BLOCK_SIZE,AREA_ROW*BLOCK_SIZE);//画方块预告painter.setBrush(QBrush(Qt::blue,Qt::SolidPattern));for(int i=0;i<4;i++)for(int j=0;j<4;j++)if(next_block[i][j]==1)painter.drawRect(MARGIN*3+AREA_COL*BLOCK_SIZE+j*BLOCK_SIZE,MARGIN+i*BLOCK_SIZE,BLOCK_SIZE,BLOCK_SIZE);//绘制得分painter.setPen(Qt::black);painter.setFont(QFont("Arial",14));painter.drawText(QRect(MARGIN*3+AREA_COL*BLOCK_SIZE,MARGIN*2+4*BLOCK_SIZE,BLOCK_SIZE*4,BLOCK_SIZE*4),Qt::AlignCenter,"score: "+QString::number(score));//绘制下落方块和稳定方块,注意方块边线的颜色是根据setPen来的,默认黑色for(int i=0;i<AREA_ROW;i++)for(int j=0;j<AREA_COL;j++){//绘制活动方块if(game_area[i][j]==1){painter.setBrush(QBrush(Qt::red,Qt::SolidPattern));painter.drawRect(j*BLOCK_SIZE+MARGIN,i*BLOCK_SIZE+MARGIN,BLOCK_SIZE,BLOCK_SIZE);}//绘制稳定方块else if(game_area[i][j]==2){painter.setBrush(QBrush(Qt::green,Qt::SolidPattern));painter.drawRect(j*BLOCK_SIZE+MARGIN,i*BLOCK_SIZE+MARGIN,BLOCK_SIZE,BLOCK_SIZE);}}}需要二个定时器,一个用于方块的自动下落,一个用于界面的刷新帧率void Widget::timerEvent(QTimerEvent *event){//方块下落if(event->timerId()==game_timer)BlockMove(DOWN);//刷新画面if(event->timerId()==paint_timer)update();}键盘响应,上件旋转,左右下移动,空格键直接下落到底void Widget::keyPressEvent(QKeyEvent *event){switch(event->key()){case Qt::Key_Up:BlockMove(UP);break;case Qt::Key_Down:BlockMove(DOWN);break;case Qt::Key_Left:BlockMove(LEFT);break;case Qt::Key_Right:BlockMove(RIGHT);break;case Qt::Key_Space:BlockMove(SPACE);break;default:break;}}

4方块移动、旋转、消行,出现下一个方块和结束逻辑

void Widget::BlockMove(Direction dir){switch (dir) {case UP:if(IsCollide(block_pos.pos_x,block_pos.pos_y,UP))break;//逆时针旋转90度BlockRotate(cur_block);//防止旋转后bug,i和j从0到4重新设置方块for(int i=0;i<4;i++)for(int j=0;j<4;j++)game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];//重新计算边界GetBorder(cur_block,cur_border);break;case DOWN://方块到达边界则不再移动if(block_pos.pos_y+cur_border.dbound==AREA_ROW-1){ConvertStable(block_pos.pos_x,block_pos.pos_y);ResetBlock();break;}//碰撞检测,只计算上下左右边界,先尝试走一格,如果碰撞则稳定方块后跳出if(IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN)){//只有最终不能下落才转成稳定方块ConvertStable(block_pos.pos_x,block_pos.pos_y);ResetBlock();break;}//恢复方块上场景for(int j=cur_border.lbound;j<=cur_border.rbound;j++)game_area[block_pos.pos_y][block_pos.pos_x+j]=0;//没有碰撞则下落一格block_pos.pos_y+=1;//方块下降一格,拷贝到场景,注意左右边界for(int i=0;i<4;i++) //必须是0到4for(int j=cur_border.lbound;j<=cur_border.rbound;j++)if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];break;case LEFT://到左边界或者碰撞不再往左if(block_pos.pos_x+cur_border.lbound==0||IsCollide(block_pos.pos_x,block_pos.pos_y,LEFT))break;//恢复方块右场景for(int i=cur_border.ubound;i<=cur_border.dbound;i++)game_area[block_pos.pos_y+i][block_pos.pos_x+3]=0;block_pos.pos_x-=1;//方块左移一格,拷贝到场景for(int i=cur_border.ubound;i<=cur_border.dbound;i++)for(int j=0;j<4;j++)if(block_pos.pos_x+j>=0&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];break;case RIGHT:if(block_pos.pos_x+cur_border.rbound==AREA_COL-1||IsCollide(block_pos.pos_x,block_pos.pos_y,RIGHT))break;//恢复方块左场景for(int i=cur_border.ubound;i<=cur_border.dbound;i++)game_area[block_pos.pos_y+i][block_pos.pos_x]=0;block_pos.pos_x+=1;//方块右移一格,拷贝到场景for(int i=cur_border.ubound;i<=cur_border.dbound;i++)for(int j=0;j<4;j++)if(block_pos.pos_x+j<=AREA_COL-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];break;case SPACE: //一次到底//一格一格下移,直到不能下移while(block_pos.pos_y+cur_border.dbound<AREA_ROW-1&&!IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN)){//恢复方块上场景for(int j=cur_border.lbound;j<=cur_border.rbound;j++)game_area[block_pos.pos_y][block_pos.pos_x+j]=0;//没有碰撞则下落一格block_pos.pos_y+=1;//方块下降一格,拷贝到场景,注意左右边界for(int i=0;i<4;i++) //必须是0到4for(int j=cur_border.lbound;j<=cur_border.rbound;j++)if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];}ConvertStable(block_pos.pos_x,block_pos.pos_y);ResetBlock();break;default:break;}//处理消行,整个场景上面的行依次下移int i=AREA_ROW-1;int line_count=0; //记消行数while(i>=1){bool is_line_full=true;for(int j=0;j<AREA_COL;j++)if(game_area[i][j]==0){is_line_full=false;i–;break;}if(is_line_full){for(int k=i;k>=1;k–)for(int j=0;j<AREA_COL;j++)game_area[k][j]=game_area[k-1][j];line_count++;//每次增加消行的行数}}score+=line_count*10; //得分//判断游戏是否结束for(int j=0;j<AREA_COL;j++)if(game_area[0][j]==2) //最顶端也有稳定方块GameOver();}每次方块稳定之后就把下一个方块拷贝到当前方块(产生新方块从顶上下落)

微风吹过,海面上金光闪闪,泛起一道道美丽的浪花,

Qt小游戏开发:俄罗斯方块

相关文章:

你感兴趣的文章:

标签云: