QT实现塔防游戏

本文详细介绍了如何在游戏开发中实现传奇角色的升级和移除功能,包括通过SelectButton2类处理升级逻辑,与Gamescene和Legends类的交互,以及游戏的暂停、返回菜单、波数、金钱和血量的显示。此外,还提到了游戏胜利和失败状态的处理,通过Settlement和Settlement2类展示结算界面。在实现过程中遇到了逻辑矛盾和多线程问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在基本功能实现后,对游戏进行优化,主要有以下几部分

  1. 实现传奇的升级
  2. 实现传奇的移除
  3. 绘画出波数与血量
  4. 实现游戏的暂停与回到选关关卡
  5. 实现游戏的胜利与失败

1.实现传奇的升级

创建一个selectbutton2类

#ifndef SELECTBUTTON2_H
#define SELECTBUTTON2_H

#include <QPainter>
#include <QPoint>
#include <QSize>

#include"gamescene.h"
#include"legends.h"

class Gamescene;
class Legends;
class SelectButton2
{
public:
    SelectButton2();
    SelectButton2(QPoint pos,Gamescene *game,QString path);
    virtual ~SelectButton2();
    //绘画button
    void virtual draw(QPainter * painter)const;
       //移除button2
       void virtual getRemoved();
       //得到该button2的传奇
       Legends * getLegend();
       //设置该button2的传奇
       void setLegend(Legends * legend);
       //判断点击点是否在button2的内部
       bool containPos(QPoint pos);
       //得到button2的左上点
       QPoint getPos();
       //图片固定大小
       static const QSize myfixedSize;
protected:
       //指向游戏界面的指针
       Gamescene *mygame;
       //我的传奇
       Legends *mylegend;
       //坐标点
       QPoint mypos;
       //图片地址
       QString mypath;

};


#endif // SELECTBUTTON2_H

 与gamescene连接.h中加入

//移除button1
    void removeButton(SelectButton *button);
//可以购买
    bool canBuy();
    bool canBuy2();
    bool canBuy3();
 //selectbutton1列表
    QList<SelectButton*> myselectButtonList;

.cpp中加入

 //画出button
    foreach (const SelectButton*button ,myselectButtonList)
    {
        button->draw(&painter);
    }
//判断可以升级
bool Gamescene::canUpdate(int money)
{
    if(myMoney>=money)
    {
        return true;
    }
    return false;
}
//移除button1
void Gamescene::removeButton(SelectButton *button)
{
    Q_ASSERT(button);
    myselectButtonList.removeOne(button);
}

与legends连接

.h中加入

//传奇升级
    void levelUp();
    //得到传奇等级
    int getLevel();
    //得到传奇的攻击力
    int getDamage();
 //传奇升级的价格
    int myupdate1Money;

.cpp中加入

//重置伤害
void Legends::reSetDamage()
{
    Legend[mynum].mydamage*=2;
}
//得到重置后的伤害
//设置升级
void Legends::levelUp()
{
    mylevel++;
}
//得到等级
int Legends::getLevel()
{
    return mylevel;
}
//得到升级的金钱
int Legends:: getUpdate1Money()
{
    myupdate1Money=Legend[mynum].mymoney*2-100;
    std::cout<<myupdate1Money<<std::endl;
    return myupdate1Money;
}

连接完成后在selectbutton2.cpp中实现

#include "selectbutton2.h"

