Qt之塔防游戏 c++(一)

话不多说,我们直接进入正题吧。

这个阶段我们要完成如下功能:
1:图片的绘制
2:敌人运动轨迹的绘制
3:防御塔坑(可放置防御塔点)的绘制
4:鼠标点击事件,实现防御塔的出现

图片的绘制

首先我们在Qt中,创建一个widget工程
Qt之塔防游戏 c++(一)得到这个工程后,我们在mainwindow.h中做如下添加:

//类外
#include <QPaintEvent>

//类内
protected:
void paintEvent(QPaintEvent*);

添加Qt Resource File文件
Qt之塔防游戏 c++(一)
把要用到的图片都放到这个文件夹中。

然后进入mainwindow.cpp,做如下添加:

void Mainwindow::paintEvent(QPaintEvent*)
{
    QPainter painter(this);
    QString path(":/images/background1.jpg");//path是你图片的路径

    painter.drawPixmap(0,0,750,375,path);
    //drawPixmap的前四个参数代表的分别是,图片左上角的横坐标,图片左上角的纵坐标,图片的width,图片的height。我们一般把width和height,与图片的真实大小匹配起来
}

运行上述程序,就可以得到下面的结果啦!
Qt之塔防游戏 c++(一)
#敌人运动轨迹的绘制
我们添加一个类wayPoint(航点)
wayPoint.h的实现:

#ifndef WAYPOINT_H
#define WAYPOINT_H

#include <QPoint>
#include <QPainter>

class wayPoint
{
public:
    wayPoint(QPoint pos);
    void setNextWayPoint(wayPoint * nextWayPoint);//设置下一个航点
    wayPoint * getNextWayPoint();//得到下一个航点的指针
    const QPoint getPos();//得到本航点的中心点
    void draw(QPainter * painter) const;//绘画类函数
private:
    QPoint m_pos;//航点的中心点
    wayPoint * m_nextWayPoint;//下一个航点的指针
};
#endif // WAYPOINT_H

wayPoint.cpp中的实现:

#include "waypoint.h"

#include <QPoint>
#include <QPainter>

wayPoint::wayPoint(QPoint pos):
    m_pos(pos),
    m_nextWayPoint(NULL)
{
}

void wayPoint::setNextWayPoint(wayPoint *nextWayPoint)
{
    this->m_nextWayPoint=nextWayPoint;
}

wayPoint * wayPoint::getNextWayPoint()
{
    return this->m_nextWayPoint;
}

const QPoint wayPoint::getPos()
{
    return this->m_pos;
}

void wayPoint::draw(QPainter * painter) const
{
    painter->save();//保存原始的绘画参数
    painter->setPen(Qt::green);//设置画笔的颜色
    painter->drawEllipse(m_pos,4,4);//画一个半径为4的圆
    //注意,图片的大小单位是像素
    painter->drawEllipse(m_pos,1,1);//半径为1的圆
    if(m_nextWayPoint)//如果存在下一个航点,就把这两个航点连接起来
    {
        painter->drawLine(m_pos,m_nextWayPoint->getPos());//painter内的画直线的方法
    }
    painter->restore();//还原原来的画笔设置
}

完成wayPoint类的创建后,我们在mainwindow(程序运行得到的窗口)中把这些航点画出来。
先在mainwindow.h中添加:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPaintEvent>

#include "waypoint.h"//我们一般引用系统内文件时,用<>,引用我们自己定义的文件时用""

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class wayPoint;//新增对wayPoint类的说明

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    
    void addWayPoint1();//用来添加航点的函数
    
protected:
    void paintEvent(QPaintEvent*);//画家类函数
    
private:
    Ui::MainWindow *ui;
    QList<wayPoint * > m_wayPointList;//用来储存航点的list
};
#endif // MAINWINDOW_H

mainwindow.cpp中的实现:

//增加头文件的引用
#include "waypoint.h"

//函数的实现
void MainWindow::addWayPoint1()
{
     wayPoint * waypoint1=new wayPoint(QPoint(79,6));
    m_wayPointList.push_back(waypoint1);

    wayPoint * waypoint2=new wayPoint(QPoint(79,55));
    waypoint1->setNextWayPoint(waypoint2);
    m_wayPointList.push_back(waypoint2);

    wayPoint * waypoint3=new wayPoint(QPoint(407,55));
    waypoint2->setNextWayPoint(waypoint3);
    m_wayPointList.push_back(waypoint3);

    wayPoint * waypoint4=new wayPoint(QPoint(407,175));
    waypoint3->setNextWayPoint(waypoint4);
    m_wayPointList.push_back(waypoint4);

    wayPoint * waypoint5=new wayPoint(QPoint(83,175));
    waypoint4->setNextWayPoint(waypoint5);
    m_wayPointList.push_back(waypoint5);

    wayPoint * waypoint6=new wayPoint(QPoint(83,292));
    waypoint5->setNextWayPoint(waypoint6);
    m_wayPointList.push_back(waypoint6);

    wayPoint * waypoint7=new wayPoint(QPoint(473,292));
    waypoint6->setNextWayPoint(waypoint7);
    m_wayPointList.push_back(waypoint7);
}
航点的绘制要比较有耐心
游戏世界的构造是以左上角为原点的,以像素为单位
制作者需要自己不断修改,在图上找到这些航点,调整,找到航点的确切位置
下面的具体数据,是适用于我使用的地图的
如果制作者要用其他的背景地图,需要自己修改数据,找到航点

