cocos2dx 植物大战僵尸 15 豌豆射手的实现

豌豆射手类似于向日葵,只不过向日葵发动技能是产生阳光,而豌豆射手则是产生豌豆子弹罢了。

我只找到了上图的资源,所以在豌豆射手的状态改变则简单多了。

class Peashooter : public Plant
{
	SDL_SYNTHESIZE(int,m_nDamage,Damage);//伤害值
private:
	float m_elapsed;
public:
	Peashooter();
	~Peashooter();
	static Peashooter*create(const string&plantName);
	bool init(const string&plantName);

	virtual void updateHook(float dt);
protected:
	void shoot();
};
初始化函数和以前的类似,就不添加了

void Peashooter::updateHook(float dt)
{
	//获取当前所在行
	int row = m_pCarrier->getRow();
	auto zombie = m_pDelegate->findFirstZombieOfRow(row);
	//僵尸存在并且还没有死亡,则进行攻击倒计时
	if (zombie != nullptr && !zombie->isDead())
	{
		m_elapsed += dt;
		//到达攻击时间
		if (m_elapsed > this->getColdDownTime())
		{
			m_elapsed -= this->getColdDownTime();
			//发射豌豆
			this->shoot();
		}
	}
	else
	{
		m_elapsed = 0.f;
	}
}
豌豆射手是获取所在行是否存在僵尸,然后判断是否发射子弹,m_pCarrier是豌豆射手所在的塔基。

ZombieBase*ZombieLayer::findFirstZombieOfRow(int row)
{
	auto it = m_zombies.find(row);
	ZombieBase*zombie = nullptr;

	if (it != m_zombies.end())
	{
		auto&zombies = it->second;
		//TODO
		if (!zombies.empty())
		{
			zombie = zombies.front();
		}
	}

	return zombie;
}
这个函数是ZombieLayer提供的,就是获取某一行是否存在僵尸,如果存在,则获取第一个,否则返回nullptr

class PlantDelegate
{
public:
	virtual ~PlantDelegate(){}
	virtual void makeSun(int number,FiniteTimeAction*action,const Point&bornPos)=0;
	virtual ZombieBase*findFirstZombieOfRow(int row) = 0;
	virtual void addPeaBullet(int damage,int row,const Point&startPos) = 0;
};
这是目前的植物的委托类里的函数,豌豆射手需要的后两个都需要在GameScene中有对应的实现
void Peashooter::shoot()
{		
	int row = m_pCarrier->getRow();
	int damage = this->getDamage();
	Point startPos = this->getPosition();
	Size size = this->getContentSize();
	//对发射的位置进行确定
	startPos.x += size.width/2.f;
	startPos.y -= size.height/4.f;
	//添加豌豆子弹
	m_pDelegate->addPeaBullet(damage,row,startPos);
}
豌豆的发射函数,在这里调整了以下豌豆的出生位置为豌豆的右上角左右的位置。

好了,现在先测试测试吧

void GameScene::addPeaBullet(int damage,int row,const Point&startPos)
{
	printf("pea bullet shoot\n");
}
编译运行后在豌豆射手所在行放置僵尸就会在控制台上每隔1.4s就会打印一句"pea bullet shoot"的。

接下来就是实现子弹类和豌豆子弹了

,这两个就是豌豆的贴图和死亡动画(死亡动画只有一帧,持续0.5秒)

class Bullet : public Entity
{
public://使用cocos2dx,把SDL修改为CC
	SDL_SYNTHESIZE(int,m_nRow,Row);//子弹当前的行数
	SDL_SYNTHESIZE(int,m_nHitPoint,HitPoint);//当前血量
	SDL_SYNTHESIZE(int,m_nDamage,Damage);//当前伤害
	SDL_BOOL_SYNTHESIZE(m_bDead,Dead);//是否死亡
	SDL_BOOL_SYNTHESIZE(m_bTrack,Track);//是否是追踪弹
public:
	Bullet();
	~Bullet();
	virtual void hurt();
	bool isDying()const;
};
//-----------------------豌豆子弹PeaBullet------------------------------
class PeaBullet : public Bullet
{
public:
	PeaBullet();
	~PeaBullet();
	CREATE_FUNC(PeaBullet);
	bool init();

	virtual void hurt();
};
子弹类有一些基础的数值,值得一提的就是m_bTrack了,这个主要是为了标识该子弹是否是追踪子弹(如屋顶类植物,卷心菜和玉米发射的子弹),因为豌豆子弹会攻击第一个碰到的僵尸,而卷心菜等则是到达目的地后就直接尝试攻击(2d实现类似3D的攻击效果),这个留给以后扩展使用。然后就是hurt(),不同类型的子弹的hurt()是不同的,如豌豆子弹,在碰到第一个僵尸后就死亡了,即不再参与碰撞。而像西瓜,在攻击僵尸后还会有一个溅射伤害,就可以以在这里进行相应的判断。

