StupidBeauty
Read times:932Posted at:Tue Jan 28 18:13:33 2014
- no title specified

Qt®4的Ruby教程翻译:第11章:撸一炮,Chapter 11: Giving It a Shot

Screenshot: Giving It a ShotScreenshot: Giving It a Shot


文件:

概述

在这个示例里,我们加入一个定时器,用于实现打炮的动画。

一行行地研究

cannon.rb

CannonField现在有了开炮的功能。

include Math

我们包含了Math,因为我们需要sin()cos()函数。

@timerCount = 0

@autoShootTimer = Qt::Timer.new(self)

connect(@autoShootTimer, SIGNAL('timeout()'),

self, SLOT('moveShot()'))

@shootAngle = 0

@shootForce = 0

我们初始化那些新的私有变量,再将Qt::Timer::timeout()信号连接到moveShot()信号槽上。每当定时器超时的时候,我们就会移动子弹。

timerCount用于记录自从打炮以来所经过的时间。在打炮时,shootAngle将会是加农炮的角度,shootForce将会是加农炮的力道。

def shoot()

if @autoShootTimer.isActive()

return

end;

@timerCount = 0

@shootAngle = @currentAngle

@shootForce = @currentForce

@autoShootTimer.start(5)

end

除非已经有一颗子弹在空中了,不然的话,这个函数就会发出一发炮弹。timerCount会被重置为0. shootAngleshootForce变量会被设置成加农炮当前的角度和力道。最后,我们启动定时器。

def moveShot()

region = Qt::Region.new(shotRect())

@timerCount += 1

shotR = shotRect()

if shotR.x() > width() || shotR.y() > height()

@autoShootTimer.stop()

else

region = region.unite(Qt::Region.new(shotR))

end

update(region)

end

moveShot()是用来移动子弹的信号槽,每隔5毫秒,当 Qt::Timer发射信号时就会执行一次。

它的任务就是:计算出新的位置;更新屏幕,让子弹显示在新的位置;并且在必要的时候停止定时器。

首先,我们创建一个记录有旧的子弹区域(shotRect())的Qt::Region。一个Qt::Region可用于保护任意类型的区域,这里我们用它来简化绘图过程。shotRect()返回子弹当前所在的位置的矩形区域。我们稍后会详细说明这一点。

然后我们增加timerCount,所产生的效果就是子弹沿着弹道向前移动一步。

下一步就是计算出新的子弹矩形区域。

如果子弹已经超出了本部件的右边缘或底部边缘的话,我们就停止定时器,否则我们将新的子弹区域(shotRect())加到Qt::Region上去。

最后,我们重绘Qt::Region。这会引起单个的绘图(paint)事件,在其中会对一个或两个需要更新的矩形进行更新。

def paintEvent(event)

painter = Qt::Painter.new(self)

paintCannon(painter)

if @autoShootTimer.isActive()

paintShot(painter)

end

painter.end()

end

绘图事件函数相对于前一章做了简化。大部分的逻辑代码都移动到了新的paintShot()paintCannon()函数中。

def paintShot(painter)

painter.setPen(Qt::NoPen)

painter.setBrush(Qt::Brush.new(Qt::black))

painter.drawRect(shotRect())

end

这个私有函数会绘制一个以黑色填充的矩形,以此来绘制出子弹。

我们这里略过paintCannon()的实现代码说明;它与前一章中的Qt::Widget::paintEvent()代码相同。

def shotRect()

gravity = 4.0

time = @timerCount / 20.0

velocity = @shootForce

radians = @shootAngle * 3.14159265 / 180.0

velx = velocity * cos(radians)

vely = velocity * sin(radians)

x0 = (@barrelRect.right() + 5.0) * cos(radians)

y0 = (@barrelRect.right() + 5.0) * sin(radians)

x = x0 + velx * time

y = y0 + vely * time - 0.5 * gravity * time * time

result = Qt::Rect.new(0, 0, 6, 6)

result.moveCenter(Qt::Point.new(x.round, height() - 1 - y.round))

return result

end

这个私有函数计算子弹的中心点,并且返回包含着子弹的矩形。它在计算过程中会用到最初的开炮力道和角度,以及timerCount,最后这个参数会随时时间而增加。

所使用的是在无摩擦力的重力场中物体位移的标准牛顿力学公式。出于简单性考虑,我们在此忽略了任何的爱因斯坦力学效应。

我们在一个y坐标向上增长的坐标系里计算中心点。计算完了中心点之后,我们构造出一个尺寸为 6 x 6 的Qt::Rect,然后将它的中心点移动到刚才计算到的位置处。在同一个操作中,我们将那个点的坐标换算回到本部件自身的坐标系统中(参考坐标系统)。

t11.rb

唯一的区别就是加入了Shoot按钮。

shoot = Qt::PushButton.new(tr('&Shoot'))

shoot.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold))

在构造函数中,我们就像之前的Quit 按钮一样,创建并且设置好Shoot 按钮。

connect(shoot, SIGNAL('clicked()'), cannonField, SLOT('shoot()'))

Shoot 按钮的clicked()信号连接到CannonField shoot()信号槽。

运行程序

加农炮现在可以打炮了,但是现在还没有射击目标。

练习

将子弹变成一个实心的圆形。[提示:可能会用上Qt::Painter::drawEllipse()。]

当有子弹在空中飞时,将加农炮变成另一种颜色。

[下一篇: 12章 ]

陈海茵

Your opinions
Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands