本部分的代码出自:,但是有一些基于软件工程方面考虑的修改,例如常量放置的位置等。
前面说过,Qt 提供了自己的绘制系统,还提供了 Graphics View Framework。很明显,绘制图形和移动图形,是一个游戏的核心。对于游戏而言,将其中的每一个部分看做对象是非常合理的,也是相当有成效的。因此,我们选择 Graphics View Framework 作为核心框架。回忆一下,这个框架具有一系列面向对象的特性,能够让我们将一个个图形作为对象进行处理。同时,Graphics View Framework 的性能很好,即便是数千上万的图形也没有压力。这一点非常适合于游戏。
正如我们前面所说,Graphics View Framework 有三个主要部分:
在头文件中声明了MainWindow
。构造函数除了初始化成员变量,还设置了窗口的大小,并且需要对场景进行初始化:
值得说明的是最后一行代码。singleShot()
函数原型如下:
该函数接受三个参数,简单来说,它的作用是,在 msec 毫秒之后,调用 receiver 的 member 槽函数。在我们的代码中,第一个参数传递的是 0,也就是 0ms 之后,调用。这与直接调用this->adjustViewSize();
有什么区别呢?如果你看文档,这一段的解释很隐晦。文档中写到:“It is very convenient to use this function because you do not need to bother with a timerEvent or create a local QTimer object”,也就是说,它的作用是方便使用,无需重写timerEvent()
函数或者是创建一个局部的QTimer
对象。当我们使用QTimer::signleShot(0, …)
的时候,实际上也是对QTimer
的简化,而不是简单地函数调用。QTimer
的处理是将其放到事件列表中,等到下一次事件循环开始时去调用这个函数。那么,QTimer::signleShot(0, …)
意思是,在下一次事件循环开始时,立刻调用指定的槽函数。在我们的例子中,我们需要在视图绘制完毕后才去改变大小(视图绘制当然是在paintEvent()
事件中),因此我们需要在下一次事件循环中调用函数。这就是为什么我们需要用QTimer
而不是直接调用adjustViewSize()
。如果熟悉 flash,这相当于 flash 里面的callLater()
函数。接下来看看initScene()
和initSceneBackground()
的代码:
initSceneBackground()
函数看似很长,实际却很简单。首先我们创建一个边长TILE_SIZE
的,将其使用灰色填充矩形。我们没有设置边框颜色,默认就是黑色。然后将这个QPixmap
作为背景画刷,铺满整个视图。
现在我们的程序看起来是这样的: