卡牌游戏作为重度游戏的典型,其魅力就不用多说了,好了,让我们一起动手写一个简单的战斗环节的例子吧。
说明,为简单起见,只写了一个1 vs 1的例子,也没有使用特效。
废话不说,上代码:
1、角色血量条的使用:
_blood=ProgressTimer::create(Sprite::create("bloodProgress.png"));
_blood->setScale(_showWidth/_blood->getContentSize().width);
_blood->setPosition(0,_showHeight/2+5);
_blood->setMidpoint(Point(0,0.5));
_blood->setType(ProgressTimer::Type::BAR);
_blood->setPercentage(100);
role里面既包括卡牌角色 _body,也使用ProgressTimer的_blood作为血量条显示,需要注意对战双方的setMidPoint()设置的减少方向最好不一样,美观。
2、重中之重,状态机FSM来了:
(1)创建存放状态、进入状态时回调函数、回调函数对状态的签约等数据的存放,我们使用
std::unordered_map作为主要的容器:
Array *_status=Array::create();
std::unordered_map<std::string, std::function<void()>> _onEnters;
std::unordered_map<std::string, std::unordered_map<std::string,std::string>> _events;
(2)数据初始化
Array *_status, _events可以在FSM类里面直接加载:
fsm->_status->addObject(String::createWithFormat("idle"));
fsm->_status->addObject(String::createWithFormat("walk"));
fsm->_status->addObject(String::createWithFormat("attack"));
fsm->_status->addObject(String::createWithFormat("beHit"));
fsm->_status->addObject(String::createWithFormat("dead"));
fsm->addEvents("doWalk","idle","walk");
fsm->addEvents("doAttack","walk","attack");
fsm->addEvents("doBeHit","attack","beHit");
fsm->addEvents("doAttack","beHit","attack");
fsm->addEvents("doAttack","attack","attack");
fsm->addEvents("doDead","attack","dead");
fsm->addEvents("doDead","beHit","dead");
那么问题来了,当然不是挖掘机, _onEnters容器因为存放的更多是Role类的动作,因此把这个容器的初始化交给Role类吧。
//intial _onEnters
_fsm->_onEnters.insert(std::pair<std::string,std::function<void()>>(String::createWithFormat("idle")->getCString(), onIdle));
_fsm->_onEnters.insert(std::pair<std::string,std::function<void()>>(String::createWithFormat("walk")->getCString(), onWalk));
_fsm->_onEnters.insert(std::pair<std::string,std::function<void()>>(String::createWithFormat("attack")->getCString(), onAttack));
_fsm->_onEnters.insert(std::pair<std::string,std::function<void()>>(String::createWithFormat("beHit")->getCString(), onBeHit));
_fsm->_onEnters.insert(std::pair<std::string,std::function<void()>>(String::createWithFormat("dead")->getCString(), onDead));
(3)每个状态的对应的动作,
auto onIdle=[&](){log("i am idle");};
auto onWalk=[&](){
log("i am walk");
auto moveTo=MoveTo::create(8, _tagetPoint);
this->runAction(moveTo);
};
auto onAttack=[&](){
log("i am attack");
if(_health>0){
this->stopAllActions();
if(_relation==good){
this->schedule(schedule_selector(Role::attackFromPlayer),2);
}
else{
this->schedule(schedule_selector(Role::attackFromMonster),3);}
}else{
//this->unscheduleAllSelectors();
if(_fsm->getStatus()=="dead"){
}else{
_fsm->doEvent("doDead");
}
}
};
auto onBeHit=[&](){
log("i am beHit");
//this->stopAllActions();
if(_health>0){
auto moveLeft=MoveBy::create(0.1, Point(-20,0));
auto moveRight=MoveBy::create(0.1, Point(20,0));
auto onBeHitCall=CallFunc::create([&](){
_health=_health-400;
_blood->setPercentage((_health/1000)*100);
if(_health>0){
this->_fsm->doEvent("doAttack");
}else{
log("signa, h1");
_fsm->doEvent("doDead");
}
});
auto delay=DelayTime::create(2);
if(_relation!=good){
auto seq=Sequence::create(moveRight,moveLeft,onBeHitCall,NULL);
this->runAction(seq);
//this->_fsm->doEvent("doAttack");
}else{
auto seq=Sequence::create(moveLeft,moveRight,onBeHitCall,NULL);
this->runAction(seq);
}
}
else{
log("%f",_health);
}
};
auto onDead=[&](){
this->unscheduleAllSelectors();
this->stopAllActions();
log("i am dead");
CCNotificationCenter::sharedNotificationCenter()->postNotification("someone is killed", NULL);
};