寒冰射手是我目前感觉比较难的植物,因为这个植物的子弹会给僵尸一个减速的debuff的,而这个debuff对有的僵尸 起作用,比如一般的僵尸,而对带有报纸的僵尸或者一些特殊的僵尸也是不会减速的,所以饰品需要以下两个功能
1.吸收伤害
2.吸收一些debuff
因为c++无法有两个返回值,所以就不能使用返回值了,对于c++,可以使用引用来改变伤害值和当前攻击类型。先对当前的饰品基类进行改变。
class Garnishry : public Entity
{
public:
enum class Type
{
Common,//普通饰品
Iron,//铁质饰品
};
SDL_SYNTHESIZE(int,m_nHitPoint,HitPoint);//当前血量
SDL_SYNTHESIZE(Type,m_type,Type);//饰品类型
public:
Garnishry();
~Garnishry();
/**吸收伤害值
*baseDamage attackType可能会改变*/
virtual void absorbDamage(int &baseDamage,AttackType &attackType);
路障和报纸需要作一些改变
void Conehead::absorbDamage(int &baseDamage,AttackType &attackType)
{
//吸收部分伤害,如果伤害值小于0,强制减少1
auto damage = baseDamage - this->getDefense();
if (damage < 0)
{
damage = 1;
}
baseDamage = damage;
}
void Paper::absorbDamage(int &baseDamage,AttackType &attackType)
{
int afterDamage = 0;
//跟踪性子弹,无法吸收
if (Bullet::isTrackBullet(attackType))
{
afterDamage = baseDamage;
}
else//吸收伤害TODO
{
auto afterHP = this->getHitPoint() - baseDamage;
//饰品即将死亡
if (afterHP <= 0)
{
afterDamage = SDL_abs(afterHP);
afterHP = 0;
}
this->setHitPoint(afterHP);
m_pHpBar->setValue((float)afterHP);
//如果受到寒冰攻击,转换成正常状态
if (attackType == AttackType::Deceleration)
{
attackType = AttackType::Common;
}
}
baseDamage = afterDamage;
}
报纸能使得当前的报纸僵尸免疫减速效果的,这里注意,AttackType增加了一个减速类型,目前的Attack Type如下
enum class AttackType
{
Common,/*无效果*/
Boom,/*爆炸效果*/
Swallow,/*吞噬效果*/
Track,/*追踪效果*/
Deceleration,/*减速*/
TrackAndDeceleration,/*追踪且减速*/
};
这是对以后的一些适配,以后可能还是会修改的。接下来就是僵尸基类的改变了。
void ZombieBase::hurt(int baseDamage,AttackType attackType)
{
int afterDamage = baseDamage;
//调用饰品,吸收伤害
if (m_pGarnishry != nullptr && m_pGarnishry->getHitPoint() > 0)
{
m_pGarnishry->absorbDamage(baseDamage,attackType);
afterDamage = baseDamage;
//如果饰品死亡,回调饰品死亡函数
if (m_pGarnishry->getHitPoint() <= 0)
onGarnishryDead();
}
if (afterDamage <= 0)
return ;
//进行debuff的操作
this->debuff(attackType);
auto afterHP = this->getHitPoint() - afterDamage;
bool bDead = false;
if (afterHP <= 0)
{
afterHP = 0;
bDead = true;
}
this->setHitPoint(afterHP);
//设置血量条
if (m_pHpBar != nullptr)
{
m_pHpBar->setValue((float)afterHP);
}
onHurt();
//如果死亡,回调死亡函数
if (bDead)
{
if (attackType == AttackType::Swallow)
{
onSwallowDead();
}
else if (attackType == AttackType::Boom)
{
onBoomDead();
}
else
{
onNormalDead();
}
}
}
在这之前还需要添加
/*僵尸的减益效果*/
enum class DebuffType
{
Deceleration,
};
typedef struct Debuff
{
DebuffType type;
float duration;
public:
Debuff(DebuffType type,float d):type(type),duration(d){}
}Debuff;
表示当前的减益效果和持续时间。然后需要在ZombieBase种添加一个数组,来保存当前的debuff
vector<Debuff> m_debuffs;//当前的僵尸的debuff
然后添加了两个函数。
//减速debuff开始动画必须经过Speed包装
virtual void onDecelerationDebuffEnter();
//减速debuff结束
virtual void onDecelerationDebuffExit();
void ZombieBase::debuff(AttackType attackType)
{
//TODO
if (attackType == AttackType::Deceleration)
{
DebuffType debuffType = DebuffType::Deceleration;
auto it = find_if(m_debuffs.begin(),m_debuffs.end(),[debuffType](Debuff debuff)
{
return debuff.type == debuffType;
});
if (it != m_debuffs.end())
{
it->duration = 10.f;
}
else//添加buff
{
m_debuffs.push_back(Debuff(debuffType,10.f));
//回调减速函数
this->onDecelerationDebuffEnter();
}
}
}
同时修改了update的函数。
void ZombieBase::update(float dt)
{
//当前僵尸血量小于0,死亡
if (this->getHitPoint() <= 0)
{
auto afterCRP = this->getCriticalPoint() - 0.4f;
this->setCriticalPoint(afterCRP);
//僵尸真正死亡
if (afterCRP <= 0.f)
{
onCRPDead();
}
}
else
{
updateAlive(dt);
}
//进行debuff的更新
for (auto it = m_debuffs.begin();it != m_debuffs.end();)
{
auto &debuff = *it;
debuff.duration -= dt;
//减速结束
if (debuff.duration <= 0.f &&
debuff.type == DebuffType::Deceleration)
{
it = m_debuffs.erase(it);
this->onDecelerationDebuffExit();
}
else
{
it++;
}
}
performMove(dt);
}
在update中进行debuff的更新,如果持续时间到了之后,就回调onDecelrationDebuffExit()函数。
void ZombieBase::onDecelerationDebuffEnter()
{
if (m_pSprite)
{
m_pSprite->setColorMod(Color3B(0,255,255));
auto action = this->getSprite()->getActionByTag(ANIMATION_TAG);
auto speed = dynamic_cast<Speed*>(action);
speed->setSpeed(0.7f);
}
}
void ZombieBase::onDecelerationDebuffExit()
{
if (m_pSprite)
{
m_pSprite->setColorMod(Color3B(255,255,255));
auto action = this->getSprite()->getActionByTag(ANIMATION_TAG);
auto speed = dynamic_cast<Speed*>(action);
speed->setSpeed(1.f);
}
}
注意,在僵尸减速时会使得动画播放会变得很慢,所以需要内部动画使用Speed进行封装(Speed使用了装饰者模式),这个需要在子类中进行定义每个动画的tag必须是ANIMATION_TAG,并且是Speed才行。
float ZombieBase::getCurSpeed()const
{
auto speed = m_fBaseSpeed;
//减速成三分之二
if (this->isDuringDeceleration())
{
speed = speed /3.f * 2.f;
}
return speed;
}
获取当前的速度收到减速的影响,会减速到原来的三分之二
bool ZombieBase::isDuringDeceleration()const
{
auto it = find_if(m_debuffs.begin(),m_debuffs.end(),[](Debuff debuff)
{
return debuff.type == DebuffType::Deceleration;
});
if (it != m_debuffs.end())
{
return true;
}
return false;
}
表示当前是否存在减速状态。然后需要在子类僵尸中进行更新动画,以报纸僵尸为例。
bool PaperZombie::init(const string&zombieName)
{
//正常动画
this->setZombieName(zombieName);
//获取站立状态贴图
int status = m_bIsAngry ? 0 : 1;
auto animationName = StringUtils::format("%sWalk%d",m_zombieName.c_str(),status);
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/2);
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;
}
同时在
virtual void changeState(State state);
virtual void showZombieHead();
virtual float getCurSpeed()const;
virtual void onGarnishryDead();
也是需要上面的更新。
float PaperZombie::getCurSpeed()const
{
auto speed = ZombieBase::getCurSpeed();
if (m_bIsAngry)
speed *= 2.f;
return speed;
}
当前速度受到了减速效果,同时也会加速的。然后就是寒冰子弹的实现了,寒冰子弹的实现类似于一般的豌豆子弹,只是死亡动画不同,不再叙述。
SnowPeaBullet*BulletLayer::addSnowPeaBullet()
{
//设置基础属性
auto bullet = SnowPeaBullet::create();
bullet->setDead(false);
bullet->setAttackType(AttackType::Deceleration);
bullet->setHitPoint(1);
m_bullets.push_back(bullet);
return bullet;
}
本节游戏截图。