C++开发斗地主(QT)第二篇之牌的绘制与显示

这一节为什么讲的是牌的显示?因为我觉得要学习这些,必须与实际相结合。

一.单张牌显示控件 (CardWidget)

我的QT版本是5.12.5,编译器是MinGW32,没下载的请先下载QT,下载地址:http://download.qt.io/archive/qt/

新建一个项目名称为:Game_DDZ,点右键选 Add New,新建一个类 CardWidget ,继承类为:QWidget,path:为项目目录下的Widgets(所有自己写的控件都放这里)。

 

由于要重新绘制牌,将用到一些图片:

 

牌分红黑 和四个颜色其中J Q K 小王 大王图片不一样。

我对其中的图片命名为:

头文件CardWidget.h

#ifndef CARDWIDGET_H
#define CARDWIDGET_H

#include <QWidget>
//单张牌控件类
class CardWidget : public QWidget
{
    Q_OBJECT
public:
    explicit CardWidget(QWidget *parent = nullptr);
    explicit CardWidget(int num,int angle,bool isShow,QWidget *parent = nullptr);
    void setClick(bool b);
    void setSelected(bool b);
    int angle=0;//角度
    int num=0;//牌大小(3-17)
    int value=0;//值大小(1-54)
    int color=0;//花色(0-4)
    bool isShow;//是否显示(只有自己的牌显示,其它的牌打完显示)
    bool isClick=false;//是否点击了,牌向上升起
    bool isSelected=false;//是否选中(改变牌面变暗)
protected://重绘控件
    void paintEvent(QPaintEvent *event);

};

#endif // CARDWIDGET_H

源文件CardWidget.cpp

#include "cardwidget.h"
#include "qpainter.h"
#include "QMouseEvent"
#include "classes/cardai.h"
#include "qdebug.h"


//获取牌的颜色
int getMaskColor(int card)
{
    if(card>0&&card<=13)//梅花
    {
        return 0;
    }
    else if(card>13&&card<=26)//方块
    {
        return  1;
    }
    else if(card>26&&card<=39)//黑桃
    {
        return  2;
    }
    else if(card>39&&card<=52)//红桃
    {
        return  3;
    }
    return -1;
}


//基本构造函数
CardWidget::CardWidget(QWidget *parent) : QWidget(parent)
{

}
//带参数基本构造函数
//value 牌值 angle角度 isShow是否显示正面
CardWidget::CardWidget(int value,int angle,bool isShow,QWidget *parent): QWidget(parent)
{
    this->angle=angle;
    this->value=value;
    this->isShow=isShow;
}

//当牌左键点击是调用此函数 是要是位置上升还是回复到原样
void CardWidget::setClick(bool b)
{
    isClick=b;
    if(isClick)
    {
        if(angle==CarcAngle)//左边上家的牌 要向右边移动10像素
        {
             move(this->x()+10,this->y());
        }
        else if(angle==-CarcAngle)//右边下家的牌 要向左边移动10像素
        {
              move(this->x()-10,this->y());
        }
        else//自己的牌向上移30像素
        {
            move(this->x(),this->y()-30);
        }
    }
    else//回位
    {
        if(angle==CarcAngle)
        {
             move(this->x()-10,this->y());
        }
        else if(angle==-CarcAngle)
        {
              move(this->x()+10,this->y());
        }
        else
        {
            move(this->x(),this->y()+30);
        }
    }
    //更新重绘
    update();
}

