Cocos2d-x制作《单机斗地主》源码解剖3:玩家类的解剖

2015年03月24日 08:41 0 点赞 0 评论 更新于 2025-11-21 18:21

在本次剖析中,我们将深入探究《单机斗地主》游戏中玩家类的实现。下面直接给出代码并进行详细分析。

头文件

class Player : public CCObject {
public:
// 构造函数
Player();
// 析构函数
~Player();
// 设置牌的位置
void updatePkWeiZhi();

private:
// 是否为地主
CC_SYNTHESIZE(bool, m_isDiZhu, IsDiZhu);
// 是否已叫地主
CC_SYNTHESIZE(bool, m_isCall, Call);
// 叫地主的分数
CC_SYNTHESIZE(int, m_iCallNum, CallNum);
// 手里拥有的扑克牌
CC_SYNTHESIZE(CCArray*, m_arrPk, ArrPk);
// 牌在桌面的初始位置
CC_SYNTHESIZE(CCPoint, m_point, Point);
// 玩家种类: 0为玩家,1为电脑,2为显示的三张牌, 3为玩家要出的牌,4为电脑1要出的牌,5为电脑2要出的牌
CC_SYNTHESIZE(int, m_iPlayerClass, PlayerClass);
// 保存牌型
std::vector<PaiXing> m_vecPX;
// 玩家是否出牌 true: 出 false: 不出
CC_SYNTHESIZE(bool, m_isOutPk, IsOutPk);
};

源文件

void Player::updatePkWeiZhi() {
// 获取屏幕可见尺寸
CCSize size = CCDirector::sharedDirector()->getVisibleSize();
int x, y;

// 计算玩家牌和出的牌的初始位置
if (m_iPlayerClass == 0 || m_iPlayerClass == 3) {
// 玩家或玩家要出的牌的初始位置计算
x = size.width / 2 - ((m_arrPk->count() - 1) * pkJianJu + pkWidth) / 2;
y = m_point.y;
} else if (m_iPlayerClass == 1 || m_iPlayerClass == 4 || m_iPlayerClass == 5) {
// 电脑玩家或电脑要出的牌的初始位置
x = m_point.x;
y = m_point.y;
} else if (m_iPlayerClass == 2) {
// 显示的三张牌的初始位置计算
x = size.width / 2 - (m_arrPk->count() * pkWidth + (m_arrPk->count() - 1) * pkJianJu) / 2;
y = m_point.y;
}

int num = 0;
CCObject* object;

// 对牌进行排序
if (m_iPlayerClass != 3 && m_iPlayerClass != 4 && m_iPlayerClass != 5) {
for (int i = 0; m_arrPk->count() != 0 && i < m_arrPk->count() - 1; ++i) {
for (int j = 0; j < m_arrPk->count() - 1 - i; ++j) {
Poker* pk1 = (Poker*)m_arrPk->objectAtIndex(j);
Poker* pk2 = (Poker*)m_arrPk->objectAtIndex(j + 1);
if (pk1->getNum() < pk2->getNum()) {
// 交换牌的位置,实现从大到小排序
m_arrPk->exchangeObject(pk1, pk2);
}
}
}
}

// 更新位置
CCARRAY_FOREACH(m_arrPk, object) {
Poker* pk = (Poker*)object;
if (m_iPlayerClass == 0 || m_iPlayerClass == 3) {
// 玩家或玩家要出的牌显示正面并设置位置
pk->showFront();
pk->setPosition(ccp(x + num * pkJianJu + pkWidth / 2, y));
} else if (m_iPlayerClass == 1 || m_iPlayerClass == 4 || m_iPlayerClass == 5) {
// 电脑玩家或电脑要出的牌显示正面
pk->showFront();
if (m_iPlayerClass == 1) {
// 电脑玩家的牌显示最后一张
pk->showLast();
}
pk->setPosition(ccp(x, y - num * pkJianJu));
} else if (m_iPlayerClass == 2) {
// 显示的三张牌设置位置
pk->setPosition(ccp(x + num * pkJianJu + num * pkWidth + pkWidth / 2, y));
}
++num;
}

// 改变牌的z值或牌的触摸优先级
int i = m_arrPk->count() - 1;
CCARRAY_FOREACH(m_arrPk, object) {
Poker* pk = (Poker*)object;
// 改变z值
if (m_iPlayerClass == 1 || m_iPlayerClass == 4 || m_iPlayerClass == 5) {
// 电脑玩家或电脑要出的牌根据y坐标设置z值
pk->setZOrder(size.height - pk->getPositionY());
}
if (m_iPlayerClass == 0 || m_iPlayerClass == 3) {
// 玩家或玩家要出的牌根据x坐标设置z值
pk->setZOrder(pk->getPositionX());
}
// 改变优先级
/* Poker* pk1 = (Poker *)m_arrPk->objectAtIndex(i--);
pk->setTouchPriority(pk1->getPositionX()); */
}
}

代码解析

这个类中最核心的方法是 updatePkWeiZhi(),其主要功能是将玩家手中的牌从大到小进行排序,并将牌居中显示在屏幕上。

位置计算

根据 m_iPlayerClass 的不同取值,计算出不同类型玩家牌的初始位置。这确保了玩家、电脑玩家以及显示的三张牌都能正确地显示在屏幕上。

牌排序

对于非出牌阶段的牌(即 m_iPlayerClass 不为 3、4、5),使用冒泡排序算法将牌按照牌面数字从大到小进行排序。

位置更新

遍历玩家手中的所有牌,根据 m_iPlayerClass 的不同,设置牌的显示状态(正面、最后一张显示等)和位置。

改变z值和触摸优先级

改变牌的 z 值和触摸优先级是为了解决牌顺序改变后可能出现的覆盖问题。当牌的顺序发生变化时,底下的牌可能会覆盖到其他牌之上,通过调整 z 值和触摸优先级,可以确保牌的显示和交互正常。

综上所述,updatePkWeiZhi() 方法在整个玩家类中起着至关重要的作用,它保证了玩家手中牌的正确显示和交互。

作者信息

boke

boke

共发布了 3994 篇文章