[Cocos2d塔防遊戲開發]Cocos2dx-3.X完成塔防遊戲《王國保衛戰》--敵人(一)

該章節介紹如何製作敵人

首先根據可能出現的情況定義一個枚舉類型,包括了敵人可能出現的所有狀態,將在以後用到

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函數,這樣就使得敵人不斷的行走,直到走到終點。期間根據下一個地點的位置,判斷敵人是向左走、向右走、向上走,來更新敵人的動畫。當走到終點後,從單例中清除。


這樣就完成了最簡單的敵人

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章