qt实现五子棋(二)
打开游戏,单击开始按钮游戏开始,按照事先选好的下棋顺序,将显示当前棋手的执子颜色与步数,并且允许当前棋色落子,同时伴随落子音效,也在检测是否选手获得胜利,一旦胜利,立马提示胜利方。棋盘大小可以根据窗口大小改变。
**第一个大步骤是构建棋盘。**将棋盘的逻辑(即棋盘)与下棋的逻辑(主界面)分开,棋盘可以响应鼠标事件发出当前坐标信号,主界面接收坐标信号,并利用棋盘提供的刷新棋盘的方法,刷新棋盘。每下一步棋,刷新棋盘,判断是否胜利,切换角色方等。
这个小项目里面有前面基础篇讲到东西的应用,也有没出现的类的简单应用,同时更应看重,程序模块化解耦的设计能力,这种能力能让大型项目瞬间”土崩瓦解“,让你的编程、调试、测试井井有条,游刃有余。
由于本人电脑加密的原因,无法将工程上传,所以请各位结合下面的步骤说明和代码注释理解并创建自己的工程。
创建chessplate类,继承自QWidget,虽然不带ui界面,但在main中实例化该对象并调用show方法,会显示出界面,这是因为它继承自widget。
.pro中添加多媒体模块以便于读QSound的使用,如下:
QT += multimedia
在chessplate.h文件中添加用到类的头文件,如下:
#include
#include
#include
#include
#include
①画背景图(末尾一起贴代码)
②画棋盘线。将棋盘格子、起始、结束点定义成成员变量, 当窗口发生改变的时候,触发resize事件,在resize事件中改变上述三个变量的大小,同时resize又将触发QPainter事件,进行界面重绘,那么将实现棋盘会随着窗口大小的改变而改变。
③画定位点,可以使用brush在四个点画圆,也可以在固定位置画图片。
④画棋子,使用二维数组存储当前棋盘的状态,例如1代表黑色。使用鼠标点击事件,点击生成坐标,坐标信息结合当前角色刷新存储阵列,调用update()重绘图片,将根据最新的存储阵列刷新棋盘状态。为了能与其他模块交互,进行如下设计,在鼠标点击的时候,将坐标信息(不是真实的坐标,而是经过转换的格子的坐标)当成信号发出,其他类(主要指主界面)收到这个信息,刷新主界面中的存储阵列p,将p作为参数传递给棋盘类提供的刷新棋盘的方法中,完成模块之间的交互,完成界面的刷新。除此之外,还应注意,棋子应该画到棋盘横竖线的交叉点上。
单击鼠标,记录当前的坐标信息,将其划分到格子中,即获取用户是想在哪个位置落子的信息,将这个格子的坐标当作信号发出。如果想做细致,应该考虑约束鼠标点击事件的响应方位,例如应限制在交叉点的多少半径范围之内才进行响应,避免用户不小心点错。
每次resize会自动触发QPainter重绘,那么在resize的事件中只需要,重新回去当前的界面大小信息,随后计算在这个界面大小的情况下,棋盘格子应该为多大,将这些计算好之后QPainter将依据这个数据重新绘制,确保棋盘大小随着界面大小动态变化。但是考虑到棋盘的美观性,应该对界面能拉大缩小的范围有所限制,当然这个限制是在调用它的主界面中做的。
提供修改背景图片的方法,提供修改棋盘线粗、线风格、线颜色的方法,提供刷新棋盘状态的方法。当一个类想对外提供修改某些东西的方法时,应该添加类成员变量,然后利用对外方法函数的参数来改变类成员变量的值,而这些个成员变量同时被”要修改的内容“当作参数使用。这就使得,外部类通过提供新的参数来改变这个类的东西。
//提供修改背景图片的方法
void setBackGroundPic(const QString filename);
//修改棋盘线风格
void setPlateLine(const QColor color,const Qt::PenStyle style,const int width);
//刷新棋盘
void setChessStatus(void *p);
在main中实例化并调用棋盘类的show方法,可以看到如下效果。
在这里插入图片描述
贴出代码如下:
chessPlate.h代码
#ifndef CHESSPLATE_H
#define CHESSPLATE_H
#include
#include
#include
#include
#include
#define GRIDCOUNT 16
class chessPlate : public QWidget
{
Q_OBJECT
public:
chessPlate(QWidget *parent = nullptr);
~chessPlate();
enum ChessType{Empty=0,White,Black};
//提供修改背景图片的方法
void setBackGroundPic(const QString filename);
//修改棋盘线风格
void setPlateLine(const QColor color,const Qt::PenStyle style,const int width);
//刷新棋盘
void setChessStatus(void *p);
protected:
void paintEvent(QPaintEvent *);
void resizeEvent(QResizeEvent *);
void mousePressEvent(QMouseEvent *);
private:
QString bgFileName;
QColor lineColor;
Qt::PenStyle lineStyle;
int lineWidth;
//格子宽高,画线起始点
int gridWidth, gridHeight,startX,startY;
int chessData[15][15];
void Init();
signals:
void mousePosSend(int i,int j);
};
#endif // CHESSPLATE_H
chessPlate.cpp
#include "chessplate.h"
chessPlate::chessPlate(QWidget *parent)
: QWidget(parent)
{
Init();
}
chessPlate::~chessPlate()
{
}
void chessPlate::paintEvent(QPaintEvent *)
{
QPainter painter(this);
//画背景
QRect rect(QPoint(0,0),QSize(this->width(),this->height()));
QPixmap pix(bgFileName);
painter.drawPixmap(rect,pix);
// QPixmap pix2(":/res/black.png");
// painter.drawPixmap(QPoint(100,30),pix2);
//画线
QPen pen;
pen.setColor(lineColor);
pen.setStyle(lineStyle);
pen.setWidth(lineWidth);
painter.setPen(pen);
for (int i = 0;i<15 ;i++ ) {
//画线
painter.drawLine(startX,startY+i*gridHeight,startX+14*gridWidth,startY+i*gridHeight);//x
painter.drawLine(startX+i*gridWidth,startY,startX+i*gridWidth,startY+14*gridHeight);//y
}
//画上四个圆点
QBrush brs;
brs.setColor(Qt::black);
brs.setStyle(Qt::SolidPattern);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setBrush(brs);
painter.drawEllipse(QPoint(4*gridWidth,4*gridHeight),6,6);
painter.drawEllipse(QPoint(12*gridWidth,4*gridHeight),6,6);
painter.drawEllipse(QPoint(8*gridWidth,8*gridHeight),6,6);
painter.drawEllipse(QPoint(12*gridWidth,12*gridHeight),6,6);
painter.drawEllipse(QPoint(4*gridWidth,12*gridHeight),6,6);
painter.setPen(pen);
//落子展示
QString chessFilename;
for (int i= 0;i<15 ;i++ ) {
for (int j = 0;j<15 ;j++ ) {
if(chessData[i][j]==White){
chessFilename = ":/res/white.png";
}
else if(chessData[i][j]==Black){
chessFilename = ":/res/black.png";
}
else
{
chessFilename.clear();
continue;
}
painter.drawPixmap(startX/2+i*gridWidth,startY/2+j*gridHeight,gridWidth,gridHeight,QPixmap(chessFilename));
}
}
}
void chessPlate::resizeEvent(QResizeEvent *event)
{
this->gridWidth = event->size().width()/GRIDCOUNT;
this->gridHeight = event->size().height()/GRIDCOUNT;
startX = gridWidth;
startY = gridHeight;
}
void chessPlate::mousePressEvent(QMouseEvent *event)
{
int x=event->x();
int y=event->y();
//x
if(x>=startX/2 &&(x<=startX/2+15*gridWidth)){
//y
if(y>=startY/2&&(y<=startY/2+15*gridHeight))
{
//得出当前坐标属于哪个格子 格子与实际格子并未对应,是因为,把他放在对角线上
int i = 0,j =0;
i = (x-startX/2)/gridWidth;
j = (y-startY/2)/gridHeight;
// chessData[i][j]=Black;
// this->update();
mousePosSend(i,j);
}
}
}
void chessPlate::Init()
{
//初始化函数:设置成员变量初始值(默认值);设置界面的标题与图标等初始状态
bgFileName.clear();
bgFileName = ":/res/chess_back.jpg";
lineColor = Qt::black;
lineStyle = Qt::SolidLine;
lineWidth = 3;
for (int i=0;i<15 ;i++ ) {
for (int j = 0;j<15 ;j++ ) {
chessData[i][j]=Empty;
}
}
// chessData[3][3] = White;
// chessData[4][4] = Black;
}
void chessPlate::setBackGroundPic(const QString filename)
{
this->bgFileName = filename;
this->update();
}
void chessPlate::setPlateLine(const QColor color, const Qt::PenStyle style, const int width)
{
this->lineColor = color;
this->lineStyle = style;
this->lineWidth = width;
this->update();
}
void chessPlate::setChessStatus(void *p)
{
memcpy(chessData,p,sizeof(int)*15*15);
this->update();
}
未完待续……