【AppGameKit】使用c++简单制作游戏,让飞机发射子弹

这次我们加入三种子弹,并实现多态。

资源管理

我们这次不用外部加载资源了,我们可以使用代码自己创建一个图像资源。

类结构

在这里插入图片描述
和之前的类结构对比,由于飞机需要发射子弹,所以需要一个计时器,控制发射速度。
物体箱子类,用于存放所有的角色对象,并执行它们的函数。这是多态的体现。
子弹类其实和上次飞机类的道理一样。
这次重在理解飞机发射子弹的原理。

加载资源

声明用于子弹的图片编号(全局变量)

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的所有对象。
这个类很重要,是类与类之间联系的桥梁。

ActorBox.h

#pragma once
#include"Actor.h"
#include<list>
using namespace std;
class ActorBox
{
public:
	ActorBox();
	~ActorBox();

	void Func();
	void Add(Actor* actor);
	void AutoRemove();//自动回收
	void RemoveAll();
	list<Actor*> actors;
};

ActorBox.cpp

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();
}

子弹类

Bullet.h

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;
};

Bullet.cpp

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();
}

普通子弹类

这三个具体的子弹类,只是参数有所不同,和之前的三个飞机类似。

Bullet_normal.h

#include "Bullet.h"
class Bullet_normal :
	public Bullet
{
public:
	Bullet_normal(float _x, float _y,int team);
	~Bullet_normal();
};

个人感觉长方形的子弹更美观。

Bullet_normal.cpp

#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()
{
}

高速弹类

这里速度较高,关于赋值是否合理,我们之后再进行权衡。

Bullet_HS.h

#pragma once
#include "Bullet.h"
class Bullet_HS :
	public Bullet
{
public:
	Bullet_HS(float _x, float _y, int team);
	~Bullet_HS();
};

Bullet_HS.cpp

#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()
{
}

穿透弹类

这里血量较高,不容易直接爆炸。

Bullet_PT.h

#pragma once
#include "Bullet.h"
class Bullet_PT :
	public Bullet
{
public:
	Bullet_PT(float _x, float _y, int team);
	~Bullet_PT();
};

Bullet_PT.cpp

#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的目的。

Time.h

#pragma once
class Timer
{
public:
	Timer(float _timelong);
	~Timer();
	bool GetOk();
	void ReStart();
private:
	float _timelong;
	float _time1;
	float _time2;
};

GetRunTime是AGK提供的获取程序运行时间的函数。
用time1与time2的差值进行计时。

Time.cpp

#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类

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类

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类

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型子弹,

template类

增加一个全局变量,用于储存所有的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("感谢使用");
}

运行效果

在这里插入图片描述
关于飞机的喷气,我们下期讲。

猜你喜欢

转载自blog.csdn.net/weixin_44611096/article/details/106038660