該章節介紹如何製作敵人
首先根據可能出現的情況定義一個枚舉類型,包括了敵人可能出現的所有狀態,將在以後用到
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函數,這樣就使得敵人不斷的行走,直到走到終點。期間根據下一個地點的位置,判斷敵人是向左走、向右走、向上走,來更新敵人的動畫。當走到終點後,從單例中清除。
這樣就完成了最簡單的敵人