最近在开发一款基于cocos2d-x的斗地主棋牌游戏,在游戏开发的过程中对cocos2d-x这款引擎有了更加深刻的理解。现针对分牌动作的实现过程来对cocos2d-x引擎下动作处理进行一个简要的概括和总结。
首先在这里先推荐一篇博客地址:http://codingnow.cn/cocos2d-x/775.html,里面就CCAction的体系结构做了不错的总结,而且该博主的一篇关于cocos2d-x关于触摸事件的文章介绍的也比较详细,建议大家去看一下。
说到动作,我们首先想到的就是精灵的移动。这里就要用到CCMoveTo和CCMoveBy了,它们都是CCAction的子类,两者区别在于,CCMoveTo是根据参数移动到指定的地点,而CCMoveBy的参数则是表明移动的具体长度,例如CCMoveBy *moveBy = CCMoveBy::create(0, ccp(100, 100));这句话表示调用精灵会在原位置点沿x轴方向(向右)移动100,沿y轴方向(向上)移动100。而如果是CCMoveTo的话则会移动到ccp(100, 100)这个点了。
然而在实际的游戏开发过程中,可能会用到比较复杂的动作效果,这时我们需要将一些对象组合起来,就需要用到CCSequence和CCSpawn了。两者创建的方式比较类似,都是通过其静态create函数来创建,参数为你需要组合的action对象,注意最后一个参数要为NULL。两者的区别就是,CCSequence会将构建时的传进的action对象依次执行,而CCSpawn则是将传进的action对象同时执行。然而上述两种方法仅是针对同一个精灵提供的系统动作的操作方法。有时我们需要做到让一个精灵区响应另一个精灵的动作,也就是说需要精灵A执行完其动作后来通知精灵B执行对应的动作。所谓动作的执行,实际上是通过CCActionManager在每一帧刷新的时候,对每一个action调用step函数从而实现动作的播放。介绍这些,主要是想说明动作的实现是基于帧的刷新的,而与代码中添加的顺序无关。比如你在for循环中让三个精灵依次执行动作,引擎并不会因为你的代码执行顺序而去执行动作实现,而是每执行一次for循环时,会同时执行这三个精灵的动作。这又回到了刚刚所谈的“如何让一个精灵去通知另一个精灵我已做完动作,该你去执行动作了”的问题。
在cocos2d-x中,提供了四种回调函数包装器来帮我们解决这个问题。
下面来具体介绍下斗地主发牌动作特效的实现。
玩过手机斗地主的朋友都知道,发牌时会先依次发给左上方和右上方两个电脑玩家看不见内容的纸牌,然后在发给用户一张可以看见具体内容的纸牌,这实际上就包含了至少两个精灵。一张背面牌精灵先移动到左上方,再回到原点,然后移到右上方(这时可设置为不可见),再将显示具体牌面的精灵(实际上会有很多张,可以放到一个CCArray中维护)移动到下方用户的选牌区域。这个过程仅仅是给用户发了一张纸牌,继续发牌可以使用递归调用,直到发够牌数后再跳出函数。实现的具体代码如下:
void GameScene::dispatchCardsCallback(CCNode *pSender, void* args) { switch ((int)args) { case 1: { //dispath left moveBackCard->setPosition(ccp(m_winSize.width / 2, m_winSize.height / 2)); CCMoveBy *leftMoveBy = CCMoveBy::create(0.1f, ccp(100 - m_winSize.width / 2, m_winSize.height / 2 - 127)); CCSequence *leftSequence = CCSequence::create(leftMoveBy, CCCallFuncND::create(this, callfuncND_selector(GameScene::dispatchCardsCallback), (void*)2), NULL); moveBackCard->runAction(leftSequence); break; } case 2: { //dispatch right moveBackCard->setPosition(ccp(m_winSize.width / 2, m_winSize.height / 2)); CCMoveBy *rightMoveBy = CCMoveBy::create(0.1f,ccp(m_winSize.width / 2 - 100, m_winSize.height / 2 - 127)); CCSequence *rightSequence = CCSequence::create(rightMoveBy, CCCallFuncND::create(this, callfuncND_selector(GameScene::dispatchCardsCallback), (void*)3), NULL); moveBackCard->runAction(rightSequence); break; } case 3: { //dispatch for user CCMoveTo *moveAction = CCMoveTo::create(0.1f, ccp(100 + (m_mutex + 1) * 30, 70)); CCSequence *sequence =CCSequence::create(moveAction, CCCallFuncND::create(this, callfuncND_selector(GameScene::dispatchCardsCallback), (void*)1), NULL); if (m_mutex < 17) { (dynamic_cast<CardSprite *>(m_array->objectAtIndex(m_mutex++)))->runAction(sequence); } else { this->removeChild(moveBackCard); this->removeChild(backCard); adjustCard(m_array); //对纸牌精灵进行排序 showCallScore(); //显示叫分Menu } break; } default: break; } }在上述函数中,我通过 callfuncND_selector多传递了一个int型变量(需要给它转成void*)用来标识该去给哪一个玩家(包括电脑和用户)来派发纸牌,当用户玩家发够17张牌的时候就跳出递归,与此同时去移除位于屏幕中央显示的纸牌精灵,并对用户发到的纸牌进行排序显示,然后显示叫分menu来提示用户进行叫分选择。由于初学cocos2d-x不久,对该引擎许多内部机制理解得还不是很透彻,也许实现该种效果还有更好的方式,欢迎大家踊跃留言指正!