C++学习 2018-12-11

1.飞机大战

1.完成FoePlaneBox类

1.与子弹盒子类似,需要一个用来存放所有飞机的链表:list<CFoePlane*> m_lstFoePlane。
2.需要有三个成员函数:CreateFoePlane、AllFoePlaneShow、AllFoePlaneShow。

1)CreateFoePlane 函数用来创建敌人飞机,在创建敌人飞机的时候,我们需要产生不同类型飞机的频率不同(意思是每产生一架大飞机能产生三架中飞机或六架小飞机),一个简单的办法是通过随机数产生一个int值,然后按照不同的区间进行划分;
2)创建飞机并初始化,注意:这里创建的飞机是父类型 FoePlane 型的指针,指向子类型 new 出来的对象;
3)调用各自的InitFoePlane函数初始化,并将飞机加入到链表中;
4)显示所有飞机,需要 AllFoePlaneShow 函数遍历 m_lstFoePlane 链表来实现;
5)所有飞机进行移动,需要先判断该机是否已经不在窗口内部显示(即判断y的值),若是则删除该飞机,否则调用其自己的show函数;

6)主要代码:

#include "FoePlaneBox.h"


CFoePlaneBox::CFoePlaneBox(void)
{
}

CFoePlaneBox::~CFoePlaneBox(void)
{
	list<CFoePlane*>::iterator ite = m_lstFoePlane.begin();
	while (ite != m_lstFoePlane.end())
	{
		delete(*ite);
		ite = m_lstFoePlane.erase(ite);
	}
}

void CFoePlaneBox::AllFoePlaneMove()
{
	list<CFoePlane*>::iterator ite = m_lstFoePlane.begin();
	while (ite != m_lstFoePlane.end())
	{
		if((*ite)->y > 550)
		{
			delete(*ite);
			ite = m_lstFoePlane.erase(ite);
		}
		else
		{
			(*ite)->FoePlaneMove();
			ite++;
		}
	}
}

void CFoePlaneBox::AllFoePlaneShow(HDC dc)
{
	list<CFoePlane*>::iterator ite = m_lstFoePlane.begin();
	while (ite != m_lstFoePlane.end())
	{
		(*ite)->FoePlaneShow(dc);
		ite++;
	}
}

void CFoePlaneBox::CreateFoePlane(HINSTANCE hIns)
{
	CFoePlane *new_foe = 0;
	int flag = rand()%12;
	if(flag >=0 && flag <= 2)
	{
		new_foe = new CBigFoePlane;
	}
	if(flag >=3 && flag <= 6)
	{
		new_foe = new CMidFoePlane;
	}
	if(flag >=6 && flag <= 12)
	{
		new_foe = new CSmallFoePlane;
	}
	new_foe->InitFoePlane(hIns);
	m_lstFoePlane.push_back(new_foe);
}
3.当我们完成上述步骤之后,我们就已经完成了敌人飞机的大部分代码,剩下的就是将敌人飞机进行调用,那么该如何进行调用呢?

1)我们需要在 PlaneApp 的成员中加入 FoePlaneBox 类对象来记录敌人飞机;
2)需要在 PlaneApp 的成员函数 OnCreateGame 中设置敌人飞机生成的定时器 FOE_PLANE_CREATE_TIMER_ID、敌人飞机移动的定时器 FOE_PLANE_MOVE_TIMER_ID;
3)需要在 PalneApp 的成员函数 OnGameRun 中设置当上述 FOE_PLANE_CREATE_TIMER_ID 定时器消息来临时,我们调用敌人飞机盒子的 CreateFoePlane 函数;
4)在 PalneApp 的成员函数 OnGameRun 中设置当上述 FOE_PLANE_MOVE_TIMER_ID 定时器消息来临时,我们调用敌人飞机盒子的 AllFoePlaneMove 函数;
5)完成上述代码之后就可以看到移动的敌人飞机了!!!

2.判断敌人飞机是否被击中

1.判断敌人飞机是否被击中就是通过子弹的x、y和敌人飞机的x、y以及宽高来比较得到是否击中。
2.对于不同类型的敌人飞机,不同之处在于每个飞机的宽和高不同,其他基本相同。

