QT环境:QT5.2.0,Qt Creator3.0.0
1.自定义方块类
class Square : public QGraphicsItem
{
public:
explicit Square(QPoint pos,QString text,QString background_color);
QString getText() {return text;}
void updatePos(QPoint pos);
void updateText(QString text,QString background_color);
void updateBox(QPoint pos,QString text,QString background_color);
enum {Type = UserType + 1};
int type() const;
private :
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QString text;
QString color;
};
Square::Square(QPoint pos,QString text,QString background_color) : QGraphicsItem()
{
setPos(pos);
this->text = text;
this->color = background_color;
setFlag(QGraphicsItem::ItemIsFocusable);
}
int Square::type() const
{
return Type;
}
QRectF Square::boundingRect() const
{
int penWidth = 1;
return QRectF(0-penWidth/2,0-penWidth/2,105+penWidth,105+penWidth);
}
void Square::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
//画背景
painter->setPen(QColor(color));
painter->setBrush(QBrush(QColor(color)));
painter->drawRect(QRect(0,0,105,105));
//设置字体 字号
switch(text.length())
{
case 1:
painter->setFont(QFont(QString::fromLocal8Bit("微软雅黑"),50,QFont::Bold));
break;
case 2:
painter->setFont(QFont(QString::fromLocal8Bit("微软雅黑"),40,QFont::Bold));
break;
case 3:
painter->setFont(QFont(QString::fromLocal8Bit("微软雅黑"),35,QFont::Bold));
break;
case 4:
painter->setFont(QFont(QString::fromLocal8Bit("微软雅黑"),30,QFont::Bold));
break;
default:
break;
}
//设置字体颜色
if(text.toInt() == 2 || text.toInt() == 4)
{
painter->setPen(QColor("#7c736a"));
}
else if(text.toInt() >= 8)
{
painter->setPen(QColor("#fff7eb"));
}
painter->drawText(QRect(0,0,105,105),Qt::AlignCenter,text);
}
void Square::updatePos(QPoint pos)
{
setPos(pos);
update(boundingRect());
}
void Square::updateText(QString text,QString background_color)
{
this->text = text;
this->color = background_color;
update(boundingRect());
}
void Square::updateBox(QPoint pos,QString text,QString background_color)
{
this->text = text;
this->color = background_color;
setPos(pos);
update(boundingRect());
}
2.自定义视图类
class GameView : public QGraphicsView
{
Q_OBJECT
public:
explicit GameView();
~GameView();
private:
QGraphicsScene *g_scene;
QMap colorBox;
void createGrid();
void createColorBox();
void startGame();
void restartGame();
bool upMerge();
bool downMerge();
bool leftMerge();
bool rightMerge();
bool newSquare();
void countEmpty(QList > &empty_index);
bool checkSucceeded();
bool checkFinished();
void keyPressEvent(QKeyEvent * event);
void drawBackground(QPainter * painter, const QRectF & rect);
public slots:
};
GameView::GameView() : QGraphicsView()
{
g_scene = new QGraphicsScene();
g_scene->setSceneRect(0,0,500,500);
setScene(g_scene);
setCacheMode(QGraphicsView::CacheBackground);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
createGrid();
createColorBox();
startGame();
}
GameView::~GameView()
{
}
void GameView::createGrid()
{
int penWidth = 16;
QPen pen(QBrush(Qt::SolidPattern),penWidth,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin);
pen.setColor(QColor("#b8af9e"));
for(int i = 0;i < 5;i ++)
{
g_scene->addLine(i*(105 + penWidth)+penWidth/2,0,i*(105 + penWidth)+penWidth/2,500,pen);
}
for(int j = 0;j < 5;j ++)
{
g_scene->addLine(0,j*(105 + penWidth)+penWidth/2,500,j*(105 + penWidth)+penWidth/2,pen);
}
}
void GameView::createColorBox()
{
colorBox.insert(0,tr("#ccc0b2"));
colorBox.insert(2,tr("#eee4da"));
colorBox.insert(4,tr("#ece0c8"));
colorBox.insert(8,tr("#f2b179"));
colorBox.insert(16,tr("#f59563"));
colorBox.insert(32,tr("#f57c5f"));
colorBox.insert(64,tr("#f65d38"));
colorBox.insert(128,tr("#edc171"));
colorBox.insert(256,tr("#edcc61"));
colorBox.insert(512,tr("#ecc850"));
colorBox.insert(1024,tr("#edc53f"));
colorBox.insert(2048,tr("#efc4ef"));
}
void GameView::startGame()
{
int index1,index2;
int num1,num2;
//随机生成位置(16个方格,0~15编号)
index1 = qrand()%16;
index2 = qrand()%16;
while(index2 == index1)
{
index2 = qrand()%16;
}
//随机生成2或4(随机生成0,1,则0代表2,1代表4)
num1 = qrand()%2==0 ? 2 : 4;
num2 = qrand()%2==0 ? 2 : 4;
QPoint pos1((index1%4)*(105+16)+16,(index1/4)*(105+16)+16);
QPoint pos2((index2%4)*(105+16)+16,(index2/4)*(105+16)+16);
g_scene->addItem(new Square(pos1,QString::number(num1),colorBox.value(num1)));
g_scene->addItem(new Square(pos2,QString::number(num2),colorBox.value(num2)));
}
void GameView::restartGame()
{
QList list = scene()->items(0,0,500,500,Qt::IntersectsItemShape,Qt::AscendingOrder);
foreach(QGraphicsItem *item,list)
{
scene()->removeItem(item);
delete item;
item = NULL;
}
createGrid();
startGame();
}
bool GameView::upMerge()
{
bool can_move = false;
for(int i = 0;i < 4;i ++)
for(int j = 0;j < 4;j ++)
{
QList current = scene()->items(i*(105+16)+16,j*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(current.count()<=0)
{
continue;
}
Square *c_square = qgraphicsitem_cast(current[0]);
for(int k = j + 1;k < 4;k ++)
{
QList next = scene()->items(i*(105+16)+16,k*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(next.count()<=0)
{
continue;
}
Square *n_square = qgraphicsitem_cast(next[0]);
if(n_square->getText() == c_square->getText())
{
int num = c_square->getText().toInt()*2;
c_square->updateText(QString::number(num),colorBox.value(num));
scene()->removeItem(n_square);
delete n_square;
n_square = NULL;
can_move = true;
}
break;
}
}
for(int i = 0;i < 4;i ++)
{
int empty_count = 0;
for(int j = 0;j < 4;j ++)
{
QList current = scene()->items(i*(105+16)+16,j*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(current.count()<=0)
{
empty_count ++;
continue;
}
Square *square = qgraphicsitem_cast(current[0]);
square->updatePos(QPoint(square->pos().x(),square->pos().y()-empty_count*(105+16)));
if(empty_count > 0)
{
can_move = true;
}
}
}
viewport()->update();
return can_move;
}
bool GameView::downMerge()
{
bool can_move = false;
for(int i = 0;i <= 3;i ++)
for(int j = 3;j >= 0;j --)
{
QList current = scene()->items(i*(105+16)+16,j*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(current.count()<=0)
{
continue;
}
Square *c_square = qgraphicsitem_cast(current[0]);
for(int k = j - 1;k >= 0;k --)
{
QList next = scene()->items(i*(105+16)+16,k*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(next.count()<=0)
{
continue;
}
Square *n_square = qgraphicsitem_cast(next[0]);
if(n_square->getText() == c_square->getText())
{
int num = c_square->getText().toInt()*2;
c_square->updateText(QString::number(num),colorBox.value(num));
scene()->removeItem(n_square);
delete n_square;
n_square = NULL;
can_move = true;
}
break;
}
}
for(int i = 0;i < 4;i ++)
{
int empty_count = 0;
for(int j = 3;j >= 0;j --)
{
QList current = scene()->items(i*(105+16)+16,j*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(current.count()<=0)
{
empty_count ++;
continue;
}
Square *square = qgraphicsitem_cast(current[0]);
square->updatePos(QPoint(square->pos().x(),square->pos().y()+empty_count*(105+16)));
if(empty_count>0)
{
can_move = true;
}
}
}
viewport()->update();
return can_move;
}
bool GameView::leftMerge()
{
bool can_move = false;
for(int i = 0;i < 4;i ++)
for(int j = 0; j < 4;j ++)
{
QList current = scene()->items(j*(105+16)+16,i*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(current.count()<=0)
{
continue;
}
Square *c_square = qgraphicsitem_cast(current[0]);
for(int k = j+1;k < 4;k ++)
{
QList next = scene()->items(k*(105+16)+16,i*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(next.count()<=0)
{
continue;
}
Square *n_square = qgraphicsitem_cast(next[0]);
if(c_square->getText() == n_square->getText())
{
int num = c_square->getText().toInt()*2;
c_square->updateText(QString::number(num),colorBox.value(num));
scene()->removeItem(n_square);
delete n_square;
n_square = NULL;
can_move = true;
}
break;
}
}
for(int i = 0;i < 4;i ++)
{
int empty_count = 0;
for(int j = 0;j < 4;j ++)
{
QList current = scene()->items(j*(105+16)+16,i*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(current.count()<=0)
{
empty_count ++;
continue;
}
Square *square = qgraphicsitem_cast(current[0]);
square->updatePos(QPoint(square->pos().x()-empty_count*(105+16),square->pos().y()));
if(empty_count>0)
{
can_move = true;
}
}
}
viewport()->update();
return can_move;
}
bool GameView::rightMerge()
{
bool can_move = false;
for(int i = 0;i < 4;i ++)
for(int j = 3; j>= 0;j --)
{
QList current = scene()->items(j*(105+16)+16,i*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(current.count()<=0)
{
continue;
}
Square *c_square = qgraphicsitem_cast(current[0]);
for(int k = j-1;k >= 0;k --)
{
QList next = scene()->items(k*(105+16)+16,i*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(next.count()<=0)
{
continue;
}
Square *n_square = qgraphicsitem_cast(next[0]);
if(c_square->getText() == n_square->getText())
{
int num = c_square->getText().toInt()*2;
c_square->updateText(QString::number(num),colorBox.value(num));
scene()->removeItem(n_square);
delete n_square;
n_square = NULL;
can_move = true;
}
break;
}
}
for(int i = 0;i < 4;i ++)
{
int empty_count = 0;
for(int j = 3;j >= 0;j --)
{
QList current = scene()->items(j*(105+16)+16,i*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(current.count()<=0)
{
empty_count ++;
continue;
}
Square *square = qgraphicsitem_cast(current[0]);
square->updatePos(QPoint(square->pos().x()+empty_count*(105+16),square->pos().y()));
if(empty_count>0)
{
can_move = true;
}
}
}
viewport()->update();
return can_move;
}
bool GameView::newSquare()
{
QList > empty_index;
countEmpty(empty_index);
int num = qrand()%2==0 ? 2 : 4;
int index = qrand()%empty_index.count();
QPoint pos(empty_index[index].first*(105+16)+16,empty_index[index].second*(105+16)+16);
g_scene->addItem(new Square(pos,QString::number(num),colorBox.value(num)));
countEmpty(empty_index);
if(empty_index.count() <= 0)
{
if(checkFinished()) //添加新块,检查是否Game over
{
return false;
}
}
return true;
}
void GameView::countEmpty(QList > &empty_index)
{
empty_index.clear();
for(int i = 0;i < 4;i ++) //行号
{
for(int j = 0;j < 4;j ++) //列号
{
QList list = scene()->items(i*(105+16)+16,j*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(list.count() <= 0)
{
empty_index.append(qMakePair(i,j));
}
}
}
}
bool GameView::checkSucceeded()
{
for(int i = 0;i < 4;i ++)
for(int j = 0;j < 4;j ++)
{
QList list = scene()->items(i*(105+16)+16,j*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
if(list.count() >= 1)
{
Square *square = qgraphicsitem_cast(list[0]);
if(square->getText().toInt() == 2048)
{
QMessageBox::information(this,QStringLiteral("好消息"),QStringLiteral("恭喜完成,太厉害啦!"),QMessageBox::Ok);
return true;
}
}
}
return false;
}
bool GameView::checkFinished()
{
QList current;
QList next;
Square *c_square;
Square *n_square;
for(int i = 0;i < 4;i ++)
for(int j = 0;j < 3;j ++)
{
current.clear();
next.clear();
current = scene()->items(i*(105+16)+16,j*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
next = scene()->items(i*(105+16)+16,(j+1)*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
c_square = qgraphicsitem_cast(current[0]);
n_square = qgraphicsitem_cast(next[0]);
if(c_square->getText() == n_square->getText())
{
return false;
}
current.clear();
next.clear();
current = scene()->items(j*(105+16)+16,i*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
next = scene()->items((j+1)*(105+16)+16,i*(105+16)+16,105,105,Qt::IntersectsItemShape,Qt::AscendingOrder);
c_square = qgraphicsitem_cast(current[0]);
n_square = qgraphicsitem_cast(next[0]);
if(c_square->getText() == n_square->getText())
{
return false;
}
}
QMessageBox::information(this,QStringLiteral("游戏结束"),QStringLiteral("游戏结束..."),QMessageBox::Ok);
return true;
}
void GameView::keyPressEvent(QKeyEvent * event)
{
switch(event->key())
{
case Qt::Key_Up:
if(upMerge())
{
if(checkSucceeded() || !newSquare())
{
restartGame();
}
}
break;
case Qt::Key_Down:
if(downMerge())
{
if(checkSucceeded() || !newSquare())
{
restartGame();
}
}
break;
case Qt::Key_Left:
if(leftMerge())
{
if(checkSucceeded() || !newSquare())
{
restartGame();
}
}
break;
case Qt::Key_Right:
if(rightMerge())
{
if(checkSucceeded() || !newSquare())
{
restartGame();
}
}
break;
default:
QGraphicsView::keyPressEvent(event);
break;
}
}
void GameView::drawBackground(QPainter * painter, const QRectF & rect)
{
painter->setBrush(QBrush(QColor("#ccc0b2")));
painter->drawRect(QRect(sceneRect().x(),sceneRect().y(),sceneRect().width(),sceneRect().height()));
}
最后剩下的就是把view显示到mainwindow上:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle(tr("2048"));
setIconSize(QSize(48,48));
setFixedSize(542,542);
setWindowIcon(QIcon(tr("://resources/window_ico_48.png")));
ui->center->addWidget(new GameView());
}
总结:
代码中有许多不完善的地方,比如方块合并没有特效、没有移动轨迹;新方块的出现太突兀和随机;合并算法比较累赘;程序逻辑不够清晰。甚至存在许多没有发现的错误。不管怎样,作为代码小菜鸟算是抛砖引玉了,请给位大牛指教!!!
附上效果图一张: