头文件:
//Tetris.h
#ifndef ELUOSIWIDGET_H
#define ELUOSIWIDGET_H
#include <QtGui/QWidget>
#include <QPainter>
#include <QPaintEvent>
#include <QKeyEvent>
#include <cstring>
#include <ctime>
#define REC_SIZE 25 //方块大小
#define SCENE_W 16 //场景列数
#define SCENE_H 10 //场景行数
enum direction{UP,DOWN,LEFT,RIGHT,SPACE};
typedef struct {
int pos_x; //方块x坐标
int pos_y; //方块y坐标
}REC_Point;
class eluosiWidget : public QWidget
{
Q_OBJECT
signals:
void GameOver(); //游戏结束信号
void CurrentScore(int); //游戏当前分数
public slots:
void StartGame(); //游戏开始
void PlayGame(); //恢复游戏
void PauseGame(); //暂停游戏
void SetSpeed(int level);//设置速度等级
void SetHard(int level);//设置是否增加难度
public:
eluosiWidget(QWidget *parent = 0); //构造函数
~eluosiWidget(); //析构函数
void weizhi(int x,int y,int tx,direction direct); //位置变换
void paintEvent(QPaintEvent *event); //场景绘制
void change(int x,int y,int num,int state); //方块变换
void clear(int x,int y,int num); //清除当前位置信息
void move(int hang,direction direct);//部分移动
void SetLevel();//设置难度等级
protected:
void timerEvent(QTimerEvent *event); //定时设置
void keyPressEvent(QKeyEvent *event); //键盘事件
private:
int scene_num[SCENE_W][SCENE_H];//场景数据
REC_Point rpoint[4]; //定义四个方块的位置
int REC_tx[2];//定义方块的图形
int timerid1,timerid2;//记录两个定时器id
int speed_ms,fresh_ms;//speed_ms记录下落速度,fresh_ms设置窗体刷新速度
int score;//分数
bool Pauseflag;//暂停标志
bool Levelflag;//难度标志
int HardLevel;//难度等级
};
#endif // ELUOSIWIDGET_H
实现:
//Tetris.cpp
#include "Tetris.h"
int tx_code[][4]={{456,258,456,258},{142,142,142,142},{246,268,123,248},{128,467,289,346},
{238,146,278,469},{247,126,247,126},{269,234,269,234}};//图形代码
eluosiWidget::eluosiWidget(QWidget *parent)
: QWidget(parent)
{
this->resize(SCENE_H*REC_SIZE,SCENE_W*REC_SIZE); //设置窗口大小
StartGame(); //开始游戏
}
eluosiWidget::~eluosiWidget()
{
;
}
void eluosiWidget::StartGame() //初始化
{
for(int i=0;i<SCENE_W;i++) //清空舞台信息
memset(scene_num[i],0,sizeof(int) * SCENE_H);
score = 0; //分数初始化
speed_ms = 800;fresh_ms = 50;//设置下落速度和刷新速度
rpoint[0].pos_x = 1;rpoint[0].pos_y = SCENE_H/2;//设置主方块位置信息
srand(time(0));REC_tx[0] = rand()%7;REC_tx[1] = rand()%7;//随机出现第一个图形
change(rpoint[0].pos_x,rpoint[0].pos_y,tx_code[REC_tx[0]][0],2);
Pauseflag = false;Levelflag=false;
PlayGame(); //开始下落
}
void eluosiWidget::change(int x, int y, int num,int state) //设置本次位置信息
{
int temp = num;
for(int i=1;i<4;i++){
rpoint[i].pos_x=x-1+(num%10-1)/3;rpoint[i].pos_y=y-1+(num%10-1)%3;
scene_num[rpoint[i].pos_x][rpoint[i].pos_y]=state;
num = num/10;
}
scene_num[x][y]=state;
rpoint[0].pos_x = x;rpoint[0].pos_y = y;
if(258 == temp){
scene_num[x+2][y]=state;rpoint[2].pos_x=x+2;rpoint[2].pos_y=y;
}
else if(456 == temp){
scene_num[x][y+2]=state;rpoint[2].pos_x=x;rpoint[2].pos_y=y+2;
}
}
void eluosiWidget::clear(int x, int y, int num) //清除上次位置信息
{
if(258 == num)
scene_num[x+2][y]=0;
else if(456 == num)
scene_num[x][y+2]=0;
for(int i=0;i<3;i++){
scene_num[x-1+(num%10-1)/3][y-1+(num%10-1)%3]=0;
num = num/10;
}
scene_num[x][y] =0;
}
void eluosiWidget::weizhi(int x,int y,int tx,direction direct)
{ //x,y是坐标,tx是7种形状,direct是方向
static int temp_tx;bool temp_flag; temp_flag = true;
switch(direct){
case UP: //变换方块动作
for(int i=0;i<4;i++) //判断方块是否允许变换
temp_flag = temp_flag && rpoint[i].pos_y-1 >= 0 && rpoint[i].pos_x+1 < SCENE_W-1
&& rpoint[i].pos_x+1 != 1 && rpoint[i].pos_y+1 < SCENE_H ;
if(temp_flag){ //执行变换动作
clear(x,y,tx_code[tx][temp_tx++]);if(temp_tx >= 4) temp_tx = 0;change(x,y,tx_code[tx][temp_tx],2);
}break;
case DOWN://下移
for(int i=0;i<4;i++) //判断是否允许下移
temp_flag = temp_flag && scene_num[rpoint[i].pos_x+1][rpoint[i].pos_y] !=1 &&
scene_num[rpoint[i].pos_x][rpoint[i].pos_y] !=1 && rpoint[i].pos_x+1 < SCENE_W;
if(temp_flag){ //执行下移动作
clear(x,y,tx_code[tx][temp_tx]);change(x+1,y,tx_code[tx][temp_tx],2);
}else{
//落下后更改状态为静止
change(x,y,tx_code[tx][temp_tx],1);
//判断是否满行
for(int i=0;i<SCENE_W;i++){
for(int j=0;j<SCENE_H;j++){ //将满行的信息消除
if(scene_num[i][j]==0)
break;
else if(j==SCENE_H-1){
move(i,DOWN);//从第i行开始整体下移
emit CurrentScore(score*10);break;
}
}
}
//判断是否结束游戏
if(scene_num[0][y] == 1){
killTimer(timerid1);killTimer(timerid2);
emit GameOver();break;
}
//判断是否加难度
if(Levelflag){
SetLevel();
Levelflag=false;
}
//重新构造一个方块
rpoint[0].pos_x = 1;rpoint[0].pos_y = SCENE_H/2;//设置主方块位置信息
REC_tx[0]=REC_tx[1];//随机出现第一个图形
REC_tx[1] = rand()%7;
change(rpoint[0].pos_x,rpoint[0].pos_y,tx_code[REC_tx[0]][0],2);
temp_tx = 0;
}
break;
case LEFT://左移
for(int i=0;i<4;i++) //判断是否允许左移
temp_flag = temp_flag && scene_num[rpoint[i].pos_x][rpoint[i].pos_y-1] !=1 && rpoint[i].pos_y-1 >=0;
if(temp_flag){ //执行左移动作
clear(x,y,tx_code[tx][temp_tx]);change(x,y-1,tx_code[tx][temp_tx],2);
}break;
case RIGHT://右移
for(int i=0;i<4;i++) //判断是否允许右移
temp_flag = temp_flag && scene_num[rpoint[i].pos_x][rpoint[i].pos_y+1] !=1 && rpoint[i].pos_y+1 < SCENE_H;
if(temp_flag){ //执行右移动作
clear(x,y,tx_code[tx][temp_tx]);change(x,y+1,tx_code[tx][temp_tx],2);
}break;
case SPACE://快速下移
for(int j=0;j<SCENE_W;j++){
for(int i=0;i<4;i++) //判断是否允许下移
temp_flag = temp_flag && scene_num[rpoint[i].pos_x+1][rpoint[i].pos_y] !=1 &&
scene_num[rpoint[i].pos_x][rpoint[i].pos_y] !=1 && rpoint[i].pos_x+1 < SCENE_W;
if(temp_flag){ //执行下移动作
clear(x+j,y,tx_code[tx][temp_tx]);change(x+j+1,y,tx_code[tx][temp_tx],2);
}else
break;
}break;
}
}
void eluosiWidget::paintEvent(QPaintEvent *event)
{
//设置画笔
QPainter painter(this);
//刷新舞台
for(int i=0;i<SCENE_W;i++){
for(int j=0;j<SCENE_H;j++){
if(scene_num[i][j] == 1){
painter.setBrush(QBrush(Qt::blue,Qt::SolidPattern));
painter.drawRect(j*REC_SIZE,i*REC_SIZE,REC_SIZE,REC_SIZE);
}
else if(scene_num[i][j] > 1){
painter.setBrush(QBrush(Qt::red,Qt::SolidPattern));
painter.drawRect(j*REC_SIZE,i*REC_SIZE,REC_SIZE,REC_SIZE);
}
}
}
}
void eluosiWidget::timerEvent(QTimerEvent *event)
{
if(!Pauseflag && timerid1==event->timerId()){
weizhi(rpoint[0].pos_x,rpoint[0].pos_y,REC_tx[0],DOWN);//下移
}
if(!Pauseflag && timerid2==event->timerId()) this->update(); //刷新绘图
}
void eluosiWidget::keyPressEvent(QKeyEvent *event) //键盘事件
{
if(!Pauseflag)
switch(event->key()){
case Qt::Key_Up: //按下方向键上时
weizhi(rpoint[0].pos_x,rpoint[0].pos_y,REC_tx[0],UP);break;
case Qt::Key_Down: //按下方向键下时
weizhi(rpoint[0].pos_x,rpoint[0].pos_y,REC_tx[0],DOWN);break;
case Qt::Key_Left: //按下方向键左时
weizhi(rpoint[0].pos_x,rpoint[0].pos_y,REC_tx[0],LEFT);break;
case Qt::Key_Right: //按下方向键右时
weizhi(rpoint[0].pos_x,rpoint[0].pos_y,REC_tx[0],RIGHT);break;
case Qt::Key_Space: //快速下移
weizhi(rpoint[0].pos_x,rpoint[0].pos_y,REC_tx[0],SPACE);break;
}
}
void eluosiWidget::move(int hang, direction direct)
{
if(direct == UP){
for(int i=0;i<hang;i++)
for(int j=0;j<SCENE_H;j++)
scene_num[i][j] = scene_num[i+1][j];
}else if(direct == DOWN){
for(int i=hang;i>0;i--)
for(int j=0;j<SCENE_H;j++)
scene_num[i][j] = scene_num[i-1][j];
}
}
void eluosiWidget::SetHard(int level)
{
HardLevel = level;
Levelflag = true;
}
void eluosiWidget::SetSpeed(int level) //设置速度等级
{
speed_ms = 800 - level * 60;
PauseGame();
PlayGame();
}
void eluosiWidget::SetLevel()
{
for(int i=0;i<HardLevel;i++){
move(SCENE_W-1,UP);
for(int j=0;j<SCENE_H;j++)
scene_num[SCENE_W-1][j] = rand()%2;
}
}
void eluosiWidget::PlayGame()
{
timerid1 = startTimer(speed_ms);//设置下落速度
timerid2 = startTimer(fresh_ms);//设置刷新速度
Pauseflag = false;
}
void eluosiWidget::PauseGame()
{
killTimer(timerid1);killTimer(timerid2);
Pauseflag = true;
}
主函数:
#include <eluosiwidget.h>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
eluosiWidget w;
w.show();
return a.exec();
}
其他版本:
http://blog.csdn.net/imxiangzi/article/details/50667532
https://www.jerryzone.cn/qt-teris/