成品展示:
这里我放了百度网盘的链接,资源文件也在里面,可以自己点开查看,一共做了三天,我来总结一下这个小项目吧。
链接:https://pan.baidu.com/s/1vSZWPnNjJaMkXPGP_TEjGQ?pwd=rted
提取码:rted
--来自百度网盘超级会员V1的分享
一:对于主题页面的设置:
我们设置了这几个部分:标题上方文字,开始选择框,还有图片COINFlie这个图标,还有按钮图片,还有背景图;
标题设计:
//设置场景
//设计固定大小
setFixedSize(500,800);
//设置标题图标
setWindowIcon(QIcon("://res/Coin0001.png"));//标题背景图片
//设置标题
setWindowTitle("翻金币的主场景");
开始上方选择框:
//退出按钮的实现
connect(ui->actionquiit,&QAction::triggered,[=](){
this->close();
});
图片COINFlie这个图片设计和背景的设计,这里用到了Qpainter这个类,并且重写了paintEvent这个虚函数
void MainScene::paintEvent(QPaintEvent *)
{
//创建画家
QPainter painter(this);
QPixmap pix;
pix.load("://res/PlayLevelSceneBg.png");//加载背景图片
painter.drawPixmap(0,0,this->width(),this->height(),pix);
//画背景图标
pix.load("://res/Title.png");
painter.drawPixmap(0,0,pix);
}
最后就是按钮的设计了,这里我们创建了一个按钮类,用于和其他场景的一个互动,点击切换场景的效果,同时我们关联了槽函数,用于点击以后关闭本场景跳转到下一个场景去,这里我们隐藏自身的场景,显示关卡场景,并且用到了定时器去延时进入下一个关卡去。
//开始按钮
MyPushButton *starBtn=new MyPushButton("://res/MenuSceneStartButton.png");
starBtn->setParent(this);
starBtn->move(this->width()/2-starBtn->width()/2,this->height()*0.7);//放置按钮
//关联信号槽
connect(starBtn,&MyPushButton::clicked,[=](){
qDebug()<<"点击了start";//测试语句
starBtn->zoom1();
starBtn->zoom2();//按键动画效果
//延时进入到选择关卡中
QTimer::singleShot(300,this,[=](){
//设置一下chooseScense的位置
chooseScene->setGeometry(this->geometry());
//进入选择关卡的场景中
//将自身隐藏
this->hide();
//显示关卡场景
chooseScene->show();
});
});
二:选择关卡场景的设计:
这里我们创建一个选择关卡的类,然后去创建一个选择关卡的场景,在选择关卡的构造函数里面去构造场景,这里也有四个我们需要设计的东西:最上方的选择框和标题图片,上方图片COINFLie图片和20个选择按钮图标的设计,然后还有一个back的设计
最上方的选择框和标题图片:
//配置选择关卡的场景
this->setFixedSize(500,800);
//设置选择关卡图标
this->setWindowIcon(QPixmap("://res/Coin0001.png"));
//设置选择关卡的标题
//this->setWindowTitle("“江江专属”选择关卡场景");
this->setWindowTitle("选择关卡场景");
//创建选择关卡的菜单栏
QMenuBar *bar=menuBar();
setMenuBar(bar);
//创建选择关卡的开始菜单
QMenu *startMenu=bar->addMenu("开始");
//创建选择关卡的退出的菜单项
QAction *quitAction=startMenu->addAction("退出");
//点击实现退出
connect(quitAction,&QAction::triggered,this,[=](){
this->close();
})
对于back图片的设计(点击达到返回的效果),然后这里有个逻辑在里面需要解决一下,点击back按钮以后,放回上一个场景中去,这里我们使用的是信号的方式,发送emit信号,然后那边非阻塞等待信号的到来:
//返回按钮
MyPushButton *backBTn=new
MyPushButton("://res/BackButton.png","://res/BackButtonSelected.png");
backBTn->setParent(this);
//放在屏幕右下角
backBTn->move(this->width()-backBTn->width(),this->height()-backBTn->height());
//点击返回
connect(backBTn,&MyPushButton::clicked,this,[=](){
//播放返回按钮
backSound->play();
qDebug()<<"BACK按下了";
//告诉主场景我返回了,主场景监听一下ChooseLevelScene返回按钮
emit this->chooseScenceBack();//发送信号
});
主场景监听信号的到来:(如果信号来了,就将选择场景进行屏蔽,主场景显示出来 )
//监听选择关卡的返回按钮的信号
connect(chooseScene,&ChooseLevelScene::chooseScenceBack,this,[=](){
this->setGeometry(chooseScene->geometry());
chooseScene->hide();//选择场景隐藏
this->show();//返回主场景
});
背景图片COINFLie图片和20个选择按钮图标的设计(也是重写了paintEvent事件):
void ChooseLevelScene::paintEvent(QPaintEvent *)
{
QPainter painter(this);//定义一个画家类
QPixmap pix;
//pix.load("://res/20230617153524.jpg");
pix.load("://res/OtherSceneBg.png");
painter.drawPixmap(0,0,this->width(),this->height(),pix);//画背景
//加载标题
pix.load("://res/Title.png");
painter.drawPixmap((this->width()-pix.width())/2,30,pix.width(),pix.height(),pix );
}
20个选择关卡按钮图标的设计,按钮有按钮背景,按钮图片,按钮上方lable组成,这里我们通过一维数组显示二维图片的方法显示这个矩阵按钮:
创建20个按钮,每个按钮监听点击事件,如果点击,就关闭选择场景进入游戏场景,创建标签去显示关卡数字
//创建选择关卡的按钮
for (int i=0;i<20;i++)//一维数组显示二维图形
{
//创建20个按钮
MyPushButton *menuBtn=new MyPushButton("://res/LevelIcon.png");
menuBtn->setParent(this);
menuBtn->move(i%4*70+115,200+i/4*70);
//监听每个按钮的事件
connect(menuBtn,&MyPushButton::clicked,[=](){
//播放选择关卡的音效
chooseSound->play();
qDebug()<<"你选择的是关卡是:"<<i+1<<endl;
//进入游戏场景
this->hide();//关闭选择场景
play=new PlayScene(i+1);//创建对象
play->show();//显示游戏场景
//监听返回按钮
connect(play,&PlayScene::chooseScenceBack,[=](){
this->setGeometry(play->geometry());
this->show();
delete play;
play=nullptr;
});
});
//创建标签
QLabel *lable=new QLabel;
lable->setParent(this);
lable->setFixedSize(menuBtn->width(),menuBtn->height());
lable->setText(QString::number(i+1));
lable->move(i%4*70+115,200+i/4*70);
//设置label的文字对齐方式
lable->setAlignment(Qt::AlignCenter|Qt::AlignVCenter);
//设置鼠标进行穿透 51号属性
//否则就会覆盖PushButton
lable->setAttribute(Qt::WA_TransparentForMouseEvents);
}
三:游戏关卡场景的设计:
游戏场景相对复杂一些,这里对于游戏场景的设计,有几个部分,首先最上方图标和文字的显示,让后图片coinFile的显示,还有关卡level的显示,还有back按钮的显示,最重要的是中间的金币银币的显示,和翻转问题的显示,这里我们创建了一个类,play的游戏场景的一个类
首先对于最上方图标的显示和back的显示我就不用多说了吧,直接上代码,我们这里也有返回按钮同理在选择关卡的场景的时候我们也非阻塞等待了点击的事件:
/初始化游戏场景
//设计固定大小
this->setFixedSize(500,800);
//设置图标
this->setWindowIcon(QPixmap("://res/Coin0001.png"));
//设计标题
this->setWindowTitle("游戏场景");
//this->setWindowTitle("“江江专属”游戏场景");
//创建选择关卡的菜单栏
QMenuBar *bar=menuBar();
setMenuBar(bar);
//创建选择关卡的开始菜单
QMenu *startMenu=bar->addMenu("开始");
//创建选择关卡的退出的菜单项
QAction *quitAction=startMenu->addAction("退出");
//点击实现退出
connect(quitAction,&QAction::triggered,this,[=](){
this->close();
});
//返回按钮
MyPushButton *backBTn=new MyPushButton("://res/BackButton.png","://res/BackButtonSelected.png");
backBTn->setParent(this);
backBTn->move(this->width()-backBTn->width(),this->height()-backBTn->height());//放在屏幕右下角
//点击返回
connect(backBTn,&MyPushButton::clicked,this,[=](){
backSound->play();
qDebug()<<"BACK按下了";
//告诉主场景我返回了,主场景监听一下ChooseLevelScene返回按钮
emit this->chooseScenceBack();//发送信号
});
图片coinFile的显示和背景的显示,也是重写了事件:
//重写事件
void PlayScene::paintEvent(QPaintEvent *)
{
//创建背景
QPainter painter(this);
QPixmap pix;
//pix.load("://res/20230617153532.jpg");
pix.load("://res/PlayLevelSceneBg.png");
painter.drawPixmap(0,0,this->width(),this->height(),pix);
//加载标题
pix.load("://res/Title.png");
painter.drawPixmap(10,30,pix.width(),pix.height(),pix);
}
关卡level的显示:
//显示当前的关卡数
QLabel *lable=new QLabel;
lable->setParent(this);
QFont font;
font.setFamily("华文新魏");
font.setPointSize(20);
QString str1=QString("Level:%1").arg(this->levelIndex);
//字体设计标签里面去
lable->setFont(font);
lable->setText(str1);
lable->setGeometry(30,this->height()-50,120,50);
对于不同关卡的设计我们采用了这样的方法去设计(我们在另一个文件中定义二维数组,让后提取二维数组中的数字0或者1的数字):
首先我们获取二维数组的文件,自定义一个二维数组用于保存数据的二维数组:
dataConfig config;
//初始化每个关卡的二维数组
for(int i=0;i<4;i++)
{
for (int j=0;j<4;j++)
{
this->gameArr[i][j]=config.mData[this->levelIndex][i][j];
};
}
然后我们就要创建金币了,这里我们定义了一个MyCoin的类:
绘制金币后面的背景图片:
//绘制背景图片
QPixmap pix;
pix.load("://res/BoardNode(1).png");
QLabel *lable=new QLabel;
lable->setGeometry(0,0,pix.width(),pix.height());
lable->setPixmap(pix);
lable->setParent(this);
lable->move(130+i*50,300+j*50);
读取文件二维数组里的数据,1,表示金币,0,表示银币,在coin类的构造中,我们将图片的路径作为了传入的参数:
//金币银币的显示方法(读取数组数据中的1或者0)
QString str;
if(this->gameArr[i][j]==1)
{
//显示金币
str="://res/Coin0001.png";
}
else
{
//显示银币
str ="://res/Coin0008.png";
}
//创建金币,放在创建的背景图片上
MyCoin *coin=new MyCoin(str);//创建金币还是银币的对象
coin->setParent(this);
coin->move(135+i*50,304+j*50);
这样我们就可以显示不同关卡里的金币和银币了,但是这里的金币和银币,还是不能点击的,这里就需要设计点击动画了,动画的显示我们这里是不同显示图片来显示这个动画的:
//给金币属性赋值
coin->posX=i;
coin->posY=j;
coin->flag=this->gameArr[i][j];//1正面 0反面
//将金币放入自己写的二维数组里面去用于维护
coinBtn[i][j]=coin;
//点击金币翻转
connect(coin,&MyCoin::clicked,[=](){
coin->changeFlag();
this->gameArr[i][j]=this->gameArr[i][j]==0?1:0;//三木运算
//周围也需要翻转
//延时翻转
QTimer::singleShot(100,this,[=](){
if(coin->posX+1<=3)//周围右侧可以翻转的条件
{
coinBtn[coin->posX+1][coin->posY]->changeFlag();
this->gameArr[coin->posX+1][coin->posY]=this->gameArr[coin->posX+1][coin->posY]==0?1:0;//三木运算
}
if(coin->posX-1>=0) //周围左侧可以翻转的条件
{
coinBtn[coin->posX-1][coin->posY]->changeFlag();
this->gameArr[coin->posX-1][coin->posY]=this->gameArr[coin->posX-1][coin->posY]==0?1:0;//三木运算
}
if(coin->posY+1<=3)//周围上侧翻转条件
{
coinBtn[coin->posX][coin->posY+1]->changeFlag();
this->gameArr[coin->posX][coin->posY+1]=this->gameArr[coin->posX][coin->posY+1]==0?1:0;//三木运算
}
if(coin->posY-1>=0)//周围下侧翻转条件
{
coinBtn[coin->posX][coin->posY-1]->changeFlag();
this->gameArr[coin->posX][coin->posY-1]=this->gameArr[coin->posX][coin->posY-1]==0?1:0;//三木运算
}
判断胜利的逻辑,每次点击金币的翻转我们都判断一次是否胜利,为了胜利有乐趣,我们特意去设计了胜利后有图砸下来的方式去庆祝胜利,判断胜利就是我们反例法,遍历整个二维数组,只要还有一个是银币我们就给他改变标志位,这样我们就可以很方便的表示我们的胜负关系了,我们为了优化我们的项目,我们在胜利以后就不可以点击我们的金币了:
//判断胜利
for (int i=0;i<4;i++)
{
for (int j=0;j<4;j++)
{
if(coinBtn[i][j]->flag==false)//全部金币
{
this->isWin=false;//胜利
break;
}
}
}
if(this->isWin==true)
{
//QMessageBox::information(this,"胜利信息","用户关卡胜利!");
//胜利以后金币按钮不可以点击了
for (int i=0;i<4;i++)
{
for (int j=0;j<4;j++)
{
coinBtn[i][j]->isWin=true;
}
}
//将胜利的图片砸下来
QPropertyAnimation *animation=new QPropertyAnimation(winLabel,"geometry");
//设置时间间隔
animation ->setDuration(1000);
//设置开始位置
animation->setStartValue(QRect(winLabel->x()-114,winLabel->y(),winLabel->width(),winLabel->height()));
//设置结束位置
animation->setEndValue(QRect(winLabel->x()-114,winLabel->y()+228,winLabel->width(),winLabel->height()));
animation->setEasingCurve(QEasingCurve::OutBounce);
animation->start();
/************/
//QMessageBox::information(this,"通过成功","“江江”真聪明!");
}
至此我们的项目结束了,我来总结一下我们的这个游戏项目吧,我们创建了三个场景类,用槽函数信号的方式实现了场景的切换,利用文件读写二维数组的方式实现我们对不通关卡的游戏设计,该项目建立在4X4的二维数组下面进行的,点击以后对周围和自身金币的翻转,最后用反例法判断胜负的关系,大量使用信号槽和定时器对项目的实现。