bool Bullet::isDying()const
{
	return m_nHitPoint <= 0;
}
//---------------------PeaBullet-----------------------------
PeaBullet::PeaBullet()
{
}

PeaBullet::~PeaBullet()
{
}

bool PeaBullet::init()
{
	//绑定精灵
	auto spriteName = STATIC_DATA_STRING("pea_bullet_sprite_name");
	
	this->bindSpriteWithSpriteFrameName(spriteName);
	
	return true;
}

void PeaBullet::hurt()
{
	if (this->isDying() || this->isDead())
		return;

	m_nHitPoint = 0;
	//设置豌豆死亡
	auto animationName = "pea_bullet_dead_anim";
	Animation*animation = AnimationCache::getInstance()->getAnimation(animationName);
	Animate*animate = Animate::create(animation);
	//运行死亡动画
	this->getSprite()->runAction(animate);
	//在死亡动画持续后真正死亡
	DelayTime*delayTime = DelayTime::create(animate->getDuration());
	CallFunc*end = CallFunc::create([this]()
	{
		this->setDead(true);
	});
	//运行死亡动作
	auto seq = Sequence::createWithTwoActions(delayTime,end);
	
	this->stopAllActions();
	this->runAction(seq);
}
豌豆在死亡后,会有一个死亡动画,之后豌豆就真正死亡了(m_bDead 为true时,从场景中移除)

然后新建一个BulletLayer层,负责子弹的生成,更新和死亡。

class BulletLayerDelegate
{
public:
	virtual ~BulletLayerDelegate(){}
	virtual vector<ZombieBase*> getZombiesOfRow(int row)=0;
};

class BulletLayer : public Layer
{
private:
	vector<Bullet*> m_bullets;
	BulletLayerDelegate*m_pDelegate;
public:
	BulletLayer();
	~BulletLayer();
	CREATE_FUNC(BulletLayer);
	bool init();
	void update(float dt);
	//添加豌豆子弹 TODO
	Bullet*addPeaBullet();

	void setDelegate(BulletLayerDelegate*pDelegate);
private:
	void checkCollisionBetweenZombieAndBullet(Bullet*bullet);
};
子弹层有一个委托类,目前仅仅有一个方法,就是获取特定行的僵尸,这个主要是为了便于子弹对僵尸的攻击

void BulletLayer::update(float dt)
{
	for (auto it = m_bullets.begin();it != m_bullets.end();)
	{
		auto bullet = *it;
		//移除该子弹
		if (bullet->isDead())
		{
			bullet->removeFromParent();
			it = m_bullets.erase(it);
			printf("pea dead\n");
		}
		else
		{
			//当前子弹不是追踪性子弹
			if (!bullet->isTrack())
			{
				this->checkCollisionBetweenZombieAndBullet(bullet);
			}
			it++;
		}
	}
}
在这里对子弹进行遍历,看是否产生碰撞或移除。
void BulletLayer::checkCollisionBetweenZombieAndBullet(Bullet*bullet)
{
	auto row = bullet->getRow();

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

	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();
			//僵尸受伤
		}
	}
}
因为目前僵尸还没有受伤的方法,所以就先空着,这个方法的目的很明确,就是检测子弹是否和僵尸发生碰撞。

vector<ZombieBase*> ZombieLayer::getZombiesOfRow(int row)
{
	auto it = m_zombies.find(row);

	if (it != m_zombies.end())
	{
		auto vec = it->second;
		return vec;
	}
	return vector<ZombieBase*>();
}
僵尸层的一个方法,获取对应行的僵尸(目前暂时不考虑杨桃这种特殊植物,目前思路如下,杨桃的所在行设置为-1,然后在僵尸层进行判断,如果是-1就把所有的僵尸都放到一个vector中,进行一次完全遍历)

void GameScene::addPeaBullet(int damage,int row,const Point&startPos)
{
	Size visibleSize = Director::getInstance()->getVisibleSize();
	Point deltaPos(visibleSize.width - startPos.x,0);
	auto bullet = m_pBulletLayer->addPeaBullet();
	//设置基础属性
	bullet->setRow(row);
	bullet->setDamage(damage);
	bullet->setPosition(startPos);
	//添加到场景中
	auto entityLayer = this->getEntityLayer();
	entityLayer->addChild(bullet,BULLET_TAG);
	//设置位置
	deltaPos = m_pLevelLayer->convertToNodeSpace(deltaPos);
	//设置动作
	auto length = deltaPos.length();

	auto move = MoveBy::create(length/300.f,deltaPos);
	CallFunc*end = CallFunc::create([bullet]()
	{
		bullet->setDead(true);
	});
	//运行动作
	auto seq = Sequence::createWithTwoActions(move,end);
	bullet->runAction(seq);
}
修改GameScene的addPeaBullet方法,这里需要注意,豌豆子弹在出了场景之后就直接死亡的。

本节截图

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值