这一节为什么讲的是牌的显示?因为我觉得要学习这些,必须与实际相结合。
一.单张牌显示控件 (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重新排列一下,相当于现实中的切牌。
本篇就讲到这里,下一篇讲动画发牌和对桌面位置的计算。