撑杆僵尸在一开始快速运动,能跳过第一个碰到的植物,并且在这之后速度会变慢下来,并且在跳的过程中是不与子弹碰撞 以及免疫冰冻等特殊效果的。所以需要有一个撑杆饰品类,作为以后的扩展使用,同时需要更新僵尸基类。
class ZombieDelegate
{
public:
virtual Plant*getTopPlant(Terrain*terrain) = 0;
virtual void showZombieHead(const Point&pos,int tag) = 0;
virtual void showZombieDie(const Point&pos,int tag) = 0;
virtual void showConeAction(const Point&pos,int tag) = 0;
virtual void showZombieBoomDie(const Point&pos,int tag)=0;
virtual void showPaperZombieDie(const Point&pos,int tag) = 0;
virtual void showPaperZombieHead(const Point&pos,int tag) = 0;
virtual void showPoleVaultingZombieHead(const Point&pos,int tag)=0;
virtual void showPoleVaultingZombieDie(const Point&pos,int tag)=0;
};
增加了撑杆的死亡动画,同时所有的这些方法都添加了一个tag,这个tag是表示的当前僵尸所处的tag来处理各种遮挡。需要在其他类中进行相应的更新。
//是否参与与子弹的碰撞,不包括Boom
virtual bool isActiveForBullet()const;
这个是ZombieBase新添加的方法,默认返回true。接下来看看ZombieDelegate的更新。然后在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();
}
把僵尸和子弹的碰撞处理放到了一个新的函数中。
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());
}
}
这里用了新添加的函数isActiveForBullet()来处理碰撞。接着就是实现撑杆僵尸相应的动画更改和逻辑。
class PoleVaultingZombie : public ZombieBase
{
private:
enum class State
{
None,
Walk,
Attack,
WalkDead,
AttackDead,
Jump,
Jump2,
};
private:
bool m_bLosePole;//是否失去了撑杆
bool m_bIsActiveForBullet;//是否对子弹有效
State m_state;
float m_duration;
float m_elapsed;
public:
PoleVaultingZombie();
~PoleVaultingZombie();
static PoleVaultingZombie*create(const string&zombieName);
bool init(const string&zombieName);
private:
virtual void updateAlive(float dt);
virtual void performMove(float dt);
virtual float getCurSpeed()const;
virtual Rect getCollisionBoundingBox()const;
virtual void onNormalDead();
virtual void onCRPDead();
virtual bool isActiveForBullet()const;
void jump();//僵尸跳起
void jump2();//跳起阶段2
void jumpEnd();
void attackUpdate(float dt);
void changeState(State state);
void showZombieHead();
};
看看相应的实现。
bool PoleVaultingZombie::init(const string&zombieName)
{
this->setZombieName(zombieName);
//获取行走状态贴图
auto animationName = zombieName;
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,size.height*0.55f);
this->setContentSize(size);
this->addChild(m_pSprite);
//运行动画
auto animate = Animate::create(animation);
Speed*speed = Speed::create(animate,1.f);
speed->setTag(ANIMATION_TAG);
m_pSprite->runAction(speed);
//设置为行走状态
m_state = State::Walk;
return true;
}
动画处理和以前的僵尸类相同,没什么太多注意的。
void PoleVaultingZombie::updateAlive(float dt)
{
//处于跳起状态
if (m_state == State::Jump)
{
m_elapsed += dt;
//跳起完成 检测是否碰撞的是否是高坚果
if (m_elapsed >= m_duration)
{
m_elapsed = 0.f;
this->jump2();
}
}//跳起2
else if (m_state == State::Jump2)
{
m_elapsed += dt;
//转到正常动画
if (m_elapsed >= m_duration)
{
m_elapsed = 0.f;
this->jumpEnd();
}
}
else if (m_pAim != nullptr)
{
//当前存在植物
if (m_pAim->getInnerPlant() != nullptr)
{
if (m_bLosePole)
this->attackUpdate(dt);
else
this->jump();
}
else
{
this->clearAim();
m_elapsed = 0.f;
}
}
}
在updateAlive中进行相应的逻辑更新。
void PoleVaultingZombie::performMove(float dt)
{
if (m_state == State::Jump || m_state == State::Jump2)
return;
if (m_pMoveBehavior != nullptr &&
(m_state == State::Walk || m_state == State::WalkDead))
{
m_pMoveBehavior->performMove(this);
}
}
撑杆僵尸在跳起阶段是不会进行移动的,这个需要注意。
float PoleVaultingZombie::getCurSpeed()const
{
auto speed = ZombieBase::getCurSpeed();
if (m_bLosePole)
speed /= 3.f;
return speed;
}
获取当前的速度则是直接除以三即可,也可以进行相应的调整。
Rect PoleVaultingZombie::getCollisionBoundingBox()const
{
auto rect = ZombieBase::getCollisionBoundingBox();
rect.origin.x += rect.size.width/2.f;
rect.size.width /= 4.f;
rect.origin.y += rect.size.height/2.f;
rect.size.height /= 2.f;
return rect;
}
获取当前的碰撞面积,这个跟撑杆僵尸的图片有关,撑杆僵尸的图片如下
void PoleVaultingZombie::onNormalDead()
{
if (m_state == State::Walk)
changeState(State::WalkDead);
else if (m_state == State::Attack)
changeState(State::AttackDead);
}
void PoleVaultingZombie::onCRPDead()
{
this->setDead(true);
//显示死亡动画
m_pDelegate->showPoleVaultingZombieDie(this->getPosition(),this->getTag());
}
bool PoleVaultingZombie::isActiveForBullet()const
{
return m_bIsActiveForBullet;
}
void PoleVaultingZombie::jump()
{
this->changeState(State::Jump);
//不对子弹起碰撞
m_bIsActiveForBullet = false;
}
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();
}
void PoleVaultingZombie::jumpEnd()
{
this->changeState(State::Walk);
m_bIsActiveForBullet = true;
}
void PoleVaultingZombie::attackUpdate(float dt)
{
this->changeState(State::Attack);
m_elapsed += dt;
//到达攻击时间
if (m_elapsed >= this->getColdDownTime())
{
m_elapsed -= this->getColdDownTime();
//进行攻击
auto topPlant = m_pDelegate->getTopPlant(m_pAim);
topPlant->hurt(this->getDamage());
}
}
void PoleVaultingZombie::changeState(State state)
{
//状态没有发生改变,直接退出
if (m_state == state)
return ;
m_state = state;
string animationName;
Animation*animation = nullptr;
auto zombieName = this->getZombieName();
if (m_state == State::Walk)
{
if (m_bLosePole)
{
animationName = StringUtils::format("%sWalk",zombieName.c_str());
}
else
{
animationName = zombieName;
}
animation = AnimationCache::getInstance()->getAnimation(animationName);
}
else if (m_state == State::Attack)
{
animationName = StringUtils::format("%sAttack",zombieName.c_str());
animation = AnimationCache::getInstance()->getAnimation(animationName);
}
else if (m_state == State::Jump)
{
animationName = StringUtils::format("%sJump",zombieName.c_str());
animation = AnimationCache::getInstance()->getAnimation(animationName);
m_duration = animation->getDuration();
}
else if (m_state == State::Jump2)
{
animationName = StringUtils::format("%sJump2",zombieName.c_str());
animation = AnimationCache::getInstance()->getAnimation(animationName);
m_duration = animation->getDuration();
}
else if (m_state == State::WalkDead)
{
if (!m_bLosePole)
{
animationName = StringUtils::format("%sLostHead",zombieName.c_str());
}
else
{
animationName = StringUtils::format("%sLostHeadWalk",zombieName.c_str());
}
animation = AnimationCache::getInstance()->getAnimation(animationName);
this->showZombieHead();
}
else if (m_state == State::AttackDead)
{
animationName = StringUtils::format("%sLostHeadAttack",zombieName.c_str());
animation = AnimationCache::getInstance()->getAnimation(animationName);
this->showZombieHead();
}
if (animation != nullptr)
{
//停止原先的动画
this->getSprite()->stopActionByTag(ANIMATION_TAG);
auto animate = Animate::create(animation);
//是否处于减速状态
Speed*speed = Speed::create(animate,1.f);
if (this->isDuringDeceleration())
speed->setSpeed(0.7f);
speed->setTag(ANIMATION_TAG);
this->getSprite()->runAction(speed);
}
}
void PoleVaultingZombie::showZombieHead()
{
//调整位置
Point bornPos = this->getPosition();
Size size = this->getContentSize();
m_pDelegate->showPoleVaultingZombieHead(bornPos,this->getTag());
}
changeState中依旧是对动画进行改变的。接下来就是ZombieFactory
PoleVaultingZombie*ZombieFactory::createPoleVaultingZombie(const string&zombieName)
{
auto zombie = PoleVaultingZombie::create(zombieName);
//设置基础属性
zombie->setDead(false);
zombie->setHitPoint(430);
zombie->setBaseSpeed(0.3f);
zombie->setColdDownTime(1.f);
zombie->setDamage(100);
zombie->setCriticalPoint(70.f);
//添加血条 并对血条的位置进行调整
Size size = zombie->getContentSize();
Rect rect = zombie->getSprite()->getSpriteFrameRect();
Point pos(size.width*0.75f,rect.origin.y);
this->bindHpBarForZombie(zombie,pos);
return zombie;
}
新建撑杆僵尸,并且设置血条位置
Point ZombieFactory::bindHpBarForZombie(ZombieBase*zombie,const Point&pos)
{
float hp = (float)zombie->getHitPoint();
HpBar*hpBar = HpBar::create("hpBar1.png","hpBarBG.png",hp);
hpBar->setPosition(pos);
zombie->bindHpBar(hpBar);
return pos;
}
对绑定血条相应的更新
Point ZombieFactory::bindHpBarForZombie(ZombieBase*zombie)
{
//设置血条位置
auto rect = zombie->getSprite()->getSpriteFrameRect();
Size size = zombie->getContentSize();
Point pos = Point(rect.origin.x + rect.size.width/2,rect.size.height/2 - size.height/2);
return this->bindHpBarForZombie(zombie,pos);
}