这次我们加入三种子弹,并实现多态。
我们这次不用外部加载资源了,我们可以使用代码自己创建一个图像资源。
和之前的类结构对比,由于飞机需要发射子弹,所以需要一个计时器,控制发射速度。
物体箱子类,用于存放所有的角色对象,并执行它们的函数。这是多态的体现。
子弹类其实和上次飞机类的道理一样。
这次重在理解飞机发射子弹的原理。
声明用于子弹的图片编号(全局变量)
extern int IMG_bullet;
extern int IMG_bullet_HS;//高速
extern int IMG_bullet_PT;//穿透
和上次飞机图片编号一样,在template.cpp再说明一下
int IMG_bullet;
int IMG_bullet_HS;
int IMG_bullet_PT;
使用CreateImageColor自己创建一个矩形图像,参数是图像的RGB以及透明度。
IMG_bullet = agk::CreateImageColor(255, 255, 255, 255);//白色
IMG_bullet_HS = agk::CreateImageColor(255, 48, 48, 255);//红火焰
IMG_bullet_PT = agk::CreateImageColor(0, 255, 255, 255);//亮蓝色
这里使用list用来存储继承Actor的所有对象。
这个类很重要,是类与类之间联系的桥梁。
#pragma once
#include"Actor.h"
#include
using namespace std;
class ActorBox
{
public:
ActorBox();
~ActorBox();
void Func();
void Add(Actor* actor);
void AutoRemove();//自动回收
void RemoveAll();
list<Actor*> actors;
};
AutoRemove是用于自动回收所有死亡的角色。会调用它们的析构函数删除。
这里可以学习for each这种比较方便的写法。
#include "ActorBox.h"
ActorBox::ActorBox()
{
actors = list<Actor*>();
}
ActorBox::~ActorBox()
{
RemoveAll();
}
void ActorBox::Func()
{
for each (Object* var in actors)
{
var->Func();
}
AutoRemove();
}
void ActorBox::Add(Actor* actor)
{
actors.push_back(actor);
}
void ActorBox::AutoRemove()
{
for each (Actor* var in actors)
{
if (var->GetIsDie())
{
actors.remove(var);
delete var;
break;
}
}
}
void ActorBox::RemoveAll()
{
actors.clear();
}
Bomb是用于爆炸的,会对产生碰撞的Actor造成伤害。
#include "Actor.h"
class Bullet :
public Actor
{
public:
Bullet(float _hurt,int team, float _hp, float _speed, float _x, float _y, int _imageID);
~Bullet();
void Bomb();
void Move();
void Func();
private:
float _hurt;
};
isDie是一个标记,值为1时,会删除自己。
Bomb函数之后有了敌人,我们再实现,目前子弹到了距离上边框10像素时会清除。
Move函数就是子弹往上飞行。
Func里调用Actor的Func函数,表示血量低于0时,也会死亡。
#include "Bullet.h"
#include"template.h"
Bullet::Bullet(float _hurt, int team, float _hp, float _speed, float _x, float _y, int _imageID):Actor(team,_hp,_speed,_x,_y,_imageID)
{
this->_hurt = _hurt;
}
Bullet::~Bullet()
{
Actor::~Actor();
}
void Bullet::Bomb()
{
if (_y < 10)
{
isDie = true;
}
//判断与异队的角色是否碰撞,如果碰撞,对该角色造成伤害。
}
void Bullet::Move()
{
_y = agk::GetSpriteY(_spriteID)-_speed;
agk::SetSpriteY(_spriteID, _y);
}
void Bullet::Func()
{
Actor::Func();
Move();
Bomb();
}
这三个具体的子弹类,只是参数有所不同,和之前的三个飞机类似。
#include "Bullet.h"
class Bullet_normal :
public Bullet
{
public:
Bullet_normal(float _x, float _y,int team);
~Bullet_normal();
};
个人感觉长方形的子弹更美观。
#include "Bullet_normal.h"
#include"template.h"
Bullet_normal::Bullet_normal(float _x, float _y, int team):Bullet(1,team,1,5,_x,_y,IMG_bullet)
{
agk::SetSpriteSize(_spriteID, 4, 8);
}
Bullet_normal::~Bullet_normal()
{
}
这里速度较高,关于赋值是否合理,我们之后再进行权衡。
#pragma once
#include "Bullet.h"
class Bullet_HS :
public Bullet
{
public:
Bullet_HS(float _x, float _y, int team);
~Bullet_HS();
};
#include "Bullet_HS.h"
#include"template.h"
Bullet_HS::Bullet_HS(float _x, float _y, int team) :Bullet(0.5, team, 1, 2, _x, _y, IMG_bullet_HS)
{
agk::SetSpriteSize(_spriteID, 2, 5);
}
Bullet_HS::~Bullet_HS()
{
}
这里血量较高,不容易直接爆炸。
#pragma once
#include "Bullet.h"
class Bullet_PT :
public Bullet
{
public:
Bullet_PT(float _x, float _y, int team);
~Bullet_PT();
};
#include "Bullet_PT.h"
#include"template.h"
Bullet_PT::Bullet_PT(float _x, float _y, int team) :Bullet(0.01, team, 100, 1, _x, _y, IMG_bullet_PT)
{
agk::SetSpriteSize(_spriteID, 4, 4);
}
Bullet_PT::~Bullet_PT()
{
}
构造函数用于给计时器设置时间,创建好计时器对象之后,就自动开始计时。
GetOk用于判断时间是否到了,时间到时或超时时会返回true。
ReStart用于重置时间,一般配合GetOk使用,以达到隔一段时间返回一次true的目的。
#pragma once
class Timer
{
public:
Timer(float _timelong);
~Timer();
bool GetOk();
void ReStart();
private:
float _timelong;
float _time1;
float _time2;
};
GetRunTime是AGK提供的获取程序运行时间的函数。
用time1与time2的差值进行计时。
#include "Timer.h"
#include"template.h"
Timer::Timer(float _timelong)
{
this->_timelong = _timelong;
_time2 = _time1 = agk::GetRunTime();
}
Timer::~Timer()
{
}
bool Timer::GetOk()
{
_time2 = agk::GetRunTime();
if (_time2 - _time1 >= _timelong)
{
return true;
}
return false;
}
void Timer::ReStart()
{
_time2 = _time1 = agk::GetRunTime();
}
由于下面这些类只写出较上次发生变化的部分,所以有些头文件,需要大家思考后自行包含。
Object.h的变化:
添加1个布尔变量用于标记是否死亡。
protected:
bool isDie;
};
添加两个方法,用于获取精灵编号和死亡标记。由于_spriteID和_imageID是protected的,所以外部不能直接使用。
int GetID();
bool GetIsDie();
Object.cpp的变化:
构造函数中给死亡标记赋初值为假。
Object::Object(int _imageID)
{
this->_imageID = _imageID;
_spriteID =agk::CreateSprite(_imageID);
isDie = false;
}
int Object::GetID()
{
return _spriteID;
}
bool Object::GetIsDie()
{
return isDie;
}
Actor.h的变化:
这里Func函数要实现一个功能,所以由纯虚函数改为虚函数。
增加两个函数,一个是获取队伍的。一个是去除血量的。
virtual void Func();
int GetTeam();
void RemoveHp(float hp);
Actor.cpp的变化:
在Func函数中,检测血量,如果血量小于等于0,置死亡标记为真。这是符合所有角色的。
void Actor::Func()
{
if (_hp <= 0)
{
isDie=true;
}
}
int Actor::GetTeam()
{
return team;
}
void Actor::RemoveHp(float hp)
{
this->_hp -= hp;
}
Plane.h的变化:
写了一个枚举类型,用于表示子弹的不同类型。
增加一个计时器变量,用于控制发射子弹的速度。
增加了一个子弹类型的枚举变量,用于控制发射子弹的类型。
#pragma once
#include "Actor.h"
#include"Timer.h"
#include"Bullet.h"
enum BulletType
{
NM,PT,HS
};
class Plane :
public Actor
{
public:
Plane(float _fireSpeed,int team, float _hp, float _speed, float _x, float _y, int _imageID);
~Plane();
void Attack();
void Move();
virtual void Func();
protected:
float _fireSpeed;
//子弹类型
Timer* _attack_time;
BulletType bullet;
};
Plane.cpp的变化:
构造函数中根据发射速度,初始化计时器对象。频率与周期互为倒数,初始化枚举类型的变量。
Plane::Plane(float _fireSpeed, int team, float _hp, float _speed, float _x, float _y, int _imageID):Actor(team,_hp,_speed,_x,_y,_imageID)
{
this->_fireSpeed = _fireSpeed;//频率
_attack_time = new Timer(1/_fireSpeed);//周期
bullet = BulletType::NM;
}
主要逻辑是用GetOk判断时间是否到了,若时间到了,就发射子弹,将子弹放入物体盒子,并重置计时器的时间。
根据枚举变量的值,生成相应的子弹并发射。
void Plane::Attack()
{
//根据射速发射子弹
if (_attack_time->GetOk())
{
Bullet *myBullet;
_x = agk::GetSpriteX(_spriteID) + agk::GetSpriteWidth(_spriteID) / 2;
_y = agk::GetSpriteY(_spriteID);
switch (bullet)
{
case BulletType::NM:
myBullet = new Bullet_normal(_x-2, _y, team);
break;
case BulletType::HS:
myBullet = new Bullet_HS(_x-1, _y, team);
break;
case BulletType::PT:
myBullet = new Bullet_PT(_x-2, _y, team);
break;
default:
myBullet = new Bullet_normal(_x-2, _y, team);
break;
}
actorBox->Add(myBullet);
_attack_time->ReStart();
}
}
给子弹类型赋BulletType::HS值,表示该飞机要发射HS型子弹。
F11::F11(float _x, float _y) :Plane(5, 1, 10, 2, _x, _y, IMG_f11)
{
agk::SetSpriteSize(_spriteID, 60, -1);
bullet = BulletType::HS;
}
给子弹类型赋BulletType::PT值,表示该飞机要发射PT型子弹。
RedStar::RedStar(float _x, float _y) :Plane(1, 1, 10, 2, _x, _y, IMG_redStar)
{
agk::SetSpriteSize(_spriteID, 60, -1);
bullet = BulletType::PT;
}
另一个飞机的子弹类型不赋值,则表示使用默认的子弹类型,即NM型子弹,
增加一个全局变量,用于储存所有的Actor,并执行它们的Func函数。
template.h的变化:
extern ActorBox *actorBox;
template.cpp的变化:
包含相应的头文件。
初始化全局变量actorBox,和之前的全局变量一样。
将飞机加入到物体盒子中。
最后一定记得删除actorBox指针。
// Includes
#include "template.h"
#include"TM.h"
#include"F11.h"
#include"RedStar.h"
#include"ActorBox.h"
#include"Bullet_normal.h"
// Namespace
using namespace AGK;
app App;
int IMG_f11;
int IMG_tm;
int IMG_redStar;
int IMG_bullet;
int IMG_bullet_HS;
int IMG_bullet_PT;
ActorBox *actorBox=new ActorBox();
void app::Begin(void)
{
//此处省略...
IMG_bullet = agk::CreateImageColor(255, 255, 255, 255);//白色
IMG_bullet_HS = agk::CreateImageColor(255, 48, 48, 255);//红火焰
IMG_bullet_PT = agk::CreateImageColor(0, 255, 255, 255);//亮蓝色
SPR_bg1=agk::CreateSprite(IMG_bg1);
agk::SetSpriteColor(SPR_bg1,252, 157, 154, 255);
actorBox->Add(new TM(100, 300));
actorBox->Add(new F11(200,300));
actorBox->Add(new RedStar(300,300));
}
int app::Loop(void)
{
actorBox->Func();
agk::Print(agk::ScreenFPS());
agk::Print("Thank you for reading!");
agk::Sync();
return 0; // return 1 to close app
}
void app::End(void)
{
delete actorBox;
agk::Message("感谢使用");
}