├── pic ├── 1.png ├── 2.png └── game.gif ├── QtTetris ├── main.cpp ├── QtTetris.pro ├── widget.ui ├── widget.h └── widget.cpp └── README.md /pic/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tashaxing/QtTetris/HEAD/pic/1.png -------------------------------------------------------------------------------- /pic/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tashaxing/QtTetris/HEAD/pic/2.png -------------------------------------------------------------------------------- /pic/game.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tashaxing/QtTetris/HEAD/pic/game.gif -------------------------------------------------------------------------------- /QtTetris/main.cpp: -------------------------------------------------------------------------------- 1 | #include "widget.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | Widget w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QtTetris 2 | A tetris game written in Qt
3 | blog address:http://blog.csdn.net/u012234115/article/details/45966479
4 | # ScreentShots 5 | ![](https://github.com/tashaxing/QtTetris/raw/master/pic/game.gif)
6 | ![](https://github.com/tashaxing/QtTetris/raw/master/pic/1.png)
7 | ![](https://github.com/tashaxing/QtTetris/raw/master/pic/2.png)
-------------------------------------------------------------------------------- /QtTetris/QtTetris.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2015-05-23T16:45:48 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = QtTetris 12 | TEMPLATE = app 13 | 14 | 15 | SOURCES += main.cpp\ 16 | widget.cpp 17 | 18 | HEADERS += widget.h 19 | 20 | FORMS += widget.ui 21 | -------------------------------------------------------------------------------- /QtTetris/widget.ui: -------------------------------------------------------------------------------- 1 | 2 | Widget 3 | 4 | 5 | 6 | 0 7 | 0 8 | 400 9 | 300 10 | 11 | 12 | 13 | Widget 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /QtTetris/widget.h: -------------------------------------------------------------------------------- 1 | #ifndef WIDGET_H 2 | #define WIDGET_H 3 | 4 | #include 5 | 6 | const int BLOCK_SIZE=25; //单个方块单元的边长 7 | const int MARGIN=5; //场景边距 8 | const int AREA_ROW=20; //场景行数 9 | const int AREA_COL=12; //场景列数 10 | 11 | //方向 12 | enum Direction 13 | { 14 | UP, 15 | DOWN, 16 | LEFT, 17 | RIGHT, 18 | SPACE 19 | }; 20 | 21 | //定义边界信息 22 | struct Border 23 | { 24 | int ubound; 25 | int dbound; 26 | int lbound; 27 | int rbound; 28 | }; 29 | 30 | //坐标 31 | struct block_point 32 | { 33 | int pos_x; 34 | int pos_y; 35 | // block_point(int x,int y):pos_x(x),pos_y(y){} 36 | }; 37 | 38 | namespace Ui { 39 | class Widget; 40 | } 41 | 42 | class Widget : public QWidget 43 | { 44 | Q_OBJECT 45 | 46 | public: 47 | void InitGame(); //初始化 48 | void StartGame(); //开始游戏 49 | void GameOver(); //游戏结束 50 | 51 | void ResetBlock(); //重置方块 52 | void BlockMove(Direction dir); //方块变动 53 | void BlockRotate(int block[4][4]); //方块旋转 54 | void CreateBlock(int block[4][4],int block_id); //产生方块 55 | void GetBorder(int block[4][4],Border &border); //计算边界 56 | void ConvertStable(int x,int y); //转换为稳定方块 57 | bool IsCollide(int x,int y,Direction dir); //判断是否会碰撞 58 | 59 | public: 60 | explicit Widget(QWidget *parent = 0); 61 | ~Widget(); 62 | 63 | virtual void paintEvent(QPaintEvent *event); //场景刷新 64 | virtual void timerEvent(QTimerEvent *event); //定时器事件 65 | virtual void keyPressEvent(QKeyEvent *event); //键盘响应 66 | 67 | private: 68 | Ui::Widget *ui; 69 | 70 | private: 71 | int game_area[AREA_ROW][AREA_COL]; //场景区域,1表示活动的方块,2表示稳定的方块,0表示空 72 | block_point block_pos; //当前方块坐标 73 | int cur_block[4][4]; //当前方块形状 74 | Border cur_border; //当前方块边界 75 | int next_block[4][4]; //下一个方块形状 76 | bool isStable; //当前方块是否稳定了 77 | int score; //游戏分数 78 | int game_timer; //方块下落计时器 79 | int paint_timer; //渲染刷新计时器 80 | int speed_ms; //下落时间间隔 81 | int refresh_ms; //刷新时间间隔 82 | 83 | }; 84 | 85 | #endif // WIDGET_H 86 | -------------------------------------------------------------------------------- /QtTetris/widget.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "widget.h" 7 | #include "ui_widget.h" 8 | 9 | 10 | //定义图案代码和边界 11 | //田字 12 | int item1[4][4]= 13 | { 14 | {0,0,0,0}, 15 | {0,1,1,0}, 16 | {0,1,1,0}, 17 | {0,0,0,0} 18 | }; 19 | //右L 20 | int item2[4][4]= 21 | { 22 | {0,1,0,0}, 23 | {0,1,0,0}, 24 | {0,1,1,0}, 25 | {0,0,0,0} 26 | }; 27 | //左L 28 | int item3[4][4]= 29 | { 30 | {0,0,1,0}, 31 | {0,0,1,0}, 32 | {0,1,1,0}, 33 | {0,0,0,0} 34 | }; 35 | //右S 36 | int item4[4][4]= 37 | { 38 | {0,1,0,0}, 39 | {0,1,1,0}, 40 | {0,0,1,0}, 41 | {0,0,0,0} 42 | }; 43 | //左S 44 | int item5[4][4]= 45 | { 46 | {0,0,1,0}, 47 | {0,1,1,0}, 48 | {0,1,0,0}, 49 | {0,0,0,0} 50 | }; 51 | //山形 52 | int item6[4][4]= 53 | { 54 | {0,0,0,0}, 55 | {0,0,1,0}, 56 | {0,1,1,1}, 57 | {0,0,0,0} 58 | }; 59 | //长条 60 | int item7[4][4]= 61 | { 62 | {0,0,1,0}, 63 | {0,0,1,0}, 64 | {0,0,1,0}, 65 | {0,0,1,0} 66 | }; 67 | 68 | //拷贝方块图案 69 | inline void block_cpy(int dblock[4][4],int sblock[4][4]) 70 | { 71 | for(int i=0;i<4;i++) 72 | for(int j=0;j<4;j++) 73 | dblock[i][j]=sblock[i][j]; 74 | } 75 | 76 | Widget::Widget(QWidget *parent) : 77 | QWidget(parent), 78 | ui(new Ui::Widget) 79 | { 80 | ui->setupUi(this); 81 | //调整窗口尺寸布局 82 | resize(AREA_COL*BLOCK_SIZE+MARGIN*4+4*BLOCK_SIZE,AREA_ROW*BLOCK_SIZE+MARGIN*2); 83 | //初始化游戏 84 | InitGame(); 85 | } 86 | 87 | Widget::~Widget() 88 | { 89 | delete ui; 90 | } 91 | 92 | void Widget::paintEvent(QPaintEvent *event) 93 | { 94 | QPainter painter(this); 95 | //画游戏场景边框 96 | painter.setBrush(QBrush(Qt::white,Qt::SolidPattern)); 97 | painter.drawRect(MARGIN,MARGIN,AREA_COL*BLOCK_SIZE,AREA_ROW*BLOCK_SIZE); 98 | //画方块预告 99 | painter.setBrush(QBrush(Qt::blue,Qt::SolidPattern)); 100 | for(int i=0;i<4;i++) 101 | for(int j=0;j<4;j++) 102 | if(next_block[i][j]==1) 103 | painter.drawRect(MARGIN*3+AREA_COL*BLOCK_SIZE+j*BLOCK_SIZE,MARGIN+i*BLOCK_SIZE,BLOCK_SIZE,BLOCK_SIZE); 104 | //绘制得分 105 | painter.setPen(Qt::black); 106 | painter.setFont(QFont("Arial",14)); 107 | 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)); 108 | 109 | 110 | //绘制下落方块和稳定方块,注意方块边线的颜色是根据setPen来的,默认黑色 111 | for(int i=0;itimerId()==game_timer) 133 | BlockMove(DOWN); 134 | //刷新画面 135 | if(event->timerId()==paint_timer) 136 | update(); 137 | } 138 | 139 | void Widget::keyPressEvent(QKeyEvent *event) 140 | { 141 | switch(event->key()) 142 | { 143 | case Qt::Key_Up: 144 | BlockMove(UP); 145 | break; 146 | case Qt::Key_Down: 147 | BlockMove(DOWN); 148 | break; 149 | case Qt::Key_Left: 150 | BlockMove(LEFT); 151 | break; 152 | case Qt::Key_Right: 153 | BlockMove(RIGHT); 154 | break; 155 | case Qt::Key_Space: 156 | BlockMove(SPACE); 157 | break; 158 | default: 159 | break; 160 | } 161 | } 162 | 163 | void Widget::CreateBlock(int block[4][4],int block_id) 164 | { 165 | switch (block_id) 166 | { 167 | case 0: 168 | block_cpy(block,item1); 169 | break; 170 | case 1: 171 | block_cpy(block,item2); 172 | break; 173 | case 2: 174 | block_cpy(block,item3); 175 | break; 176 | case 3: 177 | block_cpy(block,item4); 178 | break; 179 | case 4: 180 | block_cpy(block,item5); 181 | break; 182 | case 5: 183 | block_cpy(block,item6); 184 | break; 185 | case 6: 186 | block_cpy(block,item7); 187 | break; 188 | default: 189 | break; 190 | } 191 | } 192 | 193 | void Widget::GetBorder(int block[4][4],Border &border) 194 | { 195 | //计算上下左右边界 196 | for(int i=0;i<4;i++) 197 | for(int j=0;j<4;j++) 198 | if(block[i][j]==1) 199 | { 200 | border.dbound=i; 201 | break; //直到计算到最后一行有1 202 | } 203 | for(int i=3;i>=0;i--) 204 | for(int j=0;j<4;j++) 205 | if(block[i][j]==1) 206 | { 207 | border.ubound=i; 208 | break; 209 | } 210 | for(int j=0;j<4;j++) 211 | for(int i=0;i<4;i++) 212 | if(block[i][j]==1) 213 | { 214 | border.rbound=j; 215 | break; 216 | } 217 | for(int j=3;j>=0;j--) 218 | for(int i=0;i<4;i++) 219 | if(block[i][j]==1) 220 | { 221 | border.lbound=j; 222 | break; 223 | } 224 | // qDebug()<AREA_COL-1) 330 | return true; 331 | return false; 332 | } 333 | 334 | void Widget::BlockMove(Direction dir) 335 | { 336 | switch (dir) { 337 | case UP: 338 | if(IsCollide(block_pos.pos_x,block_pos.pos_y,UP)) 339 | break; 340 | //逆时针旋转90度 341 | BlockRotate(cur_block); 342 | //防止旋转后bug,i和j从0到4重新设置方块 343 | for(int i=0;i<4;i++) 344 | for(int j=0;j<4;j++) 345 | game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j]; 346 | //重新计算边界 347 | GetBorder(cur_block,cur_border); 348 | break; 349 | case DOWN: 350 | //方块到达边界则不再移动 351 | if(block_pos.pos_y+cur_border.dbound==AREA_ROW-1) 352 | { 353 | ConvertStable(block_pos.pos_x,block_pos.pos_y); 354 | ResetBlock(); 355 | break; 356 | } 357 | //碰撞检测,只计算上下左右边界,先尝试走一格,如果碰撞则稳定方块后跳出 358 | if(IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN)) 359 | { 360 | //只有最终不能下落才转成稳定方块 361 | ConvertStable(block_pos.pos_x,block_pos.pos_y); 362 | ResetBlock(); 363 | break; 364 | } 365 | //恢复方块上场景,为了清除移动过程中的方块残留 366 | for(int j=cur_border.lbound;j<=cur_border.rbound;j++) 367 | game_area[block_pos.pos_y][block_pos.pos_x+j]=0; 368 | //没有碰撞则下落一格 369 | block_pos.pos_y+=1; 370 | //方块下降一格,拷贝到场景,注意左右边界 371 | for(int i=0;i<4;i++) //必须是0到4而不是边界索引,考虑到旋转后边界重新计算 372 | for(int j=cur_border.lbound;j<=cur_border.rbound;j++) 373 | if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块 374 | game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j]; 375 | break; 376 | case LEFT: 377 | //到左边界或者碰撞不再往左 378 | if(block_pos.pos_x+cur_border.lbound==0||IsCollide(block_pos.pos_x,block_pos.pos_y,LEFT)) 379 | break; 380 | //恢复方块右场景,为了清除移动过程中的方块残留 381 | for(int i=cur_border.ubound;i<=cur_border.dbound;i++) 382 | game_area[block_pos.pos_y+i][block_pos.pos_x+3]=0; 383 | block_pos.pos_x-=1; 384 | //方块左移一格,拷贝到场景 385 | for(int i=cur_border.ubound;i<=cur_border.dbound;i++) 386 | for(int j=0;j<4;j++) 387 | if(block_pos.pos_x+j>=0&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界 388 | game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j]; 389 | break; 390 | case RIGHT: 391 | if(block_pos.pos_x+cur_border.rbound==AREA_COL-1||IsCollide(block_pos.pos_x,block_pos.pos_y,RIGHT)) 392 | break; 393 | //恢复方块左场景,为了清除移动过程中的方块残留 394 | for(int i=cur_border.ubound;i<=cur_border.dbound;i++) 395 | game_area[block_pos.pos_y+i][block_pos.pos_x]=0; 396 | block_pos.pos_x+=1; 397 | //方块右移一格,拷贝到场景 398 | for(int i=cur_border.ubound;i<=cur_border.dbound;i++) 399 | for(int j=0;j<4;j++) 400 | if(block_pos.pos_x+j<=AREA_COL-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界 401 | game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j]; 402 | break; 403 | case SPACE: //一次到底 404 | //一格一格下移,直到不能下移 405 | while(block_pos.pos_y+cur_border.dbound=1) 428 | { 429 | bool is_line_full=true; 430 | for(int j=0;j=1;k--) 440 | for(int j=0;j