最近在准备数学竞赛,一直没有完整的事件写代码,导致重拾时漏洞和错误都不少,这一部分主要完成敌人的创建,和使传奇能够攻击敌人
首先是要修改之前航点时漏下的错误,(这个错误后来花了很长时间寻找)
便是在列表中,每一个航点之间要相互连系
void Gamescene::addWayPoint1()
{
WayPoint *WayPoint1=new WayPoint(QPoint(238,225));
mynextWayPointlist.push_back(WayPoint1);
WayPoint *WayPoint2=new WayPoint(QPoint(238,698));
WayPoint1->setNextWayPoint(WayPoint2);
mynextWayPointlist.push_back(WayPoint2);
WayPoint *WayPoint3=new WayPoint(QPoint(720,698));
WayPoint2->setNextWayPoint(WayPoint3);
mynextWayPointlist.push_back(WayPoint3);
WayPoint *WayPoint4=new WayPoint(QPoint(720,548));
WayPoint3->setNextWayPoint(WayPoint4);
mynextWayPointlist.push_back(WayPoint4);
WayPoint *WayPoint5=new WayPoint(QPoint(1205,548));
WayPoint4->setNextWayPoint(WayPoint5);
mynextWayPointlist.push_back(WayPoint5);
WayPoint *WayPoint6=new WayPoint(QPoint(1205,698));
WayPoint5->setNextWayPoint(WayPoint6);
mynextWayPointlist.push_back(WayPoint6);
WayPoint *WayPoint7=new WayPoint(QPoint(1685,698));
WayPoint6->setNextWayPoint(WayPoint7);
mynextWayPointlist.push_back(WayPoint7);
WayPoint *WayPoint8=new WayPoint(QPoint(1685,265));
WayPoint7->setNextWayPoint(WayPoint8);
mynextWayPointlist.push_back(WayPoint8);
}
2.创建敌人类
#ifndef ENEMY_H
#define ENEMY_H
#include <QObject>
#include <QPoint>
#include <QString>
#include <QPainter>
#include <QSize>
#include "waypoint.h"
#include "gamescene.h"
#include "legends.h"
#include<cmath>
class Gamescene;
class Legends;
class QPainter;
class WayPoint;
class Enemy:public QObject
{
Q_OBJECT
public:
Enemy(WayPoint * startPoint, Gamescene * game,QString path=":/image/enemy1.png");
~Enemy();
//绘画函数
void virtual draw(QPainter * painter)const;
//控制敌人的移动
void move();
//得到敌人的当前位置
QPoint getPos();
//被tower攻击
void getAttacked(Legends * legend0);
//敌人被攻击受到伤害
void getDamaged(int damage);
//当血量非正时,敌人死亡,需要移除
void getRemoved();
//敌人脱离tower的攻击范围
void getLostSight(Legends * legend0);
//碰撞函数
bool ifCrash(QPoint p1,QPoint p2,int r1,int r2);
protected:
//最大血量
int mymaxHp;
//当前血量
int mycurrentHp;
//移动速度
int mywalkingSpeed;
//是否可以移动
bool myactive;
//目标航点的指针
WayPoint * mydestinationWayPoint;
//指向游戏的指针
Gamescene * mygame;
//当前位置
QPoint mypos;
//图片路径
QString mypath;
//正在攻击该敌人的防御塔list
QList<Legends*> myattacking_Legendlist;
// 图片固定大小
static const QSize myfixedSize;
public slots:
//私有信号槽,敌人是否可以移动
void doActive();
};
#endif // ENEMY_H
在enemy.cpp中实现
#include <QPainter>
#include <QPoint>
#include <QSize>
#include <QString>
#include <QVector2D>
#include<iostream>
#include "enemy.h"
//敌人图片大小
const QSize Enemy::myfixedSize(110,110);
Enemy::Enemy(WayPoint * startWayPoint,Gamescene *game,QString path):
QObject(0),
//指向游戏界面
mygame(game),
//初始坐标
mypos(startWayPoint->getPos()),
//图片路径
mypath(path)
{
//初始生命值
mymaxHp=60;
//当前生命值初始化
mycurrentHp=mymaxHp;
//移动速度初始化
mywalkingSpeed=10;
//移动状态初始化
myactive=false;
//下一个航点初始化
mydestinationWayPoint=startWayPoint->getNextWayPoint();
}
//析构后的善后
Enemy::~Enemy()
{
//清除在攻击该敌人的防御塔
myattacking_Legendlist.clear();
//全部设为空指针
mydestinationWayPoint=NULL;
mygame=NULL;
}
//判断是否碰撞
bool Enemy::ifCrash(QPoint p1,QPoint p2,int r1,int r2)
{
//点在r1,r2之间
int xx=p1.x()-p2.x();
int yy=p1.y()-p2.y();
int dis=sqrt(xx*xx+yy*yy);
if(dis<r1+r2)
{
return true;
}
return false;
}
//绘画函数
void Enemy::draw(QPainter *painter) const
{
//如果敌人不能移动,就不对它进行绘画
if(!myactive)
{
return ;
}
painter->save();
//设置血条的长度
QPoint healthBarPoint=mypos+QPoint(-myfixedSize.width()/2,-myfixedSize.height());
//设置血条的左上点
static const int healthBarWidth=myfixedSize.width();
//画笔的颜色
painter->setPen(Qt::NoPen);
//刷子的颜色,用来填充内部
painter->setBrush(Qt::red);
//画出矩形
QRect healthBarBackRect(healthBarPoint,QSize(healthBarWidth,2));
//画出血条
painter->drawRect(healthBarBackRect);
//画一个显示敌人当前血量的矩形来表现扣血
painter->setBrush(Qt::green);
//画出矩形
QRect healthBarRect(healthBarPoint,QSize((double)mycurrentHp/mymaxHp*healthBarWidth,2));
//画出当前血量血条
painter->drawRect(healthBarRect);
//画出敌人
//得到图片的左上点
QPoint tmp(mypos.x()-myfixedSize.width()/2,mypos.y()-myfixedSize.height()/2);
painter->drawPixmap(tmp.x(),tmp.y(),100,100,mypath);
//重置画笔
painter->restore();
}
//敌人移动状态设置
void Enemy::move()
{
//如果不能移动,就直接退出函数
if(!myactive)
{
return ;
}
//如果到达了目标航点
if(ifCrash(mypos,mydestinationWayPoint->getPos(),15,15))
{
//如果还存在下一个航点
if(mydestinationWayPoint->getNextWayPoint())
{
//重新设置敌人的位置,和目标航点
mypos=mydestinationWayPoint->getPos();
mydestinationWayPoint=mydestinationWayPoint->getNextWayPoint();
}
//如果没有下一个航点了,代表敌人到达基地
else
{
//基地扣血
mygame->getHpDamaged();
//移除这个敌人
mygame->removeEnemy(this);
return ;
}
}
//如果还在去目标航点的路上
else
{
//采用QVectoer2D中的两点移动方法
//获取目标航点
QPoint targetPoint=mydestinationWayPoint->getPos();
//定义移动速度
double movementSpeed=mywalkingSpeed;
QVector2D normailzed(targetPoint-mypos);
normailzed.normalize();
mypos=mypos+normailzed.toPoint()*movementSpeed;
}
}
//敌人是否可以移动
void Enemy::doActive()
{
myactive=true;
}
//获取坐标
QPoint Enemy::getPos()
{
return mypos;
}
//攻击敌人的传奇
void Enemy::getAttacked(Legends *legend0)
{
myattacking_Legendlist.push_back(legend0);
}
//敌人被攻击
void Enemy::getDamaged(int damage)
{
std::cout<<"getdamaged"<<std::endl;
//扣除血量
mycurrentHp-=damage;
//如果敌人没血,获取赏金并移除
if(mycurrentHp<=0)
{
mygame->awardMoney();
getRemoved();
}
}
//移除敌人的函数
void Enemy::getRemoved()
{
//判断
if(myattacking_Legendlist.empty())
{
return ;
}
else
{
foreach(Legends *legend,myattacking_Legendlist)
legend->targetKilled();
mygame->removeEnemy(this);
}
}
//失去了敌人视野
void Enemy::getLostSight(Legends *legend0)
{
myattacking_Legendlist.push_back(legend0);
}
3.与此同时,我们创建子弹类,从而使传奇可以攻击敌人
#ifndef BULLET_H
#define BULLET_H
#include <QObject>
#include <QPoint>
#include <QPainter>
#include <QString>
#include <QSize>
#include "enemy.h"
#include "legends.h"
#include "gamescene.h"
class Enemy;
class Gamescene;
class Tower;
class Bullet :public QObject
{
Q_OBJECT
//子弹动态移动
Q_PROPERTY(QPoint mycurrentPos READ getCurrentPos WRITE setCurrentPos)
public:
Bullet();
Bullet(QPoint startPos,QPoint targetPos,int damage,Enemy * targetEnemy,Gamescene * game,QString path=":/image/bullet.png");
// 得到子弹的当前位置
QPoint getCurrentPos();
// 设置子弹的当前位置
void setCurrentPos(QPoint pos);
// 子弹的移动
void move();
// 绘画子弹
void draw(QPainter * painter)const;
private slots:
// 击中敌人的时候触发信号
void hitTarget();
private:
// 子弹出发的坐标
QPoint mystartPos;
// 子弹的目标坐标
QPoint mytargetPos;
// 子弹现在的坐标
QPoint mycurrentPos;
// 子弹附带的伤害
int mydamage;
// 图片的路径
QString mypath;
// 子弹所指向的敌人
Enemy * mytargetEnemy;
// 指向游戏场景
Gamescene * mygame;
// 图片固定大小
static const QSize myfixedSize;
};
#endif // BULLET_H
.cpp文件中实现
#include "bullet.h"
#include "legends.h"
#include "gamescene.h"
#include "enemy.h"
#include <QPoint>
#include<iostream>
#include <QPainter>
#include <QString>
#include <QPropertyAnimation>
const QSize Bullet::myfixedSize(8,8);
Bullet::Bullet()
{
}
Bullet::Bullet(QPoint startPos,QPoint targetPos,int damage,Enemy * targetEnemy,Gamescene * game,QString path):
mystartPos(startPos),
mytargetPos(targetPos),
mydamage(damage),
mypath(path),
mytargetEnemy(targetEnemy),
mygame(game)
{
}
//获取当前坐标
QPoint Bullet::getCurrentPos()
{
return mycurrentPos;
}
//设定当前坐标
void Bullet::setCurrentPos(QPoint pos)
{
mycurrentPos=pos;
}
void Bullet::move()
{
std::cout<<"move"<<std::endl;
//子弹飞行的时间,经过150ms击中敌人
static int duration=150;
QPropertyAnimation * animation=new QPropertyAnimation(this,"mycurrentPos");
//设置持续时间
animation->setDuration(duration);
//设置起始坐标
animation->setStartValue(mystartPos);
//设置目标坐标
animation->setEndValue(mytargetPos);
//击中敌人后,子弹动态运动结束
connect(animation,SIGNAL(finished()),this,SLOT(hitTarget()));
animation->start();
}
void Bullet::hitTarget()
{
//如果mainwindow的敌人列表中,有子弹击中的这个敌人,该敌人受到相应的伤害
if(mygame->getEnemyList().indexOf(mytargetEnemy)!=-1)
{
mytargetEnemy->getDamaged(mydamage);
}
//击中敌人后子弹就要消失
mygame->removeBullet(this);
}
//
void Bullet::draw(QPainter *painter) const
{
painter->drawPixmap(mycurrentPos,mypath);
}
4.全部完成后在gamescene中实现子弹与敌人
#ifndef MAP_H
#define MAP_H
#include <QWidget>
#include<qicon.h>
#include<qlist.h>
#include<qstring.h>
#include<qpainter.h>
#include<qsize.h>
#include<qpixmap.h>
#include<QMouseEvent>
#include<qtimer.h>
#include<qobject.h>
#include"legends.h"
#include"legend_location.h"
#include"waypoint.h"
#include "enemy.h"
#include<bullet.h>
class Enemy;
class Bullet;
class Legends;
class Gamescene : public QWidget
{
Q_OBJECT
public:
explicit Gamescene(QWidget *parent = nullptr);
// 设置地图路径
void setPath(QString path);
// 添加新的航点
void addWayPoint1();
// 添加传奇坐标
void addLegend_location1();
// 点击生成传奇
void mousePressEvent(QMouseEvent *event) ;
// 绘图事件
void paintEvent(QPaintEvent *event) ;
//敌人进入基地内部,基地要扣血
void getHpDamaged();
//敌人死亡,奖励金钱
void awardMoney();
//敌人死亡,地图中移除
void removeEnemy(Enemy * enemy);
//得到Enemy*的list
QList<Enemy *> getEnemyList();
//加载敌人
bool loadWaves();
//添加子弹
void addBullet(Bullet * bullet);
//移除子弹
void removeBullet(Bullet * bullet);
//可以购买
bool canBuy();
//游戏结束
void gameOver();
private slots:
//显示地图
void updateMap();
//游戏开始
void gameStart();
private:
// 地图路径
QString mypath;
// 新航点的列表
QList<WayPoint*>mynextWayPointlist;
// 传奇坐标列表
QList<Legend_location>mylegend_loclist;
// 传奇类型列表
QList< Legends*> mylegendlist;
// 敌人列表
QList<Enemy *> myenemylist;
// 子弹列表
QList<Bullet *> mybulletList;
// 我的金钱
int myMoney;
// 我的基地血量
int myHomeHp;
// 敌人波数
int myWave;
// 胜利
bool myWin;
// 失败
bool myLose;
signals:
};
#endif // MAP_H
.cpp中实现
#include "gamescene.h"
#include<iostream>
using namespace std;
Gamescene::Gamescene(QWidget *parent) : QWidget(parent),myMoney(1000),myHomeHp(10)
,myWave(0),myWin(false),myLose(false)
{
this->setWindowTitle("APEX");
// 设置窗口大小
this->setFixedSize(1920,1080);
// 设置图标
this->setWindowIcon(QIcon("://cal.ico.png"));
// 创建定时器
QTimer *timer=new QTimer(this);
//每30毫秒刷新地图
connect(timer,SIGNAL(timeout()),this,SLOT(updateMap()));
timer->start(30);
//静态函数,300毫秒后开始游戏
QTimer::singleShot(300,this,SLOT(gameStart()));
}
//设置地图图片路径
void Gamescene::setPath(const QString path)
{
mypath=path;
}
//绘画界面
void Gamescene::paintEvent(QPaintEvent *)
{ QPainter painter(this);
QPixmap pix;
pix.load(mypath);
painter.drawPixmap(0,0,1920,1080,pix);
//画出航点
foreach( WayPoint * WayPoint0,mynextWayPointlist)
{
WayPoint0->draw(&painter);
}
//画出传奇框
foreach(const Legend_location location0,mylegend_loclist)
location0.draw(&painter);
//画出传奇
foreach(const Legends *legend0,mylegendlist)
{
legend0->draw(&painter);
}
// 画出敌人
foreach(const Enemy * enemy0,myenemylist)
{
enemy0->draw(&painter);
}
//画出子弹
foreach(const Bullet * bullet,mybulletList)
{
bullet->draw(&painter);
}
}
//鼠标事件,点击生成传奇
void Gamescene::mousePressEvent(QMouseEvent *event)
{
//获取点击的坐标
QPoint presspos=event->pos();
//创建传奇列表
auto legendlist0=mylegend_loclist.begin();
// 遍历所有传奇格子
while(legendlist0!=mylegend_loclist.end())
{
// 限制鼠标左键点击
if(Qt::LeftButton==event->button())
{
//如果鼠标点击的位置在格子内,并且没有传奇
if(legendlist0->containPos(presspos)&&!legendlist0->getExsistence())
{
//创建一个新传奇点
Legends * legend=new Legends(legendlist0->getCenterPos(),this);
//扣除相应钱财
myMoney-=30;
std::cout<<"deduck money"<<std::endl;
//把这个防御塔放到储存防御塔的list中
mylegendlist.push_back(legend);
//设置这个防御塔坑内有防御塔了
legendlist0->setExsistence(true);
//更新地图
update();
break;
}
}
legendlist0++;
}
}
//基地扣血函数
void Gamescene::getHpDamaged()
{
//敌人进入基地,扣一滴血
myHomeHp-=1;
}
//游戏失败
void Gamescene::gameOver()
{
if(!myLose)
{
myLose=true;
}
}
//击杀敌人获取奖金
void Gamescene::awardMoney()
{
待修改
//杀死一个敌人,奖励200,制作者可以自由更改数值
myMoney+=200;
}
//判定是否可以购买
bool Gamescene::canBuy()
{
if(myMoney>=30)
{
return true;
}
return false;
}
//从敌人列表中移除敌人
void Gamescene::removeEnemy(Enemy *enemy)
{
//如果enemy不存在就debug,防止错误
Q_ASSERT(enemy);
//死亡的敌人从enemylist中移除
myenemylist.removeOne(enemy);
//delete掉敌人
delete enemy;
// 如果这一波敌人全部消灭
if(myenemylist.empty())
{
// 新增下一波
++myWave;
//如果所有波数都清零
if(!loadWaves())
myWin=true;
}
}
//创建新的子弹
void Gamescene::addBullet(Bullet *bullet)
{
std::cout<<"Add bullet"<<std::endl;
//检查是否有这个子弹,避免错误
Q_ASSERT(bullet);
mybulletList.push_back(bullet);
}
//移除子弹
void Gamescene::removeBullet(Bullet *bullet)
{
//检查是否有这个子弹,避免错误
Q_ASSERT(bullet);
mybulletList.removeOne(bullet);
}
//获取敌人列表
QList<Enemy *> Gamescene::getEnemyList()
{
return myenemylist;
}
//控制敌人一波波出现
bool Gamescene::loadWaves()
{
// 如果大于6波游戏结束
if(myWave>=6)
{
return false;
}
int enemyStartTime[]={500,1500,2500};//敌人出现的时间
for(int i=0;i<2;i++)
{
// 创建开始航点
WayPoint * startWayPoint;
// 起点为航点列表的第一个
startWayPoint=mynextWayPointlist.first();
// 创建新的敌人
Enemy *enemy=new Enemy(startWayPoint,this);
// 将敌人放入列表
myenemylist.push_back(enemy);
// 静态函数,在每一波开始时调用一次
QTimer::singleShot(enemyStartTime[i],enemy,SLOT(doActive()));
}
cout<<"one wave"<<endl;
return true;
}
// 游戏开始
void Gamescene::gameStart()
{
// 开始加载怪物波数
loadWaves();
}
// 通过刷新屏幕使敌人移动
void Gamescene::updateMap()
{
foreach(Enemy *enemy,myenemylist)
{
enemy->move();
}
foreach(Legends *legend,mylegendlist)
{
legend->checkEnemyInRange();
}
update();
}
完成效果如下
总结:
这一部分主要完成了
- 建立敌人类
- 建立子弹类
- 使传奇能够攻击敌人
主要遇到的问题
- 列表功能没有实现完全,导致空指针错误
- 碰撞函数太理想化,精度太高,实际上无法完成
- debugger运用不熟练,导致找出错误困难