1)对于大飞机:

bool CBigFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
	if(pGunner->x > this->x && pGunner->x < this->x+108
		&& pGunner->y > this->y && pGunner->y < this->y+128)
	{
		return TRUE;
	}
	return FALSE;
}

2)对于中等飞机:

bool CMidFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
	if(pGunner->x > this->x && pGunner->x < this->x+70
		&& pGunner->y > this->y && pGunner->y < this->y+90)
	{
		return TRUE;
	}
	return FALSE;
}

3)对于小型飞机:

bool CSmallFoePlane::IsGunnerHitForPlane(const CGunner* pGunner)
{
	if(pGunner->x > this->x && pGunner->x < this->x+32
		&& pGunner->y > this->y && pGunner->y < this->y+28)
	{
		return TRUE;
	}
	return FALSE;
}

3.设计一个存储爆炸飞机的类

1.我们可以将所有的飞机分为两类,一类是有血的飞机,另一类是没血了的飞机,没血了的飞机我们要播放(虽然很高大上,但实质上也是贴图…)爆炸效果,那么对于没血了的飞机,我们也需要一个类似于 FoePlaneBox 的类来存储。
2.将这个类命名为 BlastFoePlaneBox 类,该类有一个成员:list<CFoePlane*> m_lstBlastFoePlane,成员方法包括显示飞机 AllBlastFoePlaneShow 和 改变显示的飞机的ID ChangeShowID。

1)对于 ChangeShowID 函数,我们想要做的就是将 foeplane 的 showid 进行–,若减到零则删除飞机;
2)对于 AllBlastFoePlaneShow 函数,遍历 m_lstBlastFoePlane 链表,对于每个飞机调用其自身的 FoePlaneShow 函数即可;

3)主要代码:

#include "BlastFoePlaneBox.h"


CBlastFoePlaneBox::CBlastFoePlaneBox(void)
{
}

CBlastFoePlaneBox::~CBlastFoePlaneBox(void)
{
	list<CFoePlane*>::iterator ite = m_lstBlastFoePlane.begin();
	while (ite != m_lstBlastFoePlane.end())
	{
		delete(*ite);
		ite = m_lstBlastFoePlane.erase(ite);
	}
}

void CBlastFoePlaneBox::AllBlastFoePlaneShow(HDC dc)
{
	list<CFoePlane*>::iterator ite = m_lstBlastFoePlane.begin();
	while (ite != m_lstBlastFoePlane.end())
	{
		(*ite)->FoePlaneShow(dc);
		++ite;
	}
}

void CBlastFoePlaneBox::ChangeShowID()
{
	list<CFoePlane*>::iterator ite = m_lstBlastFoePlane.begin();
	while (ite != m_lstBlastFoePlane.end())
	{
		if((*ite)->m_nShowID == 0)
		{
			delete(*ite);
			ite = m_lstBlastFoePlane.erase(ite);
		}
		else
		{
			((*ite)->m_nShowID)--;
			ite++;
		}
	}
}

4.判断子弹是否击中飞机

1.对于子弹是否能击中飞机,我们如何判断?

1)由于我们将所有的子弹和所有的敌人飞机都放入了链表中,因此首先需要进行遍历子弹链表;
2)在遍历子弹链表的循环中,需要嵌套一个遍历敌人飞机的循环(即每一个子弹都与所有飞机进行判断,尽管这种方法很笨,但到目前为止我还不会其他好一点方法);
3)对于每一个敌人飞机,都调用它的 IsGunnerHitForPlane 函数,若是,则删除子弹,并让敌人飞机掉血,若敌人飞机没血了(IsBoom 函数返回true),则将该飞机添加到新的 blast_foeplane_box 对象中去,并删除这个敌人飞机;
4)在以上过程当中,我们会有一次删除子弹的操作,当击中飞机后,我们删除该子弹,但是遍历子弹链表仍会向后移动一个节点,这将会导致我们少遍历了一个子弹,因此我们用一个 bool 型变量 flag_of_hit 来记录子弹是否击中飞机,若是,则置为 true,并不进行子弹链表自增的操作,若否,则让子弹链表的迭代器自增;

