使用Qt动画框架设计角色的二维动画

Qt的动画框架是Qt4.6新添加的一个重要的特性,有了它,开发人员可以制作激动人心的动画界面,而不必局限于单调的固定窗口了,可以说,Qt动画框架是其它界面库少见的功能,它带来的是Qt的一大优势。最近我花了大概三、四天的时间研究Qt动画框架的内容,这让我感到Qt人员精心的设计给了我们一套非常规范并且易懂的代码,如果另外的一名开发者也对Qt动画框架有所了解,那么他可以毫不费力地看懂我的代码,并且从代码中了解设计思想。

演示程序的下载地址:这里

源代码的下载地址:这里

下面是程序的截图:

这里我使用了大家非常熟知的初音ミク来演示,我们可以通过【w】【s】【a】【d】键控制它的行动,不过呢,这个程序还是有一些小问题的,以后有时间再来改进。

该如何介绍呢?首先为了了解角色动画是如何进行的,还是了解一下Qt状态机框架。这里是英文的介绍,而这里呢,是英文介绍的翻译。

在初步了解Qt状态框架之后,我介绍一下最简单的二维动画:行走动画。其实演示程序所用的是初音ミク的行走图:如下图所示:

我们应用Qt的图形 – 视图框架(Graphics – View Framework)来绘制二维图形,这很简单。在成功之后我就想,如何才能让初音ミク动起来呢?在多天的尝试和失败后,我绘制了一个有限状态图。

在这个图中,站立和行走是状态机中的两个状态,它们之间可以相互转换。这样的转换通过按键的响应来完成。在Qt中有一个专门负责按键响应并且用于状态之间转换的类:QKeyEventTransition。它通过绑定一个QWidget来获取它的按键事件,并且一旦满足相应的按键条件就开始进行状态之间的转换。所以角色的行走动画可以这样制作:首先角色处于站立状态,当它捕获到了上下左右键,一个 QkeyEventTransition的对象指针负责将其转换成行走状态。转换附带了两个动画,一个是角色的帧动画,也就是初音ミク的一帧一帧的运动,另一个是角色的位置动画,让角色的位置发生变化。不可能让我们的ミク原地踏步吧!转换完毕后角色实际上进入了行走的状态,通过设定另外一个QAbstractTransition的指针来让角色从行走状态迅速地回到站立状态,这样就可以等待下一次按键的响应。

在我的实现中,让Character类来实现这样的功能,它是GameObject的子类,而GameObject类则是所有游戏对象的基类。一般来说角色(Character)基本上都有行走的功能,而同属于GameObject的其它物体则没有,比如说树木。

下面是Character.h的内容:

#ifndef CHARACTER_H
#define CHARACTER_H
#include <QRect>
#include <QKeyEventTransition>
#include <QPropertyAnimation>
#include <QWidget>
#include "GameObject.h"
/*---------------------------------------------------------------------------*/
class Character: public GameObject
{Q_OBJECTQ_PROPERTY( QRect FrameRect READ FrameRect WRITE setFrameRect )
public:explicit Character( const QString& fileName, GameObject* parent,QWidget* pWidget = 0 );// QGraphicsItem必须实现的函数QRectF boundingRect( void ) const;void paint( QPainter* pPainter, const QStyleOptionGraphicsItem* pOption,QWidget* pWidget = 0 );// 子类需要实现的函数virtual void Init( void );
public slots:void SlotSetDirection( void );
protected:// 枚举enum Direction// 即控制的按键{_Up_            = Qt::Key_W,_Down_          = Qt::Key_S,_Left_          = Qt::Key_A,_Right_         = Qt::Key_D};// 本类的函数void SetDirection( int dir );void SetFrameAnimation( int dir );void SetPosAnimation( int dir );// 本类的成员以及读/写函数DECLRARE_PROPERTY_WITH_GETTERS_AND_SETTERS( QRect, FrameRect )DECLRARE_PROPERTY_WITH_GETTERS_AND_SETTERS( int, AnimateTime )QState                     *m_pStandState, *m_pWalkState;QPropertyAnimation*         m_pPA;QPropertyAnimation*         m_pFPA[4];QWidget*                    m_pWidget;
};
/*---------------------------------------------------------------------------*/
#endif // CHARACTER_H