const QSize SelectButton2::myfixedSize(90,90);
//构造函数
SelectButton2::SelectButton2(QPoint pos,Gamescene *game,QString path):
    mygame(game),
    mypos(pos),
    mypath(path)
{
}
//析构函数
SelectButton2::~SelectButton2()
{
    mygame=NULL;
    mylegend=NULL;
}
//得到坐标
QPoint SelectButton2::getPos()
{
    return mypos;
}
//移除该button
void SelectButton2::getRemoved()
{
    mygame->removeButton2(this);
    mygame->removeButton3(this);
}
//设置此处的传奇
void SelectButton2::setLegend(Legends *Legend)
{
    mylegend=Legend;
}
//绘画函数
void SelectButton2::draw(QPainter *painter) const
{
    painter->save();
    painter->drawPixmap(mypos.x(),mypos.y(),myfixedSize.width(),myfixedSize.height(),mypath);
    painter->restore();
}
//存在选择框
bool SelectButton2::containPos(QPoint pos)
{
    bool xInHere=(pos.x()>mypos.x() && pos.x()<mypos.x()+myfixedSize.width());
    bool yInHere=(pos.y()>mypos.y() && pos.y()<mypos.y()+myfixedSize.height());
    return xInHere && yInHere;
}

最后在gamescene中改写鼠标事件函数即可,与下一步移除同时进行

2.实现传奇的移除

sellectbutton2已经全部展现,将其与gamescene连接

.h 
//selectButton3列表
    QList<SelectButton2 *> myselectButton3List;
 //移除传奇
    void removeLegend(Legends*legend);
 //移除button3
    void removeButton3(SelectButton2 *button3);

///.cpp
//移除button3
void Gamescene::removeButton3(SelectButton2 *button3)
{
     Q_ASSERT(button3);
    myselectButton3List.removeOne(button3);
}
//移除传奇
void Gamescene::removeLegend(Legends *legend)
{
     Q_ASSERT(legend);
    mylegendlist.removeOne(legend);
}

与legends连接

//.h
//移除传奇
    void getRemoved();
//.cpp
//移除传奇
void Legends::getRemoved()
{
    //这里要判断是不是空指针NULL,防止子弹错误
    if(getAttackedEnemy()!=NULL)
    {
        //移除后,敌人被该防御塔丢失视野
        getAttackedEnemy()->getLostSight(this);
    }
    mygame->removeLegend(this);
}

最后将1,2两部结合,改写gamescene函数

