1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > vs编译QT开源项目--太阳神三国杀源码分析(四) 动画

vs编译QT开源项目--太阳神三国杀源码分析(四) 动画

时间:2019-04-09 06:54:31

相关推荐

vs编译QT开源项目--太阳神三国杀源码分析(四) 动画

太阳神三国杀中,每当玩家出杀或吃桃子时,就会有一个动画效果,使界面非常生动绚丽.现在我们就分析一下QT中动画的原理,及实现方式,这里我们只分析吃桃子时的动画效果实现.由于三国杀有多个在线玩家同时游戏,因此当一个玩家吃了桃子,会将这个消息发送给服务器,服务器在分别通知每个在线玩家,使玩家界面出现吃桃子的动画.现在我们来跟踪一下代码的执行流程.

在NativeClientSocket::init()成员函数中,关联QtcpSocket的readyRead信号与其处理槽函数:connect(socket, SIGNAL(readyRead()), this, SLOT(getMessage()));在getMessage()函数中,每接收一条消息就触发message_got信号,Client类的构造函数中关联这个信号connect(socket, SIGNAL(message_got(char*)), this, SLOT(processServerPacket(char*)));在processServerPacket函数中,判断消息如果不是通知或请求,则调用processReply方法.在processReply方法中,判断消息字符串的首个字符,并根据消息内容查找对应的处理函数,此时消息内容为"animate",因此转而调用animate方法.这里有个小技巧,因为服务端和客户端之间的通信内容时字符串,那么如何根据发送的字符串找到相应的处理函数呢?将函数的名称和函数地址映射存入了一个QHash<QString, Callback> callbacks;成员变量中,在Client类的构造函数中进行初始化.

callbacks["addHistory"] = &Client::addHistory;

callbacks["animate"] = &Client::animate;

callbacks["judgeResult"] = &Client::judgeResult;

callbacks["setScreenName"] = &Client::setScreenName;

callbacks["setFixedDistance"] = &Client::setFixedDistance;

callbacks["transfigure"] = &Client::transfigure;

callbacks["jilei"] = &Client::jilei;

callbacks["cardLock"] = &Client::cardLock;

callbacks["pile"] = &Client::pile; ...... ......

其中Callback的定义为:typedef void (Client::*Callback)(const QString &);在根据函数名称查找函数地址时,只需要使用QHash类的value方法获取,而后调用:

Callback callback = callbacks.value(method, NULL);