下面是Character.cpp的内容:

#include <QDebug>
#include <QPoint>
#include <QPainter>
#include <QEasingCurve>
#include <QPropertyAnimation>
#include <QSequentialAnimationGroup>
#include <QParallelAnimationGroup>
#include "Character.h"
/*---------------------------------------------------------------------------*/
Character::Character( const QString& fileName, GameObject* parent,QWidget* pWidget ): GameObject( parent )
{m_AnimateTime = 1000;// 即1秒m_pWidget = pWidget;m_Image.load( fileName );       // 载入图片m_FrameRect.setRect( 0, 0, m_Image.width( ) / 4, m_Image.height( ) / 4 );SetDirection( _Right_ );        // 朝向右Init( );
}
/*---------------------------------------------------------------------------*/
void Character::paint( QPainter* pPainter, const QStyleOptionGraphicsItem* pOption,QWidget* pWidget )
{pPainter->drawPixmap( QPoint( 0, -16 ), m_Image, m_FrameRect );GameObject::paint( pPainter, pOption, pWidget );
}
/*---------------------------------------------------------------------------*/
QRectF Character::boundingRect( void ) const
{return QRect( 0, -16, 32, 48 );// 没有其它特殊的效果将返回帧框
}
/*---------------------------------------------------------------------------*/
void Character::SetDirection( int dir )
{int rectY = 2;switch ( dir ){case _Up_: rectY = 3; break;case _Down_: rectY = 0; break;case _Left_: rectY = 1; break;case _Right_: rectY = 2; break;}m_FrameRect = QRect( m_FrameRect.x( ),rectY * m_Image.height( ) / 4,m_FrameRect.width( ),m_FrameRect.height( ) );
}
/*---------------------------------------------------------------------------*/
static qreal OneEasingFunction( qreal progress )
{Q_UNUSED( progress )return 1.0;
}
/*---------------------------------------------------------------------------*/
void Character::SetFrameAnimation( int dir )
{int rectY = 2;switch ( dir ){case _Up_: rectY = 3; break;case _Down_: rectY = 0; break;case _Left_: rectY = 1; break;case _Right_: rectY = 2; break;}QRect rect[4];QEasingCurve curve( QEasingCurve::Custom );curve.setCustomType( OneEasingFunction );for ( int i = 0; i < 4; ++i ){rect[i] = QRect( m_FrameRect.width( ) * i, rectY * m_Image.height( ) / 4,m_FrameRect.width( ), m_FrameRect.height( ) );}for ( int i = 0; i < 4; ++i ){m_pFPA[i]->setStartValue( rect[i] );m_pFPA[i]->setEndValue( rect[( i + 1 ) % 4] );m_pFPA[i]->setEasingCurve( curve );m_pFPA[i]->setDuration( m_AnimateTime / 4 );}
}
/*---------------------------------------------------------------------------*/
void Character::SetPosAnimation( int dir )// 设定位置动画
{QPointF point = pos( ), deltaPt;switch ( dir ){case _Up_:deltaPt = QPointF( 0.0, -32.0 ); break;case _Down_:deltaPt = QPointF( 0.0, 32.0 ); break;case _Left_:deltaPt = QPointF( -32.0, 0.0 ); break;case _Right_:deltaPt = QPointF( 32.0, 0.0 ); break;}m_pPA->setStartValue( point );point += deltaPt;m_pPA->setEndValue( point );
}
/*---------------------------------------------------------------------------*/
void Character::SlotSetDirection( void )
{QKeyEventTransition* eventTrans =qobject_cast<QKeyEventTransition*>( sender( ) );SetDirection( eventTrans->key( ) );SetFrameAnimation( eventTrans->key( ) );SetPosAnimation( eventTrans->key( ) );
}
/*---------------------------------------------------------------------------*/
void Character::Init( void )
{// 初始化站和走的状态m_pStandState = new QState( &m_Machine );m_pWalkState = new QState( &m_Machine );// 初始化和设定帧动画QSequentialAnimationGroup* pFSAG = new QSequentialAnimationGroup( this );for ( int i = 0; i < 4; ++i ){m_pFPA[i] = new QPropertyAnimation( this, "FrameRect", this );pFSAG->addAnimation( m_pFPA[i] );}// 初始化和设定位置动画m_pPA = new QPropertyAnimation( this, "pos", this );m_pPA->setDuration( m_AnimateTime );// 设定一个并发的动画组,同时调用这两个动画QParallelAnimationGroup* pPAG = new QParallelAnimationGroup( this );pPAG->addAnimation( pFSAG );pPAG->addAnimation( m_pPA );QKeyEventTransition* pKeyPressTrans[4];QKeyEventTransition* pKeyReleaseTrans[4];int keys[4] = { _Up_, _Down_, _Left_, _Right_ };for ( int i = 0; i < 4; ++i ){pKeyPressTrans[i] = new QKeyEventTransition( m_pWidget,QEvent::KeyPress,keys[i],m_pStandState );pKeyPressTrans[i]->setTargetState( m_pWalkState );pKeyPressTrans[i]->addAnimation( pPAG );// “站立?行走”转换需要动画connect( pKeyPressTrans[i], SIGNAL( triggered( ) ),this, SLOT( SlotSetDirection( ) ) );// 触发转换的时候调用槽pKeyReleaseTrans[i] = new QKeyEventTransition( m_pWidget,QEvent::KeyRelease,keys[i],m_pWalkState );pKeyReleaseTrans[i]->setTargetState( m_pStandState );}// 启动有限状态机m_Machine.setInitialState( m_pStandState );m_Machine.start( );
}

