今天给大家带来一个QT的五子棋项目,没有用到xml文件,界面都是纯代码写的,这个项目的主要难点在于得分的计算、输赢的判断逻辑、界面的规划,用到了paintevent等事件,以及定时器的技术,主要写了两个类一个是主界面的,另一个是是关于游戏模式的类,因为这个可以实现人人对战,与人机对战。
首先来看运行如下:
1 项目的目录结构如下:
2 mainwindow.h:
这个就是主界面的头文件,因为主界面上的菜单栏要选择游戏模式,所以和gamemodel类交互必须包含gamemodel的头文件。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "gamemodel.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
//只要窗口部件需要被重绘就被调用
void paintEvent(QPaintEvent *event);
//鼠标移动落子 虚的
void mouseMoveEvent(QMouseEvent *event);
//真实落子
void mouseReleaseEvent(QMouseEvent *event);
private:
GameModel *game; // 游戏指针
GameType game_type; // 存储游戏类型
//存储将点击的位置
int clickPosRow,clickPosCol;
//初始化游戏
void initGame();
private slots:
//人下棋
void Peoplechess();
//AI下棋
void AIchess();
//初始化人与人模式游戏界面
void initPVPGame();
//初始化人与机器模式游戏界面
void initPVEGame();
};
#endif // MAINWINDOW_H
3 mainwindow.cpp:
这个主界面就是先画出棋盘,再进行逻辑的判断,完善人下棋机器下棋,以及鼠标移动事件等操作
#include "mainwindow.h"
#include <QPainter>
#include <QTimer>
#include <QSound>
#include <QMouseEvent>
#include <QMessageBox>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QDebug>
#include <math.h>
//游戏的音频资源
#define CHESS_ONE_SOUND "J:/respicture/chessone.wav"
#define WIN_SOUND "J:/respicture/win.wav"
#define LOSE_SOUND "J:/respicture/lose.wav"
//棋盘边缘空隙
const int Edge_gapSize=30;
//五子棋半径
const int Radius=15;
//五子棋落子标记边长
const int MarkSize=6;
//格子大小
const int LatticeSize=40;
//鼠标点击的模糊距离上限
const int ObscurePos=20;
//AI思考时间
const int AIthinkTime=700;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
//棋盘大小
setFixedSize(Edge_gapSize*2+LatticeSize*kBoardSizeNum,Edge_gapSize*2+LatticeSize*kBoardSizeNum);
//使用setMouseTracking(true)对鼠标进行监控(mouseMoveEvent(QMouseEvent *event)),如果WidgetA有个子窗体WidgetB会占据WidgetA的绝大部分空间,那么当鼠标移动到WidgetB上时,WidgetA就会失去对鼠标的监控。
setMouseTracking(true);
QMenu *menu=menuBar()->addMenu(tr("Game"));
QAction *actionPVP = new QAction("Person VS Person", this);
QAction *actionPVE = new QAction("Person VS Computer", this);
menu->addAction(actionPVP);
menu->addAction(actionPVE);
//菜单栏信号与槽
connect(actionPVP,SIGNAL(triggered(bool)),this,SLOT(initPVPGame()));
connect(actionPVE,SIGNAL(triggered(bool)),this,SLOT(initPVEGame()));
//初始化游戏
initGame();
}
MainWindow::~MainWindow()
{
if(game){
delete game;
//这个nullptr看看这个https://zhuanlan.zhihu.com/p/257421030
game=nullptr;
}
}
void MainWindow::initGame()
{
game = new GameModel;
initPVPGame();
}
void MainWindow::initPVPGame()
{
game_type = PERSON;
game->gameStatus = PLAYING;
game->startGame(game_type);
update();
}
void MainWindow::initPVEGame()
{
game_type = AI;
game->gameStatus = PLAYING;
game->startGame(game_type);
update();
}
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing,true);
for(int i=0;i<kBoardSizeNum+1;i++){
//列
painter.drawLine(Edge_gapSize+LatticeSize*i,Edge_gapSize,Edge_gapSize+LatticeSize*i,size().height()-Edge_gapSize);
//行
painter.drawLine(Edge_gapSize,Edge_gapSize+LatticeSize*i,size().width()-Edge_gapSize,LatticeSize*i+Edge_gapSize);
}
QBrush brush;
//纯色 https://doc.qt.io/archives/qt-4.8/qbrush.html#:~:text=The%20standard%20style%20for%20filling%20is%20Qt%3A%3ASolidPattern.%20The,%28%29%20defines%20the%20color%20of%20the%20fill%20pattern.
brush.setStyle(Qt::SolidPattern);
//绘制落子标记
if (clickPosRow > 0 && clickPosRow < kBoardSizeNum &&
clickPosCol > 0 && clickPosCol < kBoardSizeNum &&
game->gameMapVec[clickPosRow][clickPosCol] == 0){
if(game->playerFlag){
brush.setColor(Qt::white);
}else{
brush.setColor(Qt::black);
}
painter.drawRect(Edge_gapSize + LatticeSize * clickPosCol - MarkSize / 2, Edge_gapSize + LatticeSize * clickPosRow - MarkSize / 2, MarkSize, MarkSize);
}
// 绘制棋子
for(int i=0;i<kBoardSizeNum;i++){
for(int j=0;j<kBoardSizeNum;j++){
//画白棋
if(game->gameMapVec[i][j]==1){
brush.setColor(Qt