//鼠标事件,点击生成传奇
void Gamescene::mousePressEvent(QMouseEvent *event)
{
    //获取点击的坐标
    QPoint presspos=event->pos();
    //如果点击暂停
    if(hasPresspos(presspos,QPoint(1560,0),QSize(90,90))&&Qt::LeftButton==event->button())
    {   if(flag1/2==0)
        {
            flag1++;
            pause();
            cout<<flag1<<endl;
        }
        else {
            timer->start(30);
            flag1--;

        }
    }
    //如果点击菜单
    if(hasPresspos(presspos,QPoint(1710,0),QSize(100,100))&&Qt::LeftButton==event->button())
    {   if(flag2/2==0)
        {
            flag2++;
            pause();
        }
    }
    if(hasPresspos(presspos,QPoint(1690,120),QSize(175,60))&&Qt::LeftButton==event->button()&&flag2/2!=0)
    {
        timer->start(30);
        flag2--;
    }
    else if(hasPresspos(presspos,QPoint(1690,185),QSize(175,60))&&Qt::LeftButton==event->button())
    {
        choose *a=new choose;
        this->hide();
        a->show();
    }
    //创建传奇列表
    auto legend_locationList=mylegend_loclist.begin();
    //    遍历所有传奇格子
    while(legend_locationList!=mylegend_loclist.end())//遍历所有的防御塔坑
        {
        //如果是鼠标左键点击,可以实现传奇的建立,或者是在有button2时进行升级或者移除
            if(Qt::LeftButton==event->button())
            {
                //如果没有button,点击的点在防御塔坑的内部
                if(legend_locationList->containPos(presspos) &&!legend_locationList->hasButton() &&  !legend_locationList->hasLegend()&&!legend_locationList->hasButton2())
                {
                    //得到该防御塔坑处的button的左上点
                    QPoint tmp(legend_locationList->getPos().x(),legend_locationList->getPos().y());
                    //创建一个button
                    SelectButton * button=new SelectButton(tmp,this);
                    //设置该位置有button
                    legend_locationList->setHasButton(true);
                    //设置该位置的button
                    legend_locationList->setButton(button);
                    //把这个button加入到gamescene中
                    myselectButtonList.push_back(button);
                    update();
                    break;
                }
                //判断是否有button2/3,没有button1,点击点不在传奇内,存在传奇
                else if(legend_locationList->hasButton2() && !legend_locationList->hasButton() && !legend_locationList->containPos(presspos) &&legend_locationList->hasLegend())
                {
                    //在有button2的情况下,点击button2的内部
                    if(hasPresspos(presspos,legend_locationList->getButton2()->getPos(),legend_locationList->getButton2()->myfixedSize))
                    {
                        //对防御塔进行升级
                        //金币足以升级,传奇没有升级过,该位置有传奇
                        if(canUpdate(legend_locationList->getLegends()->getUpdate1Money()) && !legend_locationList->hasUpdate1() && legend_locationList->hasLegend())
                        {
                            //设置已经升级
                            legend_locationList->setHasUpdate1(true);
                            //扣除金币
                            myMoney-=legend_locationList->getLegends()->getUpdate1Money();
                            //重置伤害
                            legend_locationList->getLegends()->reSetDamage();
                            //记录传奇升级
                            legend_locationList->getLegends()->levelUp();
                        }
                    }
                    //点击点在button3内,对防御塔进行移除
                    else if(hasPresspos(presspos,legend_locationList->getButton3()->getPos(),legend_locationList->getButton3()->myfixedSize))
                    {
                        //移除防御塔奖励200
                        awardMoney();
                        //这个移除传奇
                        legend_locationList->getLegends()->getRemoved();
                        //设置移除防御塔带来的其他变化
                        legend_locationList->setRemoveLegend();
                    }
                    //点击了button2/3的内部,button2/3就要被移除
                    legend_locationList->getButton2()->getRemoved();
                    legend_locationList->setButton2(NULL);
                    legend_locationList->setHasButton2(false);
                    legend_locationList->getButton3()->getRemoved();
                    legend_locationList->setButton3(NULL);
                    legend_locationList->setHasButton3(false);
                    update();
                    break;
                }
                //如果这个位置有button,没有防御塔,开始创建传奇
                else if(legend_locationList->hasButton() && !legend_locationList->hasLegend())
                {
                    //如果鼠标点击的地方在第一张图片内,创造第一个传奇
                    if(presspos.x()<legend_locationList->getButton()->getPos().x()+120 && canBuy())
                    {
                        legend_locationList->setHasLegend1(true);
                        Legends * legend=new Legends(legend_locationList->getCenterPos(),this,1);
                        myMoney-=legend->getMoney();
                        legend_locationList->setLegend(legend);
                        mylegendlist.push_back(legend);
                    }
                    //鼠标点击点在第二张图片内,创建第二种传奇
                    else if(presspos.x()>legend_locationList->getButton()->getPos().x()+125
                            && presspos.x()<legend_locationList->getButton()->getPos().x()+245
                            && canBuy2())
                    {
                        legend_locationList->setHasLegend2(true);
                        Legends * legend=new Legends(legend_locationList->getCenterPos(),this,2);
                        myMoney-=legend->getMoney();
                        legend_locationList->setLegend(legend);
                        mylegendlist.push_back(legend);
                    }
                    //鼠标点击点在第二张图片内,创建第三种传奇
                    else if(presspos.x()>legend_locationList->getButton()->getPos().x()+250 && presspos.x()<legend_locationList->getButton()->getPos().x()+375
                            && canBuy3())
                    {
                        legend_locationList->setHasLegend3(true);
                        Legends * legend=new Legends(legend_locationList->getCenterPos(),this,3);
                        myMoney-=legend->getMoney();
                        legend_locationList->setLegend(legend);
                        mylegendlist.push_back(legend);
                    }
                    //构造完防御塔后,对该button进行移除
                    legend_locationList->getButton()->getRemoved();
                    legend_locationList->setButton(NULL);
                    legend_locationList->setHasButton(false);
                    update();
                    break;
                }
            }
            //如果是鼠标右键点击
            else if(Qt::RightButton==event->button())
            {
                //在有防御塔的情况下,右键会出现选择框,升级还有移除
                if(legend_locationList->containPos(presspos) && (!legend_locationList->hasButton2()) && legend_locationList->hasLegend())
                {

                    legend_locationList->setHasButton2(true);
                    legend_locationList->setHasButton3(true);
                    //创建button2
                    QPoint tmp1(legend_locationList->getPos().x()+10,legend_locationList->getPos().y()-90);
                    SelectButton2 * button2=new SelectButton2(tmp1,this,":/image/LevelUp.png");
                    myselectButton2List.push_back(button2);
                    legend_locationList->setButton2(button2);
                    //创建button2
                    QPoint tmp2(legend_locationList->getPos().x()+15,legend_locationList->getPos().y()+130);
                    SelectButton2 * button3=new SelectButton2(tmp2,this,":/image/remove.png");
                    myselectButton3List.push_back(button3);
                    legend_locationList->setButton3(button3);
                    button2->setLegend(legend_locationList->getLegends());
                    update();
                    break;
                }

            }
            ++ legend_locationList;
        }
}

