前些天实现了撑杆僵尸,对于撑杆僵尸来说,只有大坚果是无法跳过去的,所以本节实现坚果和大坚果,对于这两种植物,实现起来是不难的,因为只是一般的植物,然后会有两个损伤点,损伤点1:int(MAX HP*2/3) 损伤点2:int(MAX HP*1/3)(摘自百度词条pvz)。接着看看实现吧。
class WallNut : public Plant
{
protected:
enum class State
{
None,
Normal,
Cracked1,
Cracked2,
};
protected:
int m_nAllHp;
State m_state;
public:
WallNut();
~WallNut();
static WallNut*create(const string&plantName);
bool init(const string&plantName);
void setAllHitPoint(const int&hp);
virtual void updateHook(float dt);
virtual void onHurt();
protected:
virtual void changeState(State state);
主要的就是onHurt函数和changeState()
void WallNut::onHurt()
{
Plant::onHurt();
auto curHP = this->getHitPoint();
auto cracked = 3;
if (curHP != 0)
{
cracked = m_nAllHp/curHP;
}
State state = State::None;
if (cracked == 1)
{
state = State::Normal;
}
else if (cracked == 2)
{
state = State::Cracked1;
}
else if (cracked == 3)
{
state = State::Cracked2;
}
this->changeState(state);
}
需要注意的是,当前血量可能会为0,所以需要特地判断以下。
void WallNut::changeState(State state)
{
if (m_state == State::None || m_state == state)
return;
m_state = state;
string animationName;
auto plantName = this->getPlantName();
Animation*animation = nullptr;
if (state == State::Normal)
{
animationName = plantName;
}
else if (state == State::Cracked1)
{
animationName = StringUtils::format("%s_cracked1",plantName.c_str());
}
else if (state == State::Cracked2)
{
animationName = StringUtils::format("%s_cracked2",plantName.c_str());
}
if (!animationName.empty())
{
animation = AnimationCache::getInstance()->getAnimation(animationName);
auto animate = Animate::create(animation);
animate->setTag(ANIMATION_TAG);
//停止原先动画
this->getSprite()->stopActionByTag(ANIMATION_TAG);
this->getSprite()->runAction(animate);
}
}
改变状态还是类似于以前的代码,仅仅进行动画的改变。接下来就是大坚果了。
class TallNut : public WallNut
{
public:
TallNut();
~TallNut();
static TallNut*create(const string&plantName);
bool init(const string&plantName);
};
bool TallNut::init(const string&plantName)
{
this->setPlantName(plantName);
//设置正常动画
auto animationName = plantName;
auto animation = AnimationCache::getInstance()->getAnimation(animationName);
//设置贴图
auto firstFrame = animation->getFrames().front()->getSpriteFrame();
m_pSprite = Sprite::createWithSpriteFrame(firstFrame);
auto size = m_pSprite->getContentSize();
//设置位置
m_pSprite->setPosition(size.width/2.f,size.height/2.f);
//设置锚点
m_pSprite->setAnchorPoint(Point(0.5f,0.65f));
this->setContentSize(size);
this->addChild(m_pSprite);
//设置运行动画
Animate*animate = Animate::create(animation);
animate->setTag(ANIMATION_TAG);
this->getSprite()->runAction(animate);
//设置当前状态
m_state = State::Normal;
return true;
}
大坚果继承自坚果,除了init函数,其他函数全部不需要更新,坚果的changeState的实现中根据当前的植物名称和当前状态确定动画名称,我们所需要做的就是确保他们的格式相同即可。然后就是撑杆僵尸的更新了,撑杆僵尸在跳起会检测当前的目标的植物是否是大坚果,如果是,则越过失败。
void PoleVaultingZombie::jump2()
{
this->changeState(State::Jump2);
//失去撑杆
m_bLosePole = true;
//是否能跳过去 TODO
//直接设置位置
auto pos = this->getPosition();
auto size = this->getContentSize();
auto rect = this->getCollisionBoundingBox();
//获取当前的植物是否是高坚果
auto topPlant = m_pDelegate->getTopPlant(m_pAim);
//跳过失败
if (PlantLayer::isTallNut(topPlant))
{
this->setPosition(pos - Point(40.f,0.f));
}
else
{
this->setPosition(pos - Point(120.f,0.f));
}
//清除当前目标
this->clearAim();
}
这个40和120是根据动画有关的,以后还会进行调整的。然后就是僵尸基类添加一个新的函数。
//是否参与与子弹的碰撞,不包括Boom 默认为true
virtual bool isActiveForBullet()const;
撑杆僵尸在跳起时,暂时不参与与子弹(Boom除外)的碰撞,所以需要进行一个标识。接着就是撑杆僵尸的更新
void PoleVaultingZombie::jump()
{
this->changeState(State::Jump);
//不对子弹起碰撞
m_bIsActiveForBullet = false;
}
bool PoleVaultingZombie::isActiveForBullet()const
{
return m_bIsActiveForBullet;
}
然后就是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))
{
this->handleCollision(bullet,zombie);
}
}
//子弹碰撞结束
bullet->contactEnd();
}
然后添加一个handleCollision函数了。
void BulletLayer::handleCollision(Bullet*bullet,ZombieBase*zombie)
{
auto attactType = bullet->getAttackType();
bool isTrack = Bullet::isTrackBullet(attactType);
bool bIsHurt = false;
//当前子弹是Boom 则无法避免
if (bullet->getAttackType() == AttackType::Boom)
{
bIsHurt = true;
}//僵尸是和子弹能发生碰撞
else if (zombie->isActiveForBullet())
{
//是追踪性子弹 并且碰撞的是目标僵尸
if (isTrack && zombie == bullet->getAimZombie())
bIsHurt = true;
//不是追踪子弹
else if (!isTrack)
bIsHurt = true;
}
if (bIsHurt)
{
bullet->hurt();
//僵尸受伤
zombie->hurt(bullet->getDamage(),bullet->getAttackType());
}
}
这里也只是一个逻辑的更新。本节截图。