5)主要代码:

void CPlaneApp::GunnerHitFoePlane()
{
	bool flag_of_hit = false;
	list<CGunner*>::iterator ite_gunner = gun_box.m_lstGunner.begin();
	// 遍历所有子弹
	while (ite_gunner != gun_box.m_lstGunner.end())
	{
		// 每一个子弹是否击中飞机
		list<CFoePlane*>::iterator ite_foe_plane = foe_plane_box.m_lstFoePlane.begin();
		while (ite_foe_plane != foe_plane_box.m_lstFoePlane.end())
		{
			if((*ite_foe_plane)->IsGunnerHitForPlane(*ite_gunner) == true)		// 当前子弹击中了飞机
			{
				flag_of_hit = true;					// 击中了就标记为true
				// 删除炮弹
				delete(*ite_gunner);
				ite_gunner = gun_box.m_lstGunner.erase(ite_gunner);
				(*ite_foe_plane)->DownBlood();
				if((*ite_foe_plane)->IsBoom() == true)			// 如果飞机没血了
				{
					blast_foeplane_box.m_lstBlastFoePlane.push_back(*ite_foe_plane);
					ite_foe_plane = foe_plane_box.m_lstFoePlane.erase(ite_foe_plane);
				}
				break;
			}
			++ite_foe_plane;
		}
		if(flag_of_hit)
			flag_of_hit = false;
		else
			++ite_gunner;
	}
}

5.显示爆炸效果

1.在上面我们完成了 BlastFoePlane 类,可以通过设置定时器来显示爆炸效果了。

1)在 PlaneApp 的 OnGameRun 函数中设置一个 CHANGE_PLANE_SHOWID_TIMER_ID 的定时器,当这个定时器消息来到时,我们就调用 blast_foeplane_box.ChangeShowID();
2)增加一个小功能:按下空格键,所有的敌人飞机都爆炸(是不是外挂啊喂~);

3)主要代码:

void CPlaneApp::OnGameRun(WPARAM w_param)
{
	if(w_param == PLAYER_MOVE_TIMER_ID)
	{
		if(::GetAsyncKeyState(VK_LEFT))
			player.MovePlayer(VK_LEFT);
		if(::GetAsyncKeyState(VK_RIGHT))
			player.MovePlayer(VK_RIGHT);
		if(::GetAsyncKeyState(VK_DOWN))
			player.MovePlayer(VK_DOWN);
		if(::GetAsyncKeyState(VK_UP))
			player.MovePlayer(VK_UP);
		
	}

	if(w_param == BACK_MOVE_TIMER_ID)
	{
		back.MoveBack();
		
	}
	if(w_param == SEND_GUNNER_TIMER_ID)
	{
		player.SendGunner(gun_box, m_h_ins);
	}
	if (w_param == GUNNER_MOVE_TIMER_ID)		// 每次炮弹移动都需要进行判断是否击中了飞机
	{
		gun_box.AllGunnerMove();
		this->GunnerHitFoePlane();
	}
	// 敌方飞机生成
	if(w_param == FOE_PLANE_CREATE_TIMER_ID)
	{
		foe_plane_box.CreateFoePlane(m_h_ins);
	}
	// 敌方飞机移动
	if(w_param == FOE_PLANE_MOVE_TIMER_ID)
	{
		foe_plane_box.AllFoePlaneMove();
	}
	// 飞机爆炸显示图
	if(w_param == CHANGE_PLANE_SHOWID_TIMER_ID)
	{
		blast_foeplane_box.ChangeShowID();
	}
	this->OnGameShow();
}

void CPlaneApp::OnGameKeyDown(WPARAM w_param)		// 外挂函数
{
	// 按下空格键将所有敌人飞机销毁
	if(w_param == VK_SPACE)
	{
		blast_foeplane_box.m_lstBlastFoePlane.splice(blast_foeplane_box.m_lstBlastFoePlane.end(), foe_plane_box.m_lstFoePlane);
	}
}

-------------------------------------------未完待续-------------------------------

猜你喜欢

转载自blog.csdn.net/weixin_42896619/article/details/85852564