cocos2dx 植物大战僵尸 18 土豆雷

土豆雷是我打植物大战僵尸时喜欢使用的植物,它能保证在开局时能快速地种植植物。今天就实现土豆雷。
土豆雷有一个准备时间,在准备时间完成前不具有任何攻击力。以后破土,能够炸死小范围的僵尸,之后土豆雷死亡。 土豆雷在破土后,每一帧检查是否有僵尸和它发生碰撞,如果有,就炸死小范围的僵尸,然后有一个土豆雷炸过后的特效,以及僵尸有一个变成灰的特效。我的思路是使用一个特殊的子弹Boom,这个子弹并没有贴图,只是负责在一帧之内炸死所有和它发生碰撞的所在行的僵尸,这样一来土豆雷就负责调用API,主要的逻辑就给了Boom和检测是否碰撞的方法。
class PotatoMine : public Plant
{
	SDL_SYNTHESIZE(float,m_fReadyTime,ReadyTime);//破土时间
	SDL_SYNTHESIZE(int,m_nDamage,Damage);//伤害值
private:
	bool m_bReady;//是否已经准备完成
	float m_elapsed;
public:
	PotatoMine();
	~PotatoMine();
	static PotatoMine*create(const string&plantName);
	bool init(const string&plantName);

	virtual void updateHook(float dt);
private:
	void readySuccess();
	void checkCollision();
};
看看updateHook()
void PotatoMine::updateHook(float dt)
{
	//当前还没破土
	if (!m_bReady)
	{
		m_elapsed += dt;
		//到达破土时间,破土
		if (m_elapsed >= m_fReadyTime)
		{
			this->readySuccess();
			m_elapsed -= m_fReadyTime;
		}
	}
	else
	{
		//进行僵尸的检测
		this->checkCollision();
	}
}
updateHook()负责逻辑上的更新,并且会在准备完成后判断是否和僵尸碰撞回调函数。
void PotatoMine::checkCollision()
{
	auto row = m_pCarrier->getRow();
	auto rect = this->getBoundingBox();
	auto pos = this->getPosition();

	auto zombie = m_pDelegate->findCollidedZombie(row,rect);
	//发生碰撞,炸死僵尸
	if (zombie != nullptr && zombie->getHitPoint() > 0)
	{
		//展示特效
		m_pDelegate->showExplosionSpudow(pos);
		//直接死亡
		this->setDead(true);
		//爆炸
		m_pDelegate->addBoom(this->getDamage(),row,rect);
	}
}
获取当前行的僵尸并判断是否和僵尸发生碰撞,主要就是findCollidedZombie()函数。
void PotatoMine::readySuccess()
{
	m_bReady = true;
	//获取破土后的动画
	auto animationName = AnimationCache::getInstance()->getAnimation(this->getPlantName());
	auto animate = Animate::create(animationName);

	this->getSprite()->runAction(animate);
	//显示破土特效
	auto pos = this->getPosition();
	Size size = this->getContentSize();

	m_pDelegate->showRisingDirt(pos + Point(0,size.height/3));
}
这个是在破土之后的动画以及破土后的动画
ZombieBase*GameScene::findCollidedZombie(int row,const Rect&rect)
{
	auto zombies = this->getZombiesOfRow(row);
	ZombieBase *zombie = nullptr;

	auto it = find_if(zombies.begin(),zombies.end(),[rect](ZombieBase*zombie)
	{
		return zombie->getCollisionBoundingBox().intersectsRect(rect);
	});
	if (it != zombies.end())
	{
		zombie = *it;
	}
	return zombie;
}
这里是获取一行的僵尸,然后判断是否和所给的矩形发生碰撞,并返回发生碰撞的第一个僵尸。另外就是 并且更新了getZombiesOfRow()函数。
vector<ZombieBase*> GameScene::getZombiesOfRow(int row)
{
	const auto&allZombies = m_pZombieLayer->getZombies();
	vector<ZombieBase*> zombies;

	for (auto mapIter = allZombies.begin();mapIter != allZombies.end();mapIter++)
	{
		auto curRow = mapIter->first;
		auto &lineZombies = mapIter->second;

		if (row == curRow)
		{
			return lineZombies;
		}
		else if (row == -1)
		{
			copy(lineZombies.begin(),lineZombies.end(),back_inserter(zombies));
		}
	}
	return zombies;
}
这里做了一个小小的更新,如果row为-1时则返回所有的僵尸。 另外为Bullet添加一个虚函数,负责获取当前的碰撞范围。
	//碰撞结束后调用
	virtual void contactEnd();
	virtual Rect getCollisionBoundingBox()const;
实现Boom。另外需要注意,如果跨行直接设置row为-1即可。这样就能很简单地实现樱桃炸弹,窝瓜等植物。
class Boom : public Bullet
{
	SDL_SYNTHESIZE(Rect,m_collisionRect,CollisionRect);
public:
	Boom();
	~Boom();
	CREATE_FUNC(Boom);
	bool init();

	virtual void hurt();
	virtual void contactEnd();
	virtual Rect getCollisionBoundingBox()const;
};
Boom类似一般的子弹,接下来看看实现
void Boom::hurt()
{
}

void Boom::contactEnd()
{
	//在一帧后死亡
	this->setHitPoint(0);
	this->setDead(true);
}

Rect Boom::getCollisionBoundingBox()const
{
	return m_collisionRect;
}
contactEnd函数是在BulletLayer中调用的。
void BulletLayer::checkCollisionBetweenZombieAndBullet(Bullet*bullet)
{
	auto row = bullet->getRow();

	auto zombies = m_pDelegate->getZombiesOfRow(row);
	auto r = bullet->getCollisionBoundingBox();

	for (auto it = zombies.begin();it != zombies.end();it++)
	{
		auto zombie = *it;

		if (bullet->isDying())
			break;

		auto rect = zombie->getCollisionBoundingBox();
		//僵尸收到伤害
		if (r.intersectsRect(rect))
		{
			bullet->hurt();
			//僵尸受伤
			zombie->hurt(bullet->getDamage(),bullet->getAttackType());
		}
	}
	//子弹碰撞结束
	bullet->contactEnd();
}
至于土豆雷在发生碰撞后就直接死亡了,其他显示地全是特效层内容罢了,这里就不贴上了。本节效果图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值