我们再到mainwindow.cpppaintEvent()方法中添加航点的绘画的实现:

foreach(const wayPoint * waypoint,m_wayPointList)
        waypoint->draw(&painter);

再到mainwindow.cpp中mainwindow的构造函数中,添加addWayPoint1()的调用:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    addWayPoint1();//增加航点函数的调用
}

运行程序,我们就可以得到下面的结果啦!
Qt之塔防游戏 c++(一)
可以看到,图上已经有敌人运动的轨迹了。

好啦,接下来我们进行防御塔坑的绘制。

防御塔坑的绘制

先添加 TowerPosition
TowerPosition.h中的实现:

#ifndef TOWERPOSITION_H
#define TOWERPOSITION_H

#include <QSize>
#include <QPainter>
#include <QString>

class TowerPosition
{
public:
    TowerPosition(QPoint pos,QString path=(":/images/open_spot.png"));//图片的路径
    QPoint getCenterPos();//得到防御塔坑的中心点
    QPoint getPos();//得到防御塔坑的左上点
    
    bool ContainPos(QPoint pos);//判断pos点是否在防御塔坑的范围内
    
    void draw(QPainter * painter) const;

    bool hasTower();//判断该防御塔坑内有没有防御塔
    void setHasTower(bool hasTower=true);//设置是否有防御塔

 private:
    QPoint m_pos;
    QString m_path;
    
    bool m_hasTower;
    static const QSize m_fixedSize;//防御塔坑的固定大小
};
#endif // TOWERPOSITION_H

TowerPosition.cpp的实现:

#include "towerposition.h"

#include <QSize>
#include <QPainter>
#include <QPixmap>

const QSize TowerPosition::m_fixedSize(35,35);//设置图片的大小

TowerPosition::TowerPosition(QPoint pos, QString path):
    m_pos(pos),
    m_path(path),
    m_hasTower(false)
{
}

bool TowerPosition::hasTower()
{
    return m_hasTower;
}

void TowerPosition::setHasTower(bool hasTower)
{
    m_hasTower=hasTower;
}

QPoint TowerPosition::getCenterPos()
{
    QPoint tmp;
    tmp.setX(m_pos.x()+m_fixedSize.width()/2);
    tmp.setY(m_pos.y()+m_fixedSize.height()/2);
    return tmp;
}

QPoint TowerPosition::getPos()
{
    return m_pos;
}

bool TowerPosition::ContainPos(QPoint pos)
{
    bool xInHere=pos.x()>m_pos.x() && pos.x()<m_pos.x()+m_fixedSize.width();
    bool yInHere=pos.y()>m_pos.y() && pos.y()<m_pos.y()+m_fixedSize.height();
    return xInHere && yInHere;
}

void TowerPosition::draw(QPainter *painter) const
{
    painter->drawPixmap(m_pos.x(),m_pos.y(),m_path);
}

然后到mainwindow.h中添加下面代码

//类外
#include <towerposition.h>

//类内
public:
void loadTowerPosition1();//用来加载防御塔坑的函数
private:
QList<TowerPosition > m_towerPositionList;//用来储存防御塔坑的list

mainwindow.cpp中对上面的代码进行实现:

#include"towerposition"

void MainWindow::loadTowerPosition1()
{
    //这里和找航点是一样的,制作者需要自己不断尝试
    //找到比较合适的防御塔坑点
    QPoint pos[]=
    {
        QPoint(86,98),
        QPoint(226,98),
        QPoint(439,98),
        QPoint(105,215),
        QPoint(186,215),
        QPoint(314,215),
        QPoint(105,321),
        QPoint(223,323),
        QPoint(365,319)
    };
    int len=sizeof(pos)/sizeof(pos[0]);
    for(int i=0;i<len;i++)
    {
        m_towerPositionList.push_back(pos[i]);
    }
}

同时,在paintEvent()函数内添加对防御塔坑的绘画:

