首先根据可能出现的情况定义一个枚举类型,包括了敌人可能出现的所有状态,将在以后用到
typedef enum{ stateNone = 0,//无状态 stateWalkRight,//向右走走状态 stateWalkLeft,//向左走 stateWalkUp,//向上走 stateWalkDown,//向下走 stateAttackLeft,stateAttackRight,//攻击 stateDeath,//死亡 stateFrozen }MonsterState;
CC_SYNTHESIZE一些可能用到的属性,例如生命值、攻击力,此处略
生成敌人生命条,是一个ProgressTimer
void BaseMonster::createAndSetHpBar() { hpBgSprite = Sprite::createWithSpriteFrameName("lifebar_bg_small.png"); hpBgSprite->setPosition(Point(baseSprite->getContentSize().width / 2,baseSprite->getContentSize().height )); baseSprite->addChild(hpBgSprite); hpBar = ProgressTimer::create(Sprite::createWithSpriteFrameName("lifebar_small.png")); hpBar->setType(ProgressTimer::Type::BAR); hpBar->setMidpoint(Point(0,0.5f)); hpBar->setBarChangeRate(Point(1,0)); hpBar->setPercentage(hpPercentage); hpBar->setPosition(Point(hpBgSprite->getContentSize().width / 2,hpBgSprite->getContentSize().height / 2 )); hpBgSprite->addChild(hpBar); }
因为是一个2.5D的游戏,我们必须设置敌人在Z轴的位置,防止在上面的敌人盖住了下面的敌人,确保下面的敌人盖住上面的敌人
我们将Y轴分为100层,根据敌人所处的Y轴的位置,设置Z轴,确保上面的敌人盖住下面的敌人
void BaseMonster::setMonsterZorder(int yOrder) { int hunder = (yOrder/100); switch (hunder) { case(0): this->setLocalZOrder(10); break; case(1): this->setLocalZOrder(9); break; case(2): this->setLocalZOrder(8); break; case(3): this->setLocalZOrder(7); break; case(4): this->setLocalZOrder(6); break; case(5): this->setLocalZOrder(5); break; case(6): this->setLocalZOrder(4); break; case(7): this->setLocalZOrder(3); break; case(8): this->setLocalZOrder(2); break; case(9): this->setLocalZOrder(1); break; case(10): this->setLocalZOrder(0); break; default: break; } }
下面说说从生产敌人到敌人开始行走的步骤,以上图这种最简单的敌人为例:
首先在地图类的定时刷新敌人方法中(见地图类1)
auto thug = Thug::createMonster(path.at(monsterData->getRoad()).at(monsterData->getPath())); addChild(thug); GameManager::getInstance()->monsterVector.pushBack(thug);//讲敌人加入单例数组
会调用BaseMonster子类的createMonster函数,将从Plist读取的敌人行走路线点传给BaseMonster类,并且设置一些基本属性
Thug* Thug::createMonster(std::vector<Point> points) { auto monster = new Thug(); if (monster && monster->init()) { monster->setPointsVector(points); monster->setMaxHp(35); monster->setCurrHp(35); monster->setMoney(10); monster->setForce(4); monster->setArmor(0); monster->setForce(8); monster->setAttackBySoldier(true);//可以被士兵攻击 monster->setRunSpeed(40);//行走速度 monster->runNextPoint(); monster->autorelease(); return monster; } CC_SAFE_DELETE(monster); return NULL; }
在Init方法中我们会添加敌人静态图片精灵,并且通过scheduleUpdate设置定时器,不断调用update函数,在update函数中,我们根据敌人的状态更新图片和动画
void BaseMonster::update(float dt) { //若状态更新 if(lastState!=getState()){ //根据状态判断 switch (getState()) { case(stateWalkRight):{ lastState = stateWalkRight; //停止之前动画 stopMonsterAnimation(); baseSprite->setFlippedX(false); auto action = RepeatForever::create(Animate::create(AnimationCache::getInstance()->getAnimation(getName()+"runright"))); action->setTag(stateWalkRight); baseSprite->runAction(action);} break; case(stateWalkLeft):{ lastState = stateWalkLeft; stopMonsterAnimation(); baseSprite->setFlippedX(true); auto action = RepeatForever::create(Animate::create(AnimationCache::getInstance()->getAnimation(getName()+"runleft"))); action->setTag(stateWalkLeft); baseSprite->runAction(action);} break; case(stateWalkUp):{ lastState = stateWalkUp; stopMonsterAnimation(); baseSprite->setFlippedX(false); auto action = RepeatForever::create(Animate::create(AnimationCache::getInstance()->getAnimation(getName()+"runup"))); action->setTag(stateWalkUp); baseSprite->runAction(action);} break; case(stateWalkDown):{ lastState = stateWalkDown; stopMonsterAnimation(); baseSprite->setFlippedX(false); auto action = RepeatForever::create(Animate::create(AnimationCache::getInstance()->getAnimation(getName()+"rundown"))); action->setTag(stateWalkDown); baseSprite->runAction(action);} break; case(stateAttackRight):{ //默认向右边攻击 lastState = stateAttackRight; stopMonsterAnimation(); baseSprite->setFlippedX(false); auto action = RepeatForever::create(Animate::create(AnimationCache::getInstance()->getAnimation(getName()+"attack"))); action->setTag(stateAttackRight); baseSprite->runAction(action);} break; case(stateAttackLeft):{ lastState = stateAttackLeft; stopMonsterAnimation(); baseSprite->setFlippedX(true); auto action = RepeatForever::create(Animate::create(AnimationCache::getInstance()->getAnimation(getName()+"attack"))); action->setTag(stateAttackLeft); baseSprite->runAction(action);} break; case(stateNone):{ lastState = stateNone;} break; case(stateFrozen):{ lastState = stateFrozen;} break; case(stateDeath):{ lastState = stateDeath;} break; } } }最后通过nunNextPoint()方法让敌人开始行走
void BaseMonster::runNextPoint() { auto tempCurrPoint = currPoint(); baseSprite->setPosition(tempCurrPoint); tempNextPoint = nextPoint(); setMonsterZorder(tempNextPoint.y); if(fabs(tempNextPoint.y-tempCurrPoint.y)>5 && tempNextPoint.y > tempCurrPoint.y)//正在向上走 { setState(stateWalkUp); }else if(fabs(tempNextPoint.y-tempCurrPoint.y)>5 &&tempNextPoint.y <= tempCurrPoint.y)//正在向下走 { setState(stateWalkDown); }else if(tempNextPoint.x >= tempCurrPoint.x)//正在向右走 { setState(stateWalkRight); } else if(tempNextPoint.x < tempCurrPoint.x)//正在向左走 { setState(stateWalkLeft); } if( tempNextPoint!= tempCurrPoint ){ auto duration = tempCurrPoint.getDistance(tempNextPoint) / getRunSpeed() ; baseSprite->runAction(Sequence::create(MoveTo::create(duration,tempNextPoint),CallFuncN::create(CC_CALLBACK_0(BaseMonster::runNextPoint,this)),NULL)); }else{ //走到终点 GameManager::getInstance()->LIFE --; GameManager::getInstance()->monsterVector.eraSEObject(this); unscheduleAllCallbacks(); setCurrHp(0); } }分析:
若tempNextPoint不等于tempCurrPoint,则表示还没有走到终点,根据下一个地点和目前所处的位置与速度,计算出所需时间,在这个时间结束后再次调用runNextPoiont函数,这样就使得敌人不断的行走,直到走到终点。期间根据下一个地点的位置,判断敌人是向左走、向右走、向上走,来更新敌人的动画。当走到终点后,从单例中清除。
这样就完成了最简单的敌人