注意,在我的草图中还有一点,就是当转换被触发(triggered)时,会向Character类实例发送一个信号,通过SlotSetDirection()槽函数接收,用来针对不同的按键来设定不同的帧显示和进行不同的位置移动,也可以说槽函数设定了角色的朝向(帧框)、帧动画和位置动画。

程序运行基本正常,只是会出现“QEasingCurve: Invalid curve type 45”这样的错误(控制台下),看来是我自定义的插值函数搞的鬼。虽然我在论坛上提问了,但是目前还是没有人知道,这个问题就不了了之了。

使用Qt动画框架设计角色的二维动画相关推荐

  1. 使用Qt动画框架设计角色的二维动画(二)

    使用Qt动画框架设计角色的二维动画(二) 接上次的 日志. 上次发布demo虽然使用了大量的动画框架.有限状态机框架,但是仍有瑕疵.比如说在用户一直按下按键的时候角色会被"冻"住, ...

  2. 动画与计算机技术的区别,二维动画与计算机技术论文

    计算机技术是二维动画艺术设计的主要制作手法,掌握扎实的专业知识是熟练进行计算机二维动画艺术设计的前提.下面是学习啦小编给大家推荐的二维动画与计算机技术论文,希望大家喜欢! 二维动画与计算机技术论文篇一 ...

  3. 计算机网络应用二维动画,【单选题】二维动画属于计算机动画系统的第______级。 A. 一 B. 二 C. 三 D. 四...

    [单选题]二维动画属于计算机动画系统的第______级. A. 一 B. 二 C. 三 D. 四 更多相关问题 [单选] 包气带自上而下可分为().中间带和毛细水带. [单选] 据地下水流动状态,地下 ...

  4. 计算机二维动画的核心技术是什么意思,数字化技术在二维动画设计中的应用研究...

    摘要: 动画是艺术与技术的整合,动画的艺术性需要技术的支撑,技术需要艺术来呈现,动画技术的发展使动画导演和设计师完成作品的速度得到提高,艺术的表现效果上也越来越接近想象.随着计算机数字技术的发展,二维 ...

  5. 二维动画毕业论文参考文献精选

    随着二维动画制作技术的发展,二维动画在娱乐.广告.教育等很多领域都有着广泛的应用,已成为人们生活中必不可少的一部分.本位精选了200个中英文"二维动画毕业论文参考文献范例",希望对 ...

  6. 计算机二维动画实验原理,浅析计算机二维动画制作

    摘 要:最初的二维动画在制作上需要大量的人力还有较长的时间,相对的就是财力的消耗,在修改上也极度不便.计算机动画技术的发展提高了二维动画的制作效率,然而效率的提高却也让二维动画的质量变得不如以前.不过 ...

  7. 计算机动画类型中只有一维动画和二维动画,二维动画与计算机技术论文(2)

    二维动画与计算机技术论文篇二 <计算机二维动画艺术设计浅析> [摘要]计算机技术是二维动画艺术设计的主要制作手法,掌握扎实的专业知识是熟练进行计算机二维动画艺术设计的前提.本文概述了利用计 ...

  8. 计算机动画可分为二维和三维动画,二维动画和三维动画区别在哪

    原标题:二维动画和三维动画区别在哪 对于二维动画的印象,许多人可能是儿时记忆中的<葫芦娃>,对于三维动画,也许有人能想到<秦时明月>,可是除了直观上的区别,二维动画和三维动画具 ...

  9. 二维动画制作相对于传统宣传的优势

    **二维动画制作相对于传统宣传的优势**一个企业的发展离不开营销宣传的支持,文字.图片.广播等传统宣传方式在过去的营销中取得了一定的成绩.然而,随着近年来网络信息时代的快速发展,人们接收信息的方式和形 ...