//当牌选中时,鼠标没有松开时调用,颜色改变
void CardWidget::setSelected(bool b)
{
    isSelected=b;
     //更新重绘
    update();
}
//控件重绘
void CardWidget::paintEvent(QPaintEvent *event)
{
    QWidget::paintEvent(event);
    
    QPainter painter(this);//定义画板为自己背景,相当于VS中DC pDC
    //设置去毛边 高质量模式
    painter.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
    //透明覆盖
    painter.fillRect(this->rect(), QBrush(Qt::transparent));
    //获取牌值大小(3-17)
    this->num=getCardValue(value);
    //获取颜色(0-4)
    this->color=getMaskColor(value);
    //定义一个图像用来绘到背景
    QPixmap thisPix(width(),height());
    if(isShow)
    {
        if(num!=0)
        {
            thisPix.fill(Qt::transparent);//透明填充
            QPainter painter2;//重新定义一个画板为 thisPix
            painter2.begin(&thisPix);//画板开始
            //设置去毛边 高质量模式
            painter2.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
            //因为我图像大小是 122*150,为了能成比例,自适应缩放
            double dw=double(width())/122.00;
            double dh=double(height())/150.00;
            //3-2的绘制
            if(num>0&&num<16)
            {
                
                //这是数字绘制的矩形框 x y w h   相当于VS中的 RECT
                QRect rect=QRect(int(dw*8),int(dh*13),int(dw*23),int(dh*31));
                //颜色 黑 红 黑 红顺序(梅花 方块 黑桃 红桃)
                //字符串数组
                QStringList colors={"black.png","red.png","black.png","red.png"};
                QStringList imgbgs={"bg_4.png","bg_3.png","bg_2.png","bg_1.png"};
                QStringList imgbg1={"bg_4_4.png","bg_3_3.png","bg_2_2.png","bg_1_1.png"};
                //导入背景图像
                QPixmap img(":/images/cards/"+colors[color]);
                QPixmap img1(":/images/cards/"+imgbgs[color]);
                QPixmap img11(":/images/cards/"+imgbg1[color]);
                //如果不是 J Q K 绘入 3-10 A 2
                if(num<11 || num>13)
                {
                    painter2.drawPixmap(QRect(0,0,thisPix.width(),thisPix.height()),img1);
                    painter2.drawPixmap(rect,img,QRect(0,31*(num-3),23,31));
                }
                else//下面绘的J Q  K
                {
                    painter2.drawPixmap(QRect(0,0,thisPix.width(),thisPix.height()),img11);
                    QPixmap bgimg(":/images/cards/bg_"+QString::number(num)+".png");
                    painter2.drawPixmap(QRect(0,0,thisPix.width(),thisPix.height()),bgimg);
                    painter2.drawPixmap(rect,img,QRect(0,31*(num-3),23,31));
                }
            }
            else//下面是直接绘大小王
            {
                QPixmap bgimg(":/images/cards/card"+QString::number(num)+".png");
                painter2.drawPixmap(QRect(0,0,thisPix.width(),thisPix.height()),bgimg);
            }
            if(isSelected)//如果选中了,上面添加一层透明度为100的颜色
            {
                painter2.setBrush(QColor(44,44,44,100));
                painter2.drawRoundedRect(rect(), 15*dw, 15*dh);
            }
            painter2.end();//画板结束
        }

    }
    else//不显示直接绘牌的背面
    {
        thisPix=QPixmap(":/images/cards/background.png");

    }
    //如果角度不是0
    if(angle!=0)
    {
        QMatrix matrix;//定义一个矩阵
        matrix.rotate(angle);//旋转n度
        //运用到图像中去
        thisPix=thisPix.transformed(matrix, Qt::SmoothTransformation);
    }
     //绘制到背景上去
    painter.drawPixmap(this->rect(),thisPix);
}

 

代码的详细解释都写的很清楚,没有学过QT的也不难理解。

这里主要处理三个东西:

1.鼠标点击:

2.点击松开选中:

3.上家和下家的牌显示有角度:

这里用到了矩阵,我在程序里面的角度是120与-120度

        QMatrix matrix;//定义一个矩阵
        matrix.rotate(angle);//旋转n度
        //运用到图像中去
        thisPix=thisPix.transformed(matrix, Qt::SmoothTransformation);

有了牌的绘制,就可以生成牌了,在桌面上具体位置显示:

首先要得到54张牌,然后洗牌,抽牌谁先叫地主,

//获取一副随机牌
vector<int> CardGame::getCards()
{
    vector<int> cards;
    for(int i=1;i<=54;i++)
    {
        cards.push_back(i);
    }
    srand(uint32_t(time(nullptr)));
    //洗三次
    for(int n=0;n<3;n++)
    {
        for (uint32_t i=0;i<54;i++)
        {
            //随机交换
            swap(cards[i], cards[rand()%54]);
        }
    }
    return  cards;
}

 