if(callback){

QString arg_str = arg;

(this->*callback)(arg_str);//注意调用方式为(this->*callback)(参数列表),表示调用的是一个类实例(this)的成员函数,因为callback为函数指针,加星号降引用(好像可不加星号而直接调用,没有测试过).

现在就可以进入Client::animate成员函数了.解析出服务端发送的名称,及参数,触发animated信号.

void Client::animate(const QString &animate_str){

QStringList args = animate_str.split(":");

QString name = args.takeFirst();

emit animated(name, args);

}

在RoomScene构造函数中,关联了animated信号及处理槽:connect(ClientInstance, SIGNAL(animated(QString,QStringList)), this, SLOT(doAnimation(QString,QStringList)));doAnimation函数首先定义了一个静态QMap变量,并在首次调用的时候进行设置,将一个字符串命令与一个函数地址向映射,原理与callbacks相同,实现延时加载的目标,提高软件启动速度.接着根据传入的name参数到map中查找对应的函数地址,并调用.

void RoomScene::doAnimation(const QString &name, const QStringList &args){

static QMap<QString, AnimationFunc> map;

if(map.isEmpty()){

map["peach"] = &RoomScene::doAppearingAnimation;

map["jink"] = &RoomScene::animatePopup;

map["nullification"] = &RoomScene::doMovingAnimation;

map["analeptic"] = &RoomScene::doAppearingAnimation;

map["fire"] = &RoomScene::doAppearingAnimation;

map["lightning"] = &RoomScene::doAppearingAnimation;

map["typhoon"] = &RoomScene::doAppearingAnimation;

map["lightbox"] = &RoomScene::doLightboxAnimation;

map["huashen"] = &RoomScene::doHuashen;

map["indicate"] = &RoomScene::doIndicate;

map["hpChange"] = &RoomScene::animateHpChange;

}

AnimationFunc func = map.value(name, NULL);

if(func)

(this->*func)(name, args);

}

根据name的值可知,接下来调用了RoomScene::doAppearingAnimation成员函数,转而又调用了setEmotion函数.

if(name == "analeptic"

|| name == "peach")

{

setEmotion(args.at(0),name);

return;

}

setEmotion首先根据参数who获取对应的座位对象photo,如果找到座位则在对手的座位上播放动画,否则在本地的控制区播放动画.

void RoomScene::setEmotion(const QString &who, const QString &emotion ,bool permanent){

Photo *photo = name2photo[who];

if(photo){

photo->setEmotion(emotion,permanent);

return;

}

PixmapAnimation * pma = PixmapAnimation::GetPixmapAnimation(dashboard,emotion);

if(pma)

{

pma->moveBy(0,- dashboard->boundingRect().height()/2);

pma->setZValue(8.0);

}

}

先看setEmotion方法,参数emotion是动画名称,对应image/system/emotion/目录中的一个子目录名称,permanent指示是否永久播放动画.emotion_item是QGraphicsPixmapItem类型的指针,设置其显示,在其上播放动画,如果不是永久播放的动画则使用QTimer在2秒后隐藏动画图元emotion_item.接着就调用PixmapAnimation的静态成员函数GetPixmapAnimation来播放动画了.注意PixmapAnimation类是从QGraphicItem继承的(class PixmapAnimation : public QObject,public QGraphicsItem),动画的原理就是在桌位上创建一个PixmapAnimation实例,并使用计时器定时触发调用paint方法,在其上绘制不同的图片.

void Photo::setEmotion(const QString &emotion, bool permanent){

this->permanent = permanent;

if(emotion == "."){

emotion_item->hide();

return;

}

QString path = QString("image/system/emotion/%1.png").arg(emotion);//这个图片路径如果不存在,则emotion_item不生效.

emotion_item->setPixmap(QPixmap(path));

emotion_item->show();

if(emotion == "question" || emotion == "no-question")

return;

if(!permanent)

QTimer::singleShot(2000, this, SLOT(hideEmotion()));

PixmapAnimation::GetPixmapAnimation(this,emotion);

}

GetPixmapAnimation是一个静态函数,首先创建一个新的PixmapAnimation实例pma,并设置其图片所在路径加载路径中的图片,设置动画播放的位置,最后调用基类QObject的startTimer函数设置计时器,每50毫秒触发一次,动画完成时触发QObject的deleteLater函数,延时删除自己(在对象退出消息循环后进行自我删除).

PixmapAnimation* PixmapAnimation::GetPixmapAnimation(QGraphicsObject *parent, const QString &emotion)

{

PixmapAnimation *pma = new PixmapAnimation();

pma->setPath(QString("image/system/emotion/%1/").arg(emotion));

if(pma->valid())

{

if(emotion == "slash_red" ||

emotion == "slash_black" ||

emotion == "thunder_slash" ||

emotion == "peach" ||

emotion == "analeptic")

{

pma->moveBy(pma->boundingRect().width()*0.15,

pma->boundingRect().height()*0.15);

pma->setScale(0.7);

}

else if(emotion == "no-success")

{

pma->moveBy(pma->boundingRect().width()*0.15,

pma->boundingRect().height()*0.15);

pma->setScale(0.7);

}

pma->moveBy((parent->boundingRect().width() - pma->boundingRect().width())/2,

(parent->boundingRect().height() - pma->boundingRect().height())/2);

{

if(emotion == "fire_slash")pma->moveBy(40,0);

}

pma->setParentItem(parent);

pma->startTimer(50);

connect(pma,SIGNAL(finished()),pma,SLOT(deleteLater()));

return pma;

}

else

{

delete pma;

return NULL;

}

}

接着就进入了timerEvent函数,这是一个QObject定义的虚函数,如果对象调用了startTimer则会定时触发timerEvent,这里将其重写.

void PixmapAnimation::timerEvent(QTimerEvent *)

{

advance(1);

}

advance成员函数累加current成员变量,记录当前帧,调用update方法触发paint函数的调用,如果帧数超过总帧数,则重置为0,并触发finished信号.

void PixmapAnimation::advance(int phase)

{

if(phase)current++;

if(current>=frames.size())

{

current = 0;

emit finished();

}

update();

}

paint函数则直接根据current成员变量值获取对应的帧图片,进行绘制.frames的定义为QList<QPixmap> frames;在PixmapAnimation::setPath函数中,将指定路径中的所有png图片都加载到frames中.为了提高图片加载速度,setPath函数中加载图片是调用GetFrameFromCache从缓存中查找图片,如果没有则从磁盘上加载并存入缓存,QPixmapCache是提供这个功能的核心类.

void PixmapAnimation::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)

{

painter->drawPixmap(0,0,frames.at(current));

}

QPixmap PixmapAnimation::GetFrameFromCache(const QString &filename){

QPixmap pixmap;

if(!QPixmapCache::find(filename, &pixmap)){

pixmap.load(filename);

if(!pixmap.isNull())

QPixmapCache::insert(filename, pixmap);

}

return pixmap;

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。