最新文章

  1. 信息检索顶会SIGIR 2021大奖出炉!山东大学荣获最佳学生论文
  2. 在相同的后端上重新设计前端是什么效果? | 每日趣闻
  3. “当前不会命中断点 还没有为该文档加载任何符号”解决方法
  4. 如何在页面调用JS函数的代码
  5. python僵尸进程和孤儿进程_python学习笔记——孤儿进程和僵尸进程
  6. 大家好!欢迎来到我的博客!
  7. 游戏编程里面有哪些经典或者很酷的算法?
  8. Linux学习笔记-对父子进程直接通信基础与实例
  9. MySQL安装时Download界面为空,不显示需要下载的组件,MySQL Installer 8.0.28.0 Download界面为空解决方案
  10. latex数学公式符号 + markdown操作(图片的缩放、居中等)
  11. 读书笔记程序员的自我修养 0
  12. 阿里云配置 https
  13. 2015年第六届C/C++ B组蓝桥杯省赛真题
  14. 一个屌丝程序猿的人生(六十三)
  15. windows10家庭版修改中文用户名完美解决
  16. RSS阅读服务上线啦
  17. 运维之DNS域名解析服务基础概念与Bind9安装
  18. 简述一下延时函数delay怎么用
  19. Unity3D 5 官方教程:粒子系统 How-Tos
  20. 智能体温检测系统解决方案

热门文章

  1. 7.10 10.1-10.5
  2. 一文打尽前端 Base64 编码知识
  3. java二维数组遍历后转为一维数组_java数组(一)
  4. E2E E2E测试是什么+ E2E通信是什么 +E2E模块实现的功能
  5. 基于最新thinkphp5框架的旅游网站程序系统正式发布上线
  6. 外汇会计-概念-间接标价法(Indirect quotation)
  7. 【搜索】Playoff (dfs暴力枚举+剪枝)
  8. 浅说林黛玉《题帕诗》
  9. Unity MRTK使用详解(Htc vive+LeapMotion)
  10. 纯量产经验 | 谈谈目标检测中正负样本的问题