foreach(const TowerPosition towerposition,m_towerPositionList)
       towerposition.draw(&painter);

并在mainwindow的构造函数中,添加对loadTowerPosition1()方法的调用,类似addWayPoint1()的调用:

    loadTowerPosition1();

运行上述程序,我们可以得到下面的界面:
Qt之塔防游戏 c++(一)可以看到,我们已经画出来了敌人的航点和防御塔坑
下面就实现防御塔的安置吧!

鼠标点击实现防御塔的出现

我们先创建一个 Tower
tower.h中的实现:

#ifndef TOWER_H
#define TOWER_H

#include <QObject>
#include <QPoint>
#include <QSize>
#include <QString>

#include "mainwindow.h"

class MainWindow;
class QPainter;

class Tower:QObject
{
    Q_OBJECT
public:
    Tower(QPoint pos,MainWindow * game,QString path=":/images/tower2.png");
    ~Tower();
    Tower();
    
    void draw(QPainter * painter)const;//画出防御塔
private:
    QPoint m_pos;//防御塔的中心点
    QString m_path;//防御塔图片的路径
    
    int m_attackRange;//攻击范围
    static const QSize m_fixedSize;//防御塔图片的固定大小
    MainWindow * m_game;//指向mainwindow的指针
};
#endif // TOWER_H

同时,tower.cpp中的方法实现:

#include "tower.h"
#include "mainwindow.h"

#include <QPoint>
#include <QPainter>
#include <QString>

const QSize Tower::m_fixedSize(35,35);
Tower::Tower()
{
}

Tower::~Tower()
{
}

Tower::Tower(QPoint pos,MainWindow * game,QString path):
    m_pos(pos),
    m_path(path),
    m_attackRange(70),//根据地图的大小,确定攻击范围
    m_game(game)
{
}

void Tower::draw(QPainter *painter) const
{
    painter->save();
    painter->setPen(Qt::green);
    painter->drawEllipse(m_pos,m_attackRange,m_attackRange);//画出防御塔的攻击范围
    painter->drawPixmap(m_pos.x()-m_fixedSize.width()/2,m_pos.y()-m_fixedSize.height()/2-10,path);//画出防御塔的图片
}

mainwindow.h中做如下添加:

//类外添加
#include <QMouseEvent>

#include "tower.h"

class Tower;

//类内添加
protected:
    void mousePressEvent(QMouseEvent *);//鼠标点击类函数

private:
    QList<Tower *> m_towerList;//用来储存防御塔的list

mainwindow.cpp中,对mousePressEvent()进行实现:

void MainWindow::mousePressEvent(QMouseEvent * event)
{
    QPoint pressPos=event->pos();//得到鼠标点击的位置
    auto it=m_towerPositionList.begin();
    while(it!=m_towerPositionList.end())//遍历所有的防御塔坑
    {
        if(Qt::LeftButton==event->button())//如果是鼠标左键点击
        {
            if(it->ContainPos(pressPos) && !it->hasTower())//如果鼠标点击的位置在防御塔坑的范围内,并且没有防御塔
            {
                Tower * tower=new Tower(it->getCenterPos(),this);//创建一个新的防御塔
                m_towerList.push_back(tower);//把这个防御塔放到储存防御塔的list中
                it->setHasTower(true);//设置这个防御塔坑内有防御塔了
                update();//更新地图
                break;//进行了一次操作,可以直接退出循环了
            }
        }
        ++it;
    }
}

并在paintEvent()函数中,添加对防御塔的绘画:

foreach(const Tower * tower, m_towerList)
        tower->draw(&painter);

运行程序后,我们就可以得到下面的画面了:
Qt之塔防游戏 c++(一)
可以看到,点击防御塔坑的内部后,防御塔就会出现。
这样我们第一阶段的工作就完成啦!

下面我们对这次的工作进行一次总结:
1:首先我们完成了背景地图的绘画,用到了QPainter头文件中的drawPixmap()函数,后续的一些绘画也多次用到了这个函数。
2:我们画出了敌人运动的航点,这个过程比较麻烦,需要制作者根据mainwindow上的实际情况,不断调整航点的位置
3:引入防御塔坑,为防御塔的出现做铺垫,我们后续的很多操作,主要是鼠标点击事件,都将会以TowerPosition为基础进行操作。
4:实现鼠标点击事件,完成防御塔的出现。

OK,我们下一篇文章见!

所有的源代码和图片资源,在下面的网盘内:
https://pan.baidu.com/s/1BS7tiiyCWQxgZDsm8QhMgA
提取码:9307

上一篇:c# – 保存之前预览PDF


下一篇:关于Typescript - HTMLElement上使用append / prepend函数的问题