对了,gamescene中增加了判断点击点的函数

//判断点击点是否在图片内
bool Gamescene:: hasPresspos(QPoint pos_a,QPoint pos_b,const QSize size_c)
{
    int xx=pos_a.x()-pos_b.x();
    int yy=pos_a.y()-pos_b.y();
    if(xx>0&&xx<size_c.width()&&yy>0&&yy<size_c.height())
    {
        return true;
    }
    return false;
}

以上便是实现传奇升级和移除的函数

3.绘画出波数,金钱与血量

为了便于QT绘画,要将int变成string

创建一个递归函数

/递归函数获取字符串便于打印
QString Gamescene:: getMoneyPath(QString path,int money)
{
    if(money/10!=0)
    {
        path+='0'+(money%10);
        return getMoneyPath(path,money/10);
    }
    return (path+='0'+money);
}

 在paintevent函数中实现,用了reverse函数调转方向

//画出金钱数量
    QColor color (255,255,255);
    QFont font1("宋体",45,QFont::Bold);
    painter.setPen(color);
    painter.setFont(font1);
    QString moneyPath;
    moneyPath=getMoneyPath(moneyPath,myMoney);
    reverse(moneyPath.begin(),moneyPath.end());
    painter.drawText(180,82,moneyPath);
    //画出波数
    QString wavePath="0";
    wavePath+='0'+myWave+1;
    wavePath+="/06波怪物";
    painter.drawText(730,82,wavePath);
 //画出生命
    QFont font2("宋体",20,QFont::Bold);
    painter.setFont(font2);
    QString lifePath;
    lifePath=getMoneyPath(lifePath,myHomeHp);
    reverse(lifePath.begin(),lifePath.end());
    painter.drawText(1800,355,lifePath);

 实现以上效果

4.实现暂停与返回菜单

创建游戏终止函数

//   游戏暂停
void Gamescene:: pause()
{
    updateMap();
    timer->stop();
}

利用flag实现暂停与继续,在鼠标事件函数实现

//如果点击暂停
    if(hasPresspos(presspos,QPoint(1560,0),QSize(90,90))&&Qt::LeftButton==event->button())
    {   if(flag1/2==0)
        {
            flag1++;
            pause();
            cout<<flag1<<endl;
        }
        else {
            timer->start(30);
            flag1--;

        }
    }
    //如果点击菜单
    if(hasPresspos(presspos,QPoint(1710,0),QSize(100,100))&&Qt::LeftButton==event->button())
    {   if(flag2/2==0)
        {
            flag2++;
            pause();
        }
    }
    if(hasPresspos(presspos,QPoint(1690,120),QSize(175,60))&&Qt::LeftButton==event->button()&&flag2/2!=0)
    {
        timer->start(30);
        flag2--;
    }
    else if(hasPresspos(presspos,QPoint(1690,185),QSize(175,60))&&Qt::LeftButton==event->button())
    {
        choose *a=new choose;
        this->hide();
        a->show();
    }