下面是切牌:

    //游戏控制初始化
    cardGame->init();
    vector<int> CardValueArray=cardGame->getCards();
    //随机切牌
    //随机切牌
    srand(time(0));
    int seat=rand()%54;
    //获取首个发牌位置0-自己 1-右边 2-左边,以便确定谁先叫地主
    int startSeat=seat%13%3;
    cardGame->curSeatId=startSeat;
    int count=0;
    vector<int> tmpCards;
    //按切牌的位置重新排列牌
    while(count<54)
    {
        tmpCards.push_back(CardValueArray.at(seat));
        seat++;
        if(seat>53)
        {
            seat=0;
        }
        count++;
    }

seat就是位置,谁第一个叫地主,再按这个位置按顺序排列一下牌,假如抽到的是21 ,21%18%3=2  那上家就是最先叫地主,牌也按21-54,1-20重新排列一下,相当于现实中的切牌。

本篇就讲到这里,下一篇讲动画发牌和对桌面位置的计算。

  • 8
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
C++写的基于MFC界面的斗地主小游戏源码,内含详细注释,附带了简单的AI出牌规则,放出来供大家参考交流。vs2010编写,vs2015测试可用,理论上vs05及以上都可正常编译运行。 void Judge::MainFlow() { switch(DataCenter::Instance().GetPlayState()) { case EM_LandHolderBorn_PlayState: { //先检查是否已经问完了 //遍历玩家检查是否已经询问过了,如果已经都问过了,则设置叫分最高的为地主 BOOL bAllAsked = TRUE;//是否已经询问完了 vector & vecPlayer = DataCenter::Instance().GetPlayerList(); for (UINT i = 0; i m_nCurHighstScore) { m_nCurHighstScore = vecPlayer[i].GetLandOwerScore(); m_pToBeLandOwer = &vecPlayer;[i]; } if (vecPlayer[i].GetLandOwerScore() SetLandOwer(TRUE); } //然后根据情况执行询问流程 //如果地主已经产生,则跳入下一阶段 if (NULL != DataCenter::Instance().GetLandOwner()) { m_pCurPlayer = NULL; DataCenter::Instance().SetPlayState(EM_WaitPlayer_PlayState); MainFlow(); return; } //如果当前player为空,设置当前player为地主牌得主 if (m_pCurPlayer == NULL) { m_pCurPlayer = DataCenter::Instance().GetLandOwnerCardHolder(); } //对当前玩家执行地主问询 ASSERT(m_pCurPlayer); m_pCurPlayer->ExcuteCallLandOwer(); } break; case EM_WaitPlayer_PlayState: { //如果游戏已经结束,则执行结束逻辑 BOOL bLandOwerWin = FALSE; if (DataCenter::Instance().IsOver(bLandOwerWin)) { if (bLandOwerWin) { AfxMessageBox(_T("地主赢了!")); } else { AfxMessageBox(_T("佃户赢了!")); } DataCenter::Instance().SetPlayState(EM_WaitToStart_PlayState); //将所有玩家明牌 DataCenter::Instance().ShowAllPlayerCard(); RefreshView(); return; } //如果是出牌阶段而当前player为空,设置当前player为地主,并发予底牌 if (m_pCurPlayer == NULL) { m_pCurPlayer = DataCenter::Instance().GetLandOwner(); DataCenter::Instance().SendOutBottomCard(); RefreshView(); } ASSERT(m_pCurPlayer); m_pCurPlayer->ExcuteCallCardPlay(); } break; } } void Judge::CurPlayerCallScore(int nScore) { if (m_pCurPlayer == NULL) { ASSERT(FALSE); return; } //将玩家选择的分数设置给玩家 m_pCurPlayer->SetLandOwerScore(nScore); //如果当前玩家为空,直接返回 if(m_pCurPlayer == NULL) { return; } if (nScore == 3) { //如果玩家叫了三分,直接设为地主 m_pCurPlayer->SetLandOwer(TRUE); } else { //玩家叫的不是三分,则记下玩家叫的分数 m_pCurPlayer->SetLandOwerScore(nScore); } if (nScore == 0) { CString strWord; strWord.Format(_T("不叫")); m_pCurPlayer->Say(strWord); } else { CString strWord; strWord.Format(_T("%d分"), nScore); m_pCurPlayer->Say(strWord); } //玩家叫分后隐藏叫地主按钮 Judge::Instance().ShowCallLandOwerBtn(FALSE); //切换到下一个玩家,流程继续 SwitchToNextPlayer(); MainFlow(); }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值