Archive:Development/Tutorials/Qt4 Ruby Tutorial/Chapter 11 (zh TW): Difference between revisions
No edit summary |
m (AnneW moved page Development/Tutorials/Qt4 Ruby Tutorial/Chapter 11 (zh TW) to Archive:Development/Tutorials/Qt4 Ruby Tutorial/Chapter 11 (zh TW) without leaving a redirect: Obsolete) |
||
(2 intermediate revisions by one other user not shown) | |||
Line 26: | Line 26: | ||
'''<tt>CannonField</tt>''' 現在有射擊能力了。 | '''<tt>CannonField</tt>''' 現在有射擊能力了。 | ||
< | <syntaxhighlight lang="ruby"> | ||
include Math | include Math | ||
</ | </syntaxhighlight> | ||
我們含入(include)'''<tt>Math</tt>''',因為我們需要 '''<tt>sin()</tt>''' 和 '''<tt>cos()</tt>''' 函數。 | 我們含入(include)'''<tt>Math</tt>''',因為我們需要 '''<tt>sin()</tt>''' 和 '''<tt>cos()</tt>''' 函數。 | ||
< | <syntaxhighlight lang="ruby"> | ||
@timerCount = 0 | @timerCount = 0 | ||
Line 41: | Line 41: | ||
@shootAngle = 0 | @shootAngle = 0 | ||
@shootForce = 0 | @shootForce = 0 | ||
</ | </syntaxhighlight> | ||
我們初始化新的私有變數,並且連接 [http://doc.qt.nokia.com/latest/qtimer.html#timeout Qt::Timer::timeout()] 訊號到 '''<tt>moveShot()</tt>''' 槽。我們將在每次計時器超時(times out)時移動砲彈。 | 我們初始化新的私有變數,並且連接 [http://doc.qt.nokia.com/latest/qtimer.html#timeout Qt::Timer::timeout()] 訊號到 '''<tt>moveShot()</tt>''' 槽。我們將在每次計時器超時(times out)時移動砲彈。 | ||
Line 47: | Line 47: | ||
'''<tt>timerCount</tt>''' 會不斷追踪發射後經過的時間。'''<tt>shootAngle</tt>''' 是加農砲發射時的角度,而 '''<tt>shootForce</tt>''' 是加農砲發射時的力量。 | '''<tt>timerCount</tt>''' 會不斷追踪發射後經過的時間。'''<tt>shootAngle</tt>''' 是加農砲發射時的角度,而 '''<tt>shootForce</tt>''' 是加農砲發射時的力量。 | ||
< | <syntaxhighlight lang="ruby"> | ||
def shoot() | def shoot() | ||
if @autoShootTimer.isActive() | if @autoShootTimer.isActive() | ||
Line 58: | Line 58: | ||
@autoShootTimer.start(5) | @autoShootTimer.start(5) | ||
end | end | ||
</ | </syntaxhighlight> | ||
除非有砲彈在空中,否則這個函式會發射砲彈。'''<tt>timerCount</tt>''' 重設為零。'''<tt>shootAngle</tt>''' 和 '''<tt>shootForce</tt>''' 變數會被設定為當前加農砲的角度和力量。最後,我們啟動計時器。 | 除非有砲彈在空中,否則這個函式會發射砲彈。'''<tt>timerCount</tt>''' 重設為零。'''<tt>shootAngle</tt>''' 和 '''<tt>shootForce</tt>''' 變數會被設定為當前加農砲的角度和力量。最後,我們啟動計時器。 | ||
< | <syntaxhighlight lang="ruby"> | ||
def moveShot() | def moveShot() | ||
region = Qt::Region.new(shotRect()) | region = Qt::Region.new(shotRect()) | ||
Line 76: | Line 76: | ||
update(region) | update(region) | ||
end | end | ||
</ | </syntaxhighlight> | ||
'''<tt>moveShot()</tt>''' 是一個移動砲彈的槽,當[http://doc.qt.nokia.com/latest/qtimer.html Qt::Timer]啟動後,每5毫秒被呼叫一次。 | '''<tt>moveShot()</tt>''' 是一個移動砲彈的槽,當[http://doc.qt.nokia.com/latest/qtimer.html Qt::Timer]啟動後,每5毫秒被呼叫一次。 | ||
Line 92: | Line 92: | ||
最後,我們重繪 [http://doc.qt.nokia.com/latest/qregion.html Qt::Region]。這將發出只有一、兩個矩形需要更新的單一的繪圖事件。 | 最後,我們重繪 [http://doc.qt.nokia.com/latest/qregion.html Qt::Region]。這將發出只有一、兩個矩形需要更新的單一的繪圖事件。 | ||
< | <syntaxhighlight lang="ruby"> | ||
def paintEvent(event) | def paintEvent(event) | ||
painter = Qt::Painter.new(self) | painter = Qt::Painter.new(self) | ||
Line 103: | Line 103: | ||
painter.end() | painter.end() | ||
end | end | ||
</ | </syntaxhighlight> | ||
繪圖事件函式比起前面的章節已經簡化。大部分的邏輯操作已經移動到新的 '''<tt>paintShot()</tt>''' 和 '''<tt>paintCannon()</tt>''' 函式。 | 繪圖事件函式比起前面的章節已經簡化。大部分的邏輯操作已經移動到新的 '''<tt>paintShot()</tt>''' 和 '''<tt>paintCannon()</tt>''' 函式。 | ||
< | <syntaxhighlight lang="ruby"> | ||
def paintShot(painter) | def paintShot(painter) | ||
painter.setPen(Qt::NoPen) | painter.setPen(Qt::NoPen) | ||
Line 113: | Line 113: | ||
painter.drawRect(shotRect()) | painter.drawRect(shotRect()) | ||
end | end | ||
</ | </syntaxhighlight> | ||
這個私有函式畫出一個黑色填充矩形作為砲彈。 | 這個私有函式畫出一個黑色填充矩形作為砲彈。 | ||
Line 119: | Line 119: | ||
我們省略了 '''<tt>paintCannon()</tt>''' 的實作,它和前面章節 [http://doc.qt.nokia.com/latest/qwidget.html#paintEvent Qt::Widget::paintEvent()] 的重新實作相同。 | 我們省略了 '''<tt>paintCannon()</tt>''' 的實作,它和前面章節 [http://doc.qt.nokia.com/latest/qwidget.html#paintEvent Qt::Widget::paintEvent()] 的重新實作相同。 | ||
< | <syntaxhighlight lang="ruby"> | ||
def shotRect() | def shotRect() | ||
gravity = 4.0 | gravity = 4.0 | ||
Line 138: | Line 138: | ||
return result | return result | ||
end | end | ||
</ | </syntaxhighlight> | ||
這個私有函式計算砲彈的中心點,並且返回封裝砲彈的矩形。除了隨時間推移而增加的 '''<tt>timerCount</tt>''' 外,它還使用加農砲起始的力量和角度。 | 這個私有函式計算砲彈的中心點,並且返回封裝砲彈的矩形。除了隨時間推移而增加的 '''<tt>timerCount</tt>''' 外,它還使用加農砲起始的力量和角度。 | ||
Line 150: | Line 150: | ||
唯一增加的是 <strong>Shoot</strong> 按鈕。 | 唯一增加的是 <strong>Shoot</strong> 按鈕。 | ||
< | <syntaxhighlight lang="ruby"> | ||
shoot = Qt::PushButton.new(tr('&Shoot')) | shoot = Qt::PushButton.new(tr('&Shoot')) | ||
shoot.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) | shoot.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold)) | ||
</ | </syntaxhighlight> | ||
在建構子中,我們建立並設定了 <strong>Shoot</strong> 按鈕,就像我們為 <strong>Quit</strong> 按鈕作的。 | 在建構子中,我們建立並設定了 <strong>Shoot</strong> 按鈕,就像我們為 <strong>Quit</strong> 按鈕作的。 | ||
< | <syntaxhighlight lang="ruby"> | ||
connect(shoot, SIGNAL('clicked()'), cannonField, SLOT('shoot()')) | connect(shoot, SIGNAL('clicked()'), cannonField, SLOT('shoot()')) | ||
</ | </syntaxhighlight> | ||
連接 <strong>Shoot</strong> 按鈕的 '''<tt>clicked()</tt>''' 訊號,到'''<tt>CannonField</tt>''' 的 '''<tt>shoot()</tt>''' 槽。 | 連接 <strong>Shoot</strong> 按鈕的 '''<tt>clicked()</tt>''' 訊號,到'''<tt>CannonField</tt>''' 的 '''<tt>shoot()</tt>''' 槽。 |
Latest revision as of 15:57, 23 June 2013
Template:I18n/Language Navigation Bar (zh TW)
Template:TutorialBrowser (zh TW)
Giving It a Shot
檔案:
概覽
在這個範例中,我們引入一個計時器來實現動畫的射擊。
一行一行的瀏覽
CannonField 現在有射擊能力了。
include Math
我們含入(include)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() 槽。我們將在每次計時器超時(times out)時移動砲彈。
timerCount 會不斷追踪發射後經過的時間。shootAngle 是加農砲發射時的角度,而 shootForce 是加農砲發射時的力量。
def shoot()
if @autoShootTimer.isActive()
return
end;
@timerCount = 0
@shootAngle = @currentAngle
@shootForce = @currentForce
@autoShootTimer.start(5)
end
除非有砲彈在空中,否則這個函式會發射砲彈。timerCount 重設為零。shootAngle 和 shootForce 變數會被設定為當前加農砲的角度和力量。最後,我們啟動計時器。
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() 是一個移動砲彈的槽,當Qt::Timer啟動後,每5毫秒被呼叫一次。
它的工作是計算新的位置、更新螢幕上的砲彈到新的位置,並且在必要時停止計時器。
首先,我們建立一個 Qt::Region 來保留舊的 shotRect()。Qt::Region 有保留任何區域種類的能力,而我們將在這裡用它來簡化繪圖。shotRect() 會返回砲彈現在位置的矩形。這在稍後會詳細解釋。
然後我們遞增 timerCount,它影響砲彈沿彈道移動的每一步。
接下來,我們取得新的砲彈矩形。
如果砲彈已經越過 widget 右側或底部邊界,那麼我們停止計時器,否則我們加入新的 shotRect() 到 Qt::Region。
最後,我們重繪 Qt::Region。這將發出只有一、兩個矩形需要更新的單一的繪圖事件。
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 外,它還使用加農砲起始的力量和角度。
這個公式使用的是重力場中無摩擦運動的標準牛頓公式。為簡單起見,我們選擇忽略任何愛因斯坦效應(Einstein effect)。
我們在y坐標向上增加的坐標系統中計算中心點。在我們計算出中心點後,我們建構一個大小為6×6的 Qt::Rect 並移動它的中心點到算出的中心點之上。藉由相同的操作,我們把這個點轉換成 widget 的坐標系統(參閱坐標系統)。
唯一增加的是 Shoot 按鈕。
shoot = Qt::PushButton.new(tr('&Shoot'))
shoot.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold))
在建構子中,我們建立並設定了 Shoot 按鈕,就像我們為 Quit 按鈕作的。
connect(shoot, SIGNAL('clicked()'), cannonField, SLOT('shoot()'))
連接 Shoot 按鈕的 clicked() 訊號,到CannonField 的 shoot() 槽。
執行應用程式
加農砲可以射擊,但沒有東西會被射中。
練習
使砲彈變成一個填滿的圓。[提示:Qt::Painter::drawEllipse() 可能會有幫助。]
當砲彈在空中時,改變加農砲的顏色。