绘画函数中实现可视化

//画出暂停
    QString pausePath=":/image/pause.jpg";
    if(flag1==1)
    painter.drawPixmap(1550,0,100,100,pausePath);
//画出菜单
    QString list1Path=":/image/LIST1.jpg";
    QString list2Path=":/image/LIST2.jpg";
    if(flag2==2)
    {
        painter.drawPixmap(1690,120,175,60,list1Path);
        painter.drawPixmap(1690,185,175,60,list2Path);
    }

动态效果不再展示

5.实现游戏的胜利与失败

创建settlement与settlement2类分别表现进行与失败

///.h
#ifndef SETTLEMENT_H
#define SETTLEMENT_H

#include <QWidget>

#include"gamescene.h"
#include"choose.h"

class Gamescene;
class choose;
class Settlement : public QWidget
{
    Q_OBJECT
public:
    explicit Settlement(QWidget *parent = nullptr);
    void paintEvent(QPaintEvent *event) ;
    void mousePressEvent(QMouseEvent *event) ;
signals:

};

#endif // SETTLEMENT_H
.cpp
#include "settlement.h"

Settlement::Settlement(QWidget *parent) : QWidget(parent)
{
    this->setWindowTitle("APEX");
    //    设置窗口大小
    this->setFixedSize(1920,1080);
    //     设置图标
    this->setWindowIcon(QIcon("://cal.ico.png"));
}
void Settlement::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QString path=":/image/win.jpg";
    painter.drawPixmap(0,0,1920,1080,path);
}
void Settlement::mousePressEvent(QMouseEvent *event)
{
    if(Qt::LeftButton==event->button())
    {
        choose *a=new choose;
        this->hide();
        a->show();
    }
}
///.h
#ifndef SETTLEMENT2_H
#define SETTLEMENT2_H

#include <QWidget>

#include"gamescene.h"
#include"choose.h"

class Gamescene;
class choose;
class Settlement2 : public QWidget
{
    Q_OBJECT
public:
    explicit Settlement2(QWidget *parent = nullptr);
    void paintEvent(QPaintEvent *event) ;
    void mousePressEvent(QMouseEvent *event) ;
signals:

};

#endif // SETTLEMENT2_H
///.cpp
#include "settlement2.h"

Settlement2::Settlement2(QWidget *parent) : QWidget(parent)
{
    this->setWindowTitle("APEX");
    //    设置窗口大小
    this->setFixedSize(1920,1080);
    //     设置图标
    this->setWindowIcon(QIcon("://cal.ico.png"));
}
void Settlement2::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QString path=":/image/lose.jpg";
    painter.drawPixmap(0,0,1920,1080,path);
}

void Settlement2::mousePressEvent(QMouseEvent *event)
{
    if(Qt::LeftButton==event->button())
    {
        choose *a=new choose;
        this->hide();
        a->show();
    }
}

在gamescene中实现

void Gamescene::getHpDamaged()
{
    //敌人进入基地,扣一滴血
    myHomeHp-=1;
    //如果血量空了,游戏失败
    if(myHomeHp<=0)
    {
        timer->stop();
        Settlement2 *b=new Settlement2;
        b->show();
        this->hide();
    }

}

分别实现了失败与成功的结算界面

总结:游戏功能基本实现,总结一下遇到的问题

1.鼠标点击函数之间的逻辑矛盾,导致最开始功能无法实现

2.多线程问题,在关闭程序之前没有结束updatemap,导致出现很多次失败界面

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值