Qt:有效处理具有"大量像素图"的QGraphicsItems?(RTS)

Fez*_*vez 12 c++ qt qgraphicsview

我目前正在构建一个小型的实时策略2D引擎.而且我想知道如何处理那些最终会让我的屏幕变得更加精彩的精灵.

仅供参考,我不是针对任何AAA级别的,我只是想尝试实现一些机器学习方法.因此,我选择了魔兽争霸II放弃软件ISO,无耻地采取了一些图形,我在第一个问题上摔倒了.

http://img263.imageshack.us/img263/1480/footman.png

正如你上面所看到的,即使是魔兽争霸II的简单仆人也有大约50个精灵用于动画.这是很多.它会经常改变精灵.(黑线只是检查我的alpha通道是否正确)

因此,最后一个问题:如何有效地实现不断变化的QGraphicsObject?如何有效地实现反复更改其外观的QGraphicsItem?

我是否只是重载paint()QGraphicsPixmapItem 的方法并继续更改屏幕上使用的Pixmap?它会导致一些"口吃"吗?我听说有时候,创建一个所有像素图,隐藏所有像素图并在需要时复制它们是明智/可能的.(复制比其他操作便宜)还有其他任何聪明的想法吗?

感谢您的任何意见!(RTS引擎教程,复杂性等等......)

Den*_*ant 15

(我先从一般的想法开始,然后会有一个可能的Qt实现)

我不知道WCII精灵是如何存储的,但你应该使用精灵表(如果需要的话自己构建).与此工作表相关联,您将获得一些精灵的描述,该精灵至少包含动画列表,并且对于每个动画,它的标识符/名称以及帧列表.

您描述这些动画帧的详细程度取决于您,但必须至少包含要显示的精灵表的矩形.

作为一个例子,看看这个精灵表(显然没有优化,但举个例子,它没关系:)).这是相关的动画描述(第12到39行).所有动画都不包括在内,但你会明白这一点.

你可以看到"空闲"动画是由3帧构成的,这些子节点与精灵表中的前3帧相匹配.与子矩阵相关联,此示例中还有2个信息:

  • 持续时间:在移动到下一帧之前帧应显示多长时间?
  • 起源:框架的锚点是什么?

现在,你将如何在Qt中实现它?

动画的描述文件格式完全取决于您,但我建议使用一些层次结构文件格式.由于Qt提供了XML解析器,因此它可以是完美的.如果您习惯于提升并喜欢像JSon这样的轻量级格式,那么可以使用boost.ptree来解析无关紧要的XML/JSon文件,并使用通用接口从中提取数据.

对于图形表示,您必须使用一些类:

  • QPixmap:我们将从中绘制匹配动画帧的子矩形,
  • 一个QTimer来更新你的精灵,
  • 你自己的显示类,比如AnimatedSprite(从QGraphicsObject派生,你需要信号/插槽支持),
  • 和一个支持类,比如TimerProxy(派生自QObject).

我将从描述TimerProxy角色开始.它的作用是发送时间更新消息.它在这里是因为Qt Graphics对象不提供任何类型的"定时"更新(即更新(float dt),其中给出的dt是您最喜欢的时间单位).您可能想知道为什么我们使用代理类来处理时间.这是为了限制活跃QTimer的数量; 如果你有每个AnimatedSprite中的一个,你最终可能会有大量的计时器活跃,这显然是一个很大的禁忌.

所以它履行了两个角色:

  • 在场景的构建时间:所有AnimatedSprite都会将自己注册到它提供的信号,让我们将它命名为updateTime(int msecs),
  • 在场景运行时,它将启动一个QTimer,将超时设置为您需要的粒度(您可以继续使用16ms,接近60 fps).QTimer的信号超时()将与一个私有插槽相关联,这将启动updateTime(int msecs),其中msecs设置为您之前设置的计时器粒度.

现在,对于解决方案的核心:AnimatedSprite.该类具有以下角色:

  • 阅读和存储它需要的动画描述,
  • 启动,更新和停止动画
  • QGraphicScene上绘制活动精灵的帧

在初始化时,您应该给它以下信息:

  • TimerProxy的一个实例(由您的场景拥有,或拥有该场景的类).当提供此实例时,您只需将TimerProxy :: updateTime(int)信号连接到将更新当前动画的专用槽,
  • 持有spritesheet 的QPixmap,
  • 和动画描述

在运行时,更新方法如下所示:

  • 一个私有的timeUpdated(int)槽,它将检查当前动画的帧是否应该更新,并相应地更新它,
  • public动画方法,如startAnim(const QString&animName),它将更改当前动画,重置经过时间计数器,并更新当前子矩形以绘制以匹配新动画的第一帧.

timeUpdated(int)插槽中,您希望更新已用时间,并检查是否应使动画继续下一帧.如果是这样,只需将当前帧指针更新为新帧.

最后,要渲染,您只需重新实现QGraphicsItem :: paint(...)方法来绘制当前的子对象,它可能如下所示:

void AnimatedSprite::paint(QPainter *painter,
                           const QStyleOptionGraphicsItem * /*option*/,
                           QWidget * /*widget*/)
{
    painter->drawImage(mCurrentAnim.mCurrentFrame->mOrigin,
                       mSpriteSheet,
                       mCurrentAnim.mCurrentFrame->mSubRect);
}
Run Code Online (Sandbox Code Playgroud)

希望有所帮助(并且不是太大,哇:s)

  • 哇!谢谢!这就是我想要的那种框架! (3认同)