Cocos2d-x制作《单机斗地主》源码解剖2:洗牌和发牌
在第一讲里,我们创建了一副有顺序的扑克牌。现在,我们需要将这副牌打乱,在游戏中,这一操作俗称“洗牌”。
洗牌实现
以下是实现洗牌功能的代码:
bool GameScene::xiPai() {
bool isRet = false;
do {
for (int i = 0; i < 54; ++i) {
Poker* pk1 = (Poker*)m_arrPokers->randomObject();
Poker* pk2 = (Poker*)m_arrPokers->randomObject();
m_arrPokers->exchangeObject(pk1, pk2);
}
isRet = true;
} while (0);
return isRet;
}
上述代码的逻辑较为简单,通过循环 54 次,每次随机从牌堆中选取两张牌并交换它们的位置,以此将原先有序的牌堆打乱。
发牌实现
牌洗完之后,接下来就需要一张一张地向玩家发牌。以下是发牌功能的代码:
void GameScene::SendPk() {
Poker* pk;
if (m_iSendPk < 51 && m_isSend) { // 前51张牌发给玩家
pk = (Poker*)m_arrPokers->objectAtIndex(m_iSendPk);
if (m_iSendPk % 3 == 0) { // 给玩家发牌
MovePk(m_player, pk);
} else if (m_iSendPk % 3 == 1) { // 给电脑1发牌
MovePk(m_npcOne, pk);
} else if (m_iSendPk % 3 == 2) { // 给电脑2发牌
MovePk(m_npcTwo, pk);
}
++m_iSendPk;
m_isSend = false;
} else if (m_iSendPk > 50 && m_iSendPk < 54 && m_isSend) { // 留下三张地主牌
pk = (Poker*)m_arrPokers->objectAtIndex(m_iSendPk);
pk->showFront();
MovePk(m_Three, pk);
++m_iSendPk;
m_isSend = false;
} else if (m_iSendPk > 53) { // 牌发完分析电脑玩家的牌型
FenChaiNpcPai(m_npcOne);
FenChaiNpcPai(m_npcTwo);
m_iSendPk = 0;
m_iState = 1;
}
}
在这段代码中,我们使用 m_iSendPk 作为发牌的索引,根据不同的条件进行发牌操作:
- 当
m_iSendPk小于 51 且m_isSend为true时,将前 51 张牌依次发给玩家、电脑 1 和电脑 2。 - 当
m_iSendPk大于 50 且小于 54 且m_isSend为true时,将剩下的三张牌作为地主牌。 - 当
m_iSendPk大于 53 时,牌发完,调用FenChaiNpcPai函数分析电脑玩家的牌型,并重置发牌索引和游戏状态。
这里的 m_isSend 变量用于指示发给某一个玩家的牌动画是否完成。接下来,我们看一下 MovePk() 函数的代码:
void GameScene::MovePk(Player* play, Poker* pk) {
CCMoveTo* move;
CCCallFuncND* func;
float time = 0.05;
play->getArrPk()->addObject(pk); // 从一副牌中选择pk这张牌
move = CCMoveTo::create(time, play->getPoint());
func = CCCallFuncND::create(this, callfuncND_selector(GameScene::func), play);
CCSequence* sequence = CCSequence::create(move, func, NULL);
pk->runAction(sequence);
}
void GameScene::func(CCNode* pSender, void* pData) {
Player* play = (Player*)pData;
play->updatePkWeiZhi(); // 整理一个玩家手中的牌
m_isSend = true;
}
MovePk 函数用于将一张牌移动到指定玩家的位置。具体步骤如下:
- 将牌添加到玩家的牌堆中。
- 创建一个移动动作
CCMoveTo,将牌移动到玩家的指定位置。 - 创建一个回调函数
CCCallFuncND,在移动动作完成后调用func函数。 - 创建一个动作序列
CCSequence,将移动动作和回调函数组合起来,并让牌执行该动作序列。
在 func 函数中,调用 play->updatePkWeiZhi() 函数整理玩家手中的牌,并将 m_isSend 变量设置为 true,表示发给该玩家的牌动画已完成。
关于 play->updatePkWeiZhi() 函数的具体实现,我们将在下一篇文章中进行详细解释。