前言
该项目利用QT,完成一个小游戏–宠物连连看。
其中最主要的是使用数据库和画家类!
一、main_game_window.cpp
#include <QDebug>
#include <QSound>
#include <QAction>
#include <QMessageBox>
#include <QPainter>
#include <QLine>
#include <QTimer>
#include "main_game_window.h"
#include "ui_main_game_window.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QSqlQuery>
// --------- 全局变量 --------- //
const int kIconSize = 36;
const int kTopMargin = 70;
const int kLeftMargin = 50;
const QString kIconReleasedStyle = "";
const QString kIconClickedStyle = "background-color: rgba(255, 255, 12, 161)";
const QString kIconHintStyle = "background-color: rgba(255, 0, 0, 255)";
const int kGameTimeTotal = 5 * 60 * 1000; // 总时间
const int kGameTimerInterval = 300;
const int kLinkTimerDelay = 700;
// -------------------------- //
// 游戏主界面
MainGameWindow::MainGameWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainGameWindow),
preIcon(NULL),
curIcon(NULL)
{
ui->setupUi(this);
ui->centralWidget->installEventFilter(this);
// 选关
connect(ui->actionBasic, SIGNAL(triggered(bool)), this, SLOT(createGameWithLevel()));
connect(ui->actionMedium, SIGNAL(triggered(bool)), this, SLOT(createGameWithLevel()));
connect(ui->actionHard, SIGNAL(triggered(bool)), this, SLOT(createGameWithLevel()));
// 初始化游戏
initGame(BASIC);
//登录界面
www = new QWidget;
www->show();
// this->hide();
www->setMaximumSize(850,690);
lb1 = new QLineEdit;
lb2 = new QLineEdit;
lb1->setPlaceholderText("用户名");
lb1->setMinimumWidth(10);
lb2->setPlaceholderText("密码");
lb2->setMinimumWidth(10);
bt1 = new QPushButton("登录");
bt2 = new QPushButton("注册");
// this->setBackgroundRole(QPalette::color(redirected()));
QErrorMessage error;
bt_errmsg = new QPushButton;
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(bt1);
hbox->addWidget(bt2);
QVBoxLayout *vbox=new QVBoxLayout;
vbox->addWidget(lb1);
vbox->addWidget(lb2);
vbox->addLayout(hbox);
www->setLayout(vbox);
//创建数据库
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("namepassdb");
bool ok = db.open();
QString str("create table nampass(name, pass);");
QSqlQuery query;
query.exec(str);
connect(bt1,SIGNAL(clicked(bool)),this,SLOT(show_widget()));
connect(bt2,SIGNAL(clicked(bool)),this,SLOT(registered()));
}
void MainGameWindow::show_widget()
{
QString str =QString("select *from namepass where name='%0' and pass='%1';").arg(lb1->text().arg(lb2->text()));
QSqlQuery query;
query.exec(str);
qDebug()<<str;//测试下
if(false==query.next())
show_error();
else
{
while(query.next())//测试下
{
qDebug() << query.value(0).toString();
qDebug() << query.value(1).toString();
}
// www->hide();
this->show();
}
}
void MainGameWindow::registered()//注册
{
QString str=QString("insert into namepass values('%0','%1');").arg(lb1->text()).arg(lb2->text());
QSqlQuery query;
qDebug()<<query.exec(str);
lb1->clear();
lb2->clear();
}
void MainGameWindow::show_error()
{
error->showMessage("错误");
error->exec();
}
MainGameWindow::~MainGameWindow()
{
if (game)
delete game;
delete ui;
}
void MainGameWindow::initGame(GameLevel level)
{
// 启动游戏
game = new GameModel;
game->startGame(level);
// 添加button
for(int i = 0; i < MAX_ROW * MAX_COL; i++)
{
imageButton[i] = new IconButton(this);
imageButton[i]->setGeometry(kLeftMargin + (i % MAX_COL) * kIconSize, kTopMargin + (i / MAX_COL) * kIconSize, kIconSize, kIconSize);
// 设置索引
imageButton[i]->xID = i % MAX_COL;
imageButton[i]->yID = i / MAX_COL;
imageButton[i]->show();
if (game->getGameMap()[i])
{
// 有方块就设置图片
QPixmap iconPix;
QString fileString;
fileString.sprintf(":/res/image/%d.png", game->getGameMap()[i]);
iconPix.load(fileString);
QIcon icon(iconPix);
imageButton[i]->setIcon(icon);
imageButton[i]->setIconSize(QSize(kIconSize, kIconSize));
// 添加按下的信号槽
connect(imageButton[i], SIGNAL(pressed()), this, SLOT(onIconButtonPressed()));
}
else
imageButton[i]->hide();
}
// 进度条
ui->timeBar->setMaximum(kGameTimeTotal);
ui->timeBar->setMinimum(0);
ui->timeBar->setValue(kGameTimeTotal);
// 游戏计时器
gameTimer = new QTimer(this);
connect(gameTimer, SIGNAL(timeout()), this, SLOT(gameTimerEvent()));
gameTimer->start(kGameTimerInterval);
// 连接状态值
isLinking = false;
// 播放背景音乐
audioPlayer = new QMediaPlayer(this);
QString curDir = QCoreApplication::applicationDirPath();
QStringList sections = curDir.split(QRegExp("[/]"));
QString musicPath;
for (int i = 0; i < sections.size() - 3; i++)
musicPath += sections[i] + "/";
audioPlayer->setMedia(QUrl::fromLocalFile(musicPath + "res/sound/backgrand.mp3"));
audioPlayer->play();
}
void MainGameWindow::onIconButtonPressed()
{
// 如果当前有方块在连接,不能点击方块
// 因为涉及到多线,可能还要维护队列,有点复杂,就先这么简单处理一下
if (isLinking)
{
// 播放音效
QSound::play(":/res/sound/release.wav");
return;
}
// 记录当前点击的icon
curIcon = dynamic_cast<IconButton *>(sender());
if(!preIcon)
{
// 播放音效
QSound::play(":/res/sound/select.wav");
// 如果单击一个icon
curIcon->setStyleSheet(kIconClickedStyle);
preIcon = curIcon;
}
else
{
if(curIcon != preIcon)
{
// 如果不是同一个button就都标记,尝试连接
curIcon->setStyleSheet(kIconClickedStyle);
if(game->linkTwoTiles(preIcon->xID, preIcon->yID, curIcon->xID, curIcon->yID))
{
isLinking = true;
QSound::play(":/res/sound/pair.wav");
update();
//连接效果
QTimer::singleShot(kLinkTimerDelay, this, SLOT(handleLinkEffect()));
//是否胜利
if (game->isWin())
QMessageBox::information(this, "great", "you win");
int *hints = game->getHint();
}
else
{
// 播放音效
QSound::play(":/res/sound/release.wav");
// 消除失败,恢复
preIcon->setStyleSheet(kIconReleasedStyle);
curIcon->setStyleSheet(kIconReleasedStyle);
// 指针置空,用于下次点击判断
preIcon = NULL;
curIcon = NULL;
}
}
else if(curIcon == preIcon)
{
// 播放音效
QSound::play(":/res/sound/release.wav");
preIcon->setStyleSheet(kIconReleasedStyle);
curIcon->setStyleSheet(kIconReleasedStyle);
preIcon = NULL;
curIcon = NULL;
}
}
}
void MainGameWindow::handleLinkEffect()
{
// 消除成功
game->paintPoints.clear();
preIcon->hide();
curIcon->hide();
preIcon = NULL;
curIcon = NULL;
ui->timeBar->value()+1;
// 重绘
update();
// 恢复状态
isLinking = false;
}
bool MainGameWindow::eventFilter(QObject *watched, QEvent *event)
{
// 重绘时会调用,可以手动调用
if (event->type() == QEvent::Paint)
{
QPainter painter(ui->centralWidget);
QPen pen;
QColor color(rand() % 256, rand() % 256, rand() % 256);
pen.setColor(color);
pen.setWidth(5);
painter.setPen(pen);
QString str;
for (int i = 0; i < game->paintPoints.size(); i++)
{
PaintPoint p = game->paintPoints[i];
str += "x:" + QString::number(p.x) + "y:" + QString::number(p.y) + "->";
}
// qDebug() << str;
// 连接各点画线(注,qt中用标砖vector的size好像有点问题,需要类型转换,否则溢出)
for (int i = 0; i < int(game->paintPoints.size()) - 1; i++)
{
PaintPoint p1 = game->paintPoints[i];
PaintPoint p2 = game->paintPoints[i + 1];
// 拿到各button的坐标,注意边缘点坐标
QPoint btn_pos1;
QPoint btn_pos2;
// p1
if (p1.x == -1)
{
btn_pos1 = imageButton[p1.y * MAX_COL + 0]->pos();
btn_pos1 = QPoint(btn_pos1.x() - kIconSize, btn_pos1.y());
}
else if (p1.x == MAX_COL)
{
btn_pos1 = imageButton[p1.y * MAX_COL + MAX_COL - 1]->pos();
btn_pos1 = QPoint(btn_pos1.x() + kIconSize, btn_pos1.y());
}
else if (p1.y == -1)
{
btn_pos1 = imageButton[0 + p1.x]->pos();
btn_pos1 = QPoint(btn_pos1.x(), btn_pos1.y() - kIconSize);
}
else if (p1.y == MAX_ROW)
{
btn_pos1 = imageButton[(MAX_ROW - 1) * MAX_COL + p1.x]->pos();
btn_pos1 = QPoint(btn_pos1.x(), btn_pos1.y() + kIconSize);
}
else
btn_pos1 = imageButton[p1.y * MAX_COL + p1.x]->pos();
// p2
if (p2.x == -1)
{
btn_pos2 = imageButton[p2.y * MAX_COL + 0]->pos();
btn_pos2 = QPoint(btn_pos2.x() - kIconSize, btn_pos2.y());
}
else if (p2.x == MAX_COL)
{
btn_pos2 = imageButton[p2.y * MAX_COL + MAX_COL - 1]->pos();
btn_pos2 = QPoint(btn_pos2.x() + kIconSize, btn_pos2.y());
}
else if (p2.y == -1)
{
btn_pos2 = imageButton[0 + p2.x]->pos();
btn_pos2 = QPoint(btn_pos2.x(), btn_pos2.y() - kIconSize);
}
else if (p2.y == MAX_ROW)
{
btn_pos2 = imageButton[(MAX_ROW - 1) * MAX_COL + p2.x]->pos();
btn_pos2 = QPoint(btn_pos2.x(), btn_pos2.y() + kIconSize);
}
else
btn_pos2 = imageButton[p2.y * MAX_COL + p2.x]->pos();
QPoint pos1(btn_pos1.x() + kIconSize / 2, btn_pos1.y() + kIconSize / 2);
QPoint pos2(btn_pos2.x() + kIconSize / 2, btn_pos2.y() + kIconSize / 2);
painter.drawLine(pos1, pos2);
}
return true;
}
else
return QMainWindow::eventFilter(watched, event);
}
void MainGameWindow::gameTimerEvent()
{
// 进度条计时效果
if(ui->timeBar->value() == 0)
{
gameTimer->stop();
QMessageBox::information(this, "game over", "play again");
}
else
{
ui->timeBar->setValue(ui->timeBar->value() - kGameTimerInterval);
}
}
// 提示
void MainGameWindow::on_hintBtn_clicked()
{
// 初始时不能获得提示
for (int i = 0; i < 4;i++)
if (game->getHint()[i] == -1)
QMessageBox::information(this, "什么情况?", "开始就不会?");
return;
int srcX = game->getHint()[0];
int srcY = game->getHint()[1];
int dstX = game->getHint()[2];
int dstY = game->getHint()[3];
IconButton *srcIcon = imageButton[srcY * MAX_COL + srcX];
IconButton *dstIcon = imageButton[dstY * MAX_COL + dstX];
srcIcon->setStyleSheet(kIconHintStyle);
dstIcon->setStyleSheet(kIconHintStyle);
}
void MainGameWindow::on_robot_btn_clicked()
{
// 初始时不能自动玩
for (int i = 0; i < 4;i++)
if (game->getHint()[i] == -1)
QMessageBox::information(this, "别怂啊", "一来就投?");
return;
while (game->gameStatus == PLAYING)
{
QTimer t;
t.start(100);
// 连接生成提示
int srcX = game->getHint()[0];
int srcY = game->getHint()[1];
int dstX = game->getHint()[2];
int dstY = game->getHint()[3];
if(game->linkTwoTiles(srcX, srcY, dstX, dstY))
{
// 播放音效
QSound::play(":/res/sound/pair.wav");
// 消除成功,隐藏掉
IconButton *icon1 = imageButton[srcY * MAX_COL + srcX];
IconButton *icon2 = imageButton[dstY * MAX_COL + dstX];
icon1->hide();
icon2->hide();
game->paintPoints.clear();
// 重绘
update();
// 检查是否胜利
if (game->isWin())
QMessageBox::information(this, "great", "you win");
int *hints = game->getHint();
}
}
}
void MainGameWindow::createGameWithLevel()
{
// 先析构之前的
if (game)
{
delete game;
for (int i = 0;i < MAX_ROW * MAX_COL; i++)
{
if (imageButton[i])
delete imageButton[i];
}
}
// 停止音乐
audioPlayer->stop();
// 重绘
update();
QAction *actionSender = (QAction *)dynamic_cast<QAction *>(sender());
if (actionSender == ui->actionBasic)
{
initGame(BASIC);
}
else if (actionSender == ui->actionMedium)
{
initGame(MEDIUM);
}
else if (actionSender == ui->actionHard)
{
initGame(HARD);
}
}
二、main_game_window.h
#ifndef MAIN_GAME_WINDOW_H
#define MAIN_GAME_WINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QWidget>
#include <QTimer>
#include <QMediaPlayer>
#include "game_model.h"
#include <QLineEdit>
#include <QPushButton>
#include <QSqlDatabase>
#include <QErrorMessage>
namespace Ui {
class MainGameWindow;
}
// 继承自button,存储坐标值
struct IconButton : QPushButton
{
public:
IconButton(QWidget *parent = Q_NULLPTR) :
QPushButton(parent),
xID(-1),
yID(-1)
{
}
int xID; // x 坐标
int yID; // y 坐标
};
class MainGameWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainGameWindow(QWidget *parent = 0);
virtual ~MainGameWindow();
virtual bool eventFilter(QObject *watched, QEvent *event); // 事件过滤
private:
Ui::MainGameWindow *ui;
GameModel *game; // 模型
IconButton *imageButton[MAX_ROW * MAX_COL]; // 图片button数组
QTimer *gameTimer; // 计时器
IconButton *preIcon, *curIcon; // 记录点击的icon
bool isLinking; // 维持一个连接状态的标志
QMediaPlayer *audioPlayer; // 音乐播放器
void initGame(GameLevel level); // 初始化游戏
//开始界面
QWidget *www;
QSqlDatabase db;
QLineEdit *lb1,*lb2;
QPushButton *bt1,*bt2;
QErrorMessage *error;//报错信息框
QPushButton *bt_errmsg;//不布局出来
private slots:
void onIconButtonPressed(); // icon点击到响应
void gameTimerEvent(); // 游戏计时回调
void handleLinkEffect(); // 实现连接效果
void on_hintBtn_clicked(); // 提示按钮
void on_robot_btn_clicked(); // 自动完成
void createGameWithLevel(); // 选中难度开始
//开始界面
void show_widget();
void registered();//注册
void show_error();
//signals:
// void login_successed();
};
#endif // MAIN_GAME_WINDOW_H
三、 game_model.cpp
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <algorithm>
#include "game_model.h"
// 游戏逻辑模型,与界面分离
GameModel::GameModel() :
gameStatus(PLAYING),
gameLevel(BASIC)
{
}
GameModel::~GameModel()
{
if(gameMap)
{
free(gameMap);
free(hintArray);
gameMap = NULL;
}
}
void GameModel::startGame(GameLevel level)
{
// 初始化
gameMap = (int *)malloc(sizeof(int) * MAX_ROW * MAX_COL);
memset(gameMap, 0, MAX_ROW * MAX_COL);
for (int i = 0; i < MAX_ROW * MAX_COL; i++)
gameMap[i] = 0;
hintArray = (int *)malloc(sizeof(int) * 4);
memset(hintArray, 0, 4);
for (int i = 0; i < 4; i++)
hintArray[i] = -1;
gameStatus = PLAYING;
gameLevel = level;
int gameLevelNum;
switch (gameLevel)
{
case BASIC:
gameLevelNum = kBasicNum;
break;
case MEDIUM:
gameLevelNum = kMediumNum;
break;
case HARD:
gameLevelNum = kHardNum;
}
// 填充方块标号
int iconID = 0;
for(int i = 0; i < gameLevelNum; i += 2)
{
// 每次填充连着的两个,图片用尽了就循环
gameMap[i] = iconID % MAX_ICON + 1;
gameMap[i + 1] = iconID % MAX_ICON + 1;
iconID++;
}
// 打乱方块
srand((unsigned)time(0));
for(int i = 0; i < MAX_ROW * MAX_COL; i++)
{
int randomID = rand() % (MAX_ROW * MAX_COL);
std::swap(gameMap[i], gameMap[randomID]);
}
// 初始化判断模式
isFrozenMode = false;
// 初始化绘制点
paintPoints.clear();
}
// 重载
void GameModel::startGame()
{
startGame(gameLevel);
}
int *GameModel::getGameMap()
{
return gameMap;
}
bool GameModel::isFrozen()
{
//所有方块两两判断是否可以连接
// 每次消除后做一次判断
// 其实在这个过程中记录提示
for (int i = 0; i < MAX_ROW * MAX_COL - 1; i++)
for( int j = i + 1; j < MAX_ROW * MAX_COL; j++)
{
int srcX = i % MAX_COL;
int srcY = i / MAX_COL;
int dstX = j % MAX_COL;
int dstY = j / MAX_COL;
// 只要能找到可以连接的就不为僵局
isFrozenMode = true;
if (isCanLink(srcX, srcY, dstX, dstY))
{
// 记录第一个可以连接的hint
hintArray[0] = srcX;
hintArray[1] = srcY;
hintArray[2] = dstX;
hintArray[3] = dstY;
isFrozenMode = false;
return false;
}
}
isFrozenMode = false;
return true;
}
bool GameModel::isWin()
{
for (int i = 0; i < MAX_ROW * MAX_COL; i++)
{
if (gameMap[i])
return false;
}
gameStatus = WIN;
return true;
}
int *GameModel::getHint()
{
return hintArray;
}
// 最重要的判断连接算法
bool GameModel::canLinkDirectly(int srcX, int srcY, int dstX, int dstY)
{
// 竖线
if (srcX == dstX)
{
if (srcY > dstY)
std::swap(srcY, dstY);
for (int y = srcY + 1; y < dstY; y++)
if (gameMap[MAX_COL * y + srcX])
return false;
if (!isFrozenMode)
{
// 记录点和路线
PaintPoint p1(srcX, srcY), p2(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
}
return true;
}
// 横线
if (srcY == dstY)
{
if (srcX > dstX)
std::swap(srcX, dstX);
for (int x = srcX + 1; x < dstX; x++)
if (gameMap[MAX_COL * srcY + x])
return false;
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
}
return true;
}
return false;
}
bool GameModel::canLinkWithOneCorner(int srcX, int srcY, int dstX, int dstY)
{
if (srcX > dstX)
{
// 统一化,方便后续处理
std::swap(srcX, dstX);
std::swap(srcY, dstY);
}
// 先确定拐点,再确定直连线路,2种情况,4个点,每种情况逐个试,所以多个if顺序执行
if (dstY > srcY)
{
if (gameMap[srcY * MAX_COL + dstX] == 0)
{
// 右上角
if (canLinkDirectly(srcX, srcY, dstX, srcY) && canLinkDirectly(dstX, srcY, dstX, dstY))
{
// 只有连接模式才记录点
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(dstX, srcY), p3(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
}
return true;
}
}
if (gameMap[dstY * MAX_COL + srcX] == 0)
{
// 左下角
if (canLinkDirectly(srcX, srcY, srcX, dstY) && canLinkDirectly(srcX, dstY, dstX, dstY))
{
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(srcX, dstY), p3(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
}
return true;
}
}
}
else
{
if (gameMap[dstY * MAX_COL + srcX] == 0)
{
// 左上角
if (canLinkDirectly(srcX, srcY, srcX, dstY) && canLinkDirectly(srcX, dstY, dstX, dstY))
{
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(srcX, dstY), p3(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
}
return true;
}
}
if (gameMap[srcY * MAX_COL + dstX] == 0)
{
// 右下角
if (canLinkDirectly(srcX, srcY, dstX, srcY) && canLinkDirectly(dstX, srcY, dstX, dstY))
{
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(dstX, srcY), p3(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
}
return true;
}
}
}
return false;
}
bool GameModel::canLinkWithTwoCorner(int srcX, int srcY, int dstX, int dstY)
{
if (srcX > dstX)
{
// 统一化,方便后续处理
std::swap(srcX, dstX);
std::swap(srcY, dstY);
}
// 两种情况,横向垂线和竖向垂线,以src点作为基准遍历,双折线由直线和一个拐点的折线构成
// 常规情况
for (int y = 0; y < MAX_ROW; y++)
{
if (y != srcY && y != dstY)
{
if (gameMap[y * MAX_COL + srcX] == 0 && canLinkDirectly(srcX, srcY, srcX, y) && canLinkWithOneCorner(srcX, y, dstX, dstY))
{
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(srcX, y), p3(dstX, y), p4(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
paintPoints.push_back(p4);
}
return true;
}
}
}
for (int x = 0; x < MAX_COL; x++)
{
if (x != srcX && x != dstX)
{
if (gameMap[srcY * MAX_COL + x] == 0
&& canLinkDirectly(srcX, srcY, x, srcY)
&& canLinkWithOneCorner(x, srcY, dstX, dstY))
{
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(x, srcY), p3(x, dstY), p4(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
paintPoints.push_back(p4);
}
return true;
}
}
}
// 边缘情况,从外边缘连接,方块不一定在边缘,(分开写便于记录路径)
if ((srcX == 0 || gameMap[srcY * MAX_COL + 0] == 0 && canLinkDirectly(srcX, srcY, 0, srcY)) && (dstX == 0 || gameMap[dstY * MAX_COL + 0] == 0 && canLinkDirectly(0, dstY, dstX, dstY)))
{
// 左
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(-1, srcY), p3(-1, dstY), p4(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
paintPoints.push_back(p4);
}
return true;
}
if ((srcX == MAX_COL - 1 || gameMap[srcY * MAX_COL + MAX_COL - 1] == 0 && canLinkDirectly(srcX, srcY, MAX_COL - 1, srcY))
&& (dstX == MAX_COL - 1 || gameMap[dstY * MAX_COL + MAX_COL - 1] == 0 && canLinkDirectly(MAX_COL - 1, dstY, dstX, dstY)))
{
// 右
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(MAX_COL, srcY), p3(MAX_COL, dstY), p4(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
paintPoints.push_back(p4);
}
return true;
}
if ((srcY == 0 || gameMap[srcX] == 0 && canLinkDirectly(srcX, srcY, srcX, 0))
&& (dstY == 0 || gameMap[dstX] == 0 && canLinkDirectly(dstX, 0, dstX, dstY)))
{
// 上
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(srcX, -1), p3(dstX, -1), p4(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
paintPoints.push_back(p4);
}
return true;
}
if ((srcY == MAX_ROW - 1 || gameMap[(MAX_ROW - 1) * MAX_COL + srcX] == 0 && canLinkDirectly(srcX, srcY, srcX, MAX_ROW - 1)) && (dstY == MAX_ROW - 1 || gameMap[(MAX_ROW - 1) * MAX_COL + dstX] == 0 && canLinkDirectly(dstX, MAX_ROW - 1, dstX, dstY)))
{
// 下
if (!isFrozenMode)
{
PaintPoint p1(srcX, srcY), p2(srcX, MAX_ROW), p3(dstX, MAX_ROW), p4(dstX, dstY);
paintPoints.clear();
paintPoints.push_back(p1);
paintPoints.push_back(p2);
paintPoints.push_back(p3);
paintPoints.push_back(p4);
}
return true;
}
return false;
}
bool GameModel::isCanLink(int srcX, int srcY, int dstX, int dstY)
{
if (gameMap[srcY * MAX_COL + srcX] == 0 || gameMap[dstY * MAX_COL + dstX] == 0)
return false;
if (srcX == dstX && srcY == dstY)
return false;
if(gameMap[MAX_COL * srcY + srcX] != gameMap[MAX_COL * dstY + dstX])
return false;
//1:横向或者竖向可以直线连通
if (canLinkDirectly(srcX, srcY, dstX, dstY))
return true;
//2:一次拐弯可以连通
if (canLinkWithOneCorner(srcX, srcY, dstX, dstY))
return true;
// 3:两次拐弯可以连通
if (canLinkWithTwoCorner(srcX, srcY, dstX, dstY))
return true;
return false;
}
// 点击方块进行连接操作
bool GameModel::linkTwoTiles(int srcX, int srcY, int dstX, int dstY)
{
// 成功连接就返回true否则false
if(isCanLink(srcX, srcY, dstX, dstY))
{
//重置
gameMap[MAX_COL * srcY + srcX] = 0;
gameMap[MAX_COL * dstY + dstX] = 0;
return true;
}
return false;
}
四、game_model.h
#ifndef GAMEMODEL_H
#define GAMEMODEL_H
#include <vector>
// ------------ 全局变量 ------------ //
// 最大行和列数
const int MAX_ROW = 15;
const int MAX_COL = 20;
// 游戏可玩图片数量
const int MAX_ICON = 25;
// 游戏状态
enum GameStatus
{
PLAYING,
PAUSE,
WIN,
OVER
};
// 游戏难度,不同的方块数
const int kBasicNum = MAX_ROW * MAX_COL * 0.3;
const int kMediumNum = MAX_ROW * MAX_COL * 0.7;
const int kHardNum = MAX_ROW * MAX_COL;
enum GameLevel
{
BASIC,
MEDIUM,
HARD
};
// 用于绘制线段的连接点(其实应该统一用这个结构体的,代码有待优化)
struct PaintPoint
{
PaintPoint(int _x, int _y) : x(_x), y (_y) {}
int x;
int y;
};
// -------------------------------- //
class GameModel
{
public:
GameModel();
virtual ~GameModel();
public:
void startGame(); // 开始游戏
void startGame(GameLevel level);
int *getGameMap(); // 获得地图
GameStatus checkGameStatus(); // 判断获得游戏状态,是否输赢
bool linkTwoTiles(int srcX, int srcY, int dstX,int dstY); // 连接起点和终点方块,连接是否成功
bool isFrozen(); // 判断是否已经成为了僵局
bool isWin(); // 检查游戏是否结束
int *getHint(); // 获得提示
std::vector<PaintPoint> paintPoints; // 用于绘制的点
// 游戏状态和难度
GameStatus gameStatus;
GameLevel gameLevel;
private:
// 游戏地图,存储方块,0表示消失,1-其他数字表示图片标号
int *gameMap;
// 游戏提示,存储2个点
int *hintArray;
// 判断起点到终点的方块是否可以连接消除
bool isCanLink(int srcX, int srcY, int dstX, int dstY);
bool canLinkDirectly(int srcX, int srcY, int dstX, int dstY);
bool canLinkWithOneCorner(int srcX, int srcY, int dstX, int dstY);
bool canLinkWithTwoCorner(int srcX, int srcY, int dstX, int dstY);
// 提示模式还是连接模式判断
bool isFrozenMode;
};
#endif // GAMEMODEL_H
项目视频
源码
https://pan.baidu.com/s/1DtGi_3pbaIwcRmgJgbBjHA
提取码:8h9m