首先我们先观察下这个游戏的箭塔
这个游戏的箭塔都是拥有两个弓箭手。以初级塔为例,左右两边分别有两个弓箭手,可以攻击不同的目标,并且同时只有一个弓箭手在攻击。根据这个特性我们写一个BaseArrowTower
class BaseArrowTower: public BaseTower { public: protected: Sprite* shooter_1;//弓箭手1号 Sprite* shooter_2;//弓箭手2号 Sprite* towerBase;//塔 int shootTag;//弓箭手标记 void initTower(int level);//初始化弓箭塔 void addTerrain();//添加塔坯子(就是那个都是塔下面那个都是弓箭的地面,不同地图有不同的坯子~) virtual Bullet* ArrowTowerBullet();//生成弓箭 virtual void shoot(float dt);//射! };
现在让我们看下防御塔建造过程:
1.选择建造类型
2.执行建造动画(进度条,冒烟等)
4.开始攻击
同样用1级箭塔举例
class SimpleArrowTowerlvl1: public BaseArrowTower { public: bool init();//初始化 CREATE_FUNC(SimpleArrowTowerlvl1); void updateTower();//升级 void showUpdateMenu();//显示升级菜单 Bullet* ArrowTowerBullet();//生成弓箭 private: void buildingAnimation();//开始建造动画 void buildingSmokeAnimation(float dt); };
其中init()函数会由BaseArrowTower中的CREATE_FUNC宏自动执行,在init()中依次初始化各个参数,并且执行建造动画buildingAnimation
其中建造动画就是添加进度条、建造动画精灵等动作
void SimpleArrowTowerlvl1::buildingAnimation() { auto building = Sprite::create(); auto constructing = Sprite::createWithSpriteFrameName("tower_constructing_0004.png"); auto hpBgSprite = Sprite::createWithSpriteFrameName("buildbar_bg.png"); hpBgSprite->setPosition(Point(constructing->getContentSize().width / 2,constructing->getContentSize().height /2+10)); auto hpBar = ProgressTimer::create(Sprite::createWithSpriteFrameName("buildbar.png")); hpBar->setType(ProgressTimer::Type::BAR); hpBar->setMidpoint(Point(0,0.5f)); hpBar->setBarChangeRate(Point(1,0)); hpBar->setPercentage(0); hpBar->setPosition(Point(hpBgSprite->getContentSize().width / 2,hpBgSprite->getContentSize().height / 2 )); hpBgSprite->addChild(hpBar); constructing->addChild(hpBgSprite); building->addChild(constructing); addChild(building); hpBar->runAction(Sequence::create(ProgressTo::create(0.5f,100),CallFuncN::create(CC_CALLBACK_0(Sprite::removeFromParent,building)),NULL)); scheduleOnce(schedule_selector(SimpleArrowTowerlvl1::buildingSmokeAnimation),0.5f); }hpBar->runAction(Sequence::create(ProgressTo::create(0.5f,NULL));是执行一个动画序列,首先是执行进度条的动画,时间0.5秒,从0-100,再执行removeFromParent将建造动画精灵移除。
再延迟0.5S执行scheduleOnce(schedule_selector(SimpleArrowTowerlvl1::buildingSmokeAnimation),0.5f);执行一个冒烟动画然后将塔添加进去(当然这里也可以写到上面的动画序列当中)
void SimpleArrowTowerlvl1::buildingSmokeAnimation(float dt) { auto smoke = Sprite::createWithSpriteFrameName("effect_buildSmoke_0001.png"); addChild(smoke,99); smoke->runAction(Sequence::create( Animate::create(AnimationCache::getInstance()->getAnimation("build_smoke")),smoke)),NULL)); SoundManager::playArcherReady(); initTower(1); setListener(); schedule(schedule_selector(SimpleArrowTowerlvl1::shoot),1.0f); }并在1S后开始执行shoot攻击敌人
void BaseArrowTower::shoot(float dt) { auto instance = GameManager::getInstance(); checkNearestMonster(); char temp1[20]; sprintf(temp1,"level%d_shoot_down",level); char temp2[20]; sprintf(temp2,"level%d_shoot_top",level);//根据等级生成图片名,保存临时变量 if(nearestMonster!=NULL && nearestMonster->getCurrHp() > 0 ) { auto currBullet = ArrowTowerBullet(); Point shootVector = nearestMonster->baseSprite->getPosition() - this->getParent()->getPosition();//怪物精灵是在地图上添加的,而塔的父精灵才是在地图上添加的,所以要统一标准计算 //计算防御塔与敌人之间的角度 float startAngle = currBullet->getRotation(); float endAngle = 0; auto pVectr = currBullet->getPosition()-shootVector; float angleRadians=atan(pVectr.y/pVectr.x); float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians); if(shootVector.x - currBullet->getPosition().x<=0)//敌人在坐标,需要向左攻击 { startAngle = startAngle + abs(angleDegrees);//初始弓箭角度 endAngle = -90;//弓箭结束角度,向下,即插入地面 if(shootTag == 1)//轮到1号弓箭手 { currBullet->setPosition(Point(0,30));//子弹设置在弓箭手所在位置 shooter_1->setFlippedX(true);//X轴翻转,即面向左边 if(shootVector.y - currBullet->getPosition().y<=0) { //执行攻击动画,AnimationCache是自定义的动画类,会根据传入图片名生成帧动画 shooter_1->runAction(Animate::create(AnimationCache::getInstance()->getAnimation(temp1))); }else{ shooter_1->runAction(Animate::create(AnimationCache::getInstance()->getAnimation(temp2))); } shootTag = 2; } else//轮到2号弓箭手 { currBullet->setPosition(Point(10,30)); shooter_2->setFlippedX(true); if(shootVector.y - currBullet->getPosition().y<=0) { shooter_2->runAction(Animate::create(AnimationCache::getInstance()->getAnimation(temp1))); }else{ shooter_2->runAction(Animate::create(AnimationCache::getInstance()->getAnimation(temp2))); } shootTag = 1; } }else//敌人在右边,其他同上 { startAngle = startAngle - abs(angleDegrees); endAngle = 270; if(shootTag == 1) { currBullet->setPosition(Point(0,30)); shooter_1->setFlippedX(false); if(shootVector.y - currBullet->getPosition().y<=0) { shooter_1->runAction(Animate::create(AnimationCache::getInstance()->getAnimation(temp1))); }else{ shooter_1->runAction(Animate::create(AnimationCache::getInstance()->getAnimation(temp2))); } shootTag = 2; } else { currBullet->setPosition(Point(10,30)); shooter_2->setFlippedX(false); if(shootVector.y - currBullet->getPosition().y<=0) { shooter_2->runAction(Animate::create(AnimationCache::getInstance()->getAnimation(temp1))); }else{ shooter_2->runAction(Animate::create(AnimationCache::getInstance()->getAnimation(temp2))); } shootTag = 1; } } //贝叶斯弧线 ccBezierConfig bezier; if(shootTag == 1)//播放射箭音效 SoundManager::playArrowShoot1(); else SoundManager::playArrowShoot2(); //计算贝叶斯曲线的两个控制点 bezier.controlPoint_1 = Point(currBullet->getPosition().x,currBullet->getPosition().y+200); bezier.controlPoint_2 = Point(shootVector.x,shootVector.y+200);; bezier.endPosition = shootVector; auto action = Spawn::create(BezierTo::create(0.5f,bezier),RotateTo::create(0.5f,endAngle),NULL); //攻击动画传递弓箭 currBullet->setBulletAction(action); currBullet->shoot(); currBullet = NULL; } }根据角度使用ccBezierConfig类来生成BezierTo移动动画,是一个弧线,具体内容百度的到
然后用Spwan将BezierTo与弓箭旋转动画合成为一个action,将action传递给子弹类,由子弹类具体执行,为何不在塔类中执行,将在子弹章节中讲解。
执行子弹类的shoot,攻击动画完成
原文链接:https://www.f2er.com/cocos2dx/341201.html