--- title: 為彈跳彩球添增其他功能 slug: Learn/JavaScript/Objects/Adding_bouncing_balls_features translation_of: Learn/JavaScript/Objects/Adding_bouncing_balls_features ---
在本文中,你將繼續使用前一篇文章的彈跳彩球展示程式,另外加入幾項有趣的新功能。
必備條件: | 在開始本文所提的實作之前,應先看過先前的相關文章。 |
---|---|
要點: | 測試 JavaScript 物件與 OO 架構的完整性。 |
在開始之前,請先複製先前文章所提供的 index-finished.html、style.css、main-finished.js 等檔案,儲存於本端磁碟的新資料夾中。
注意:你也可透過如 JSBin 或 Thimble 等網站進行此一實作。你可將 HTML、CSS、JavaScript 貼入相關線上編輯器之一。如果你所用的線上編輯器並未提供獨立的 JavaScript/CSS 面板,則可將之放入 HTML 頁面內的行內 <script>
/<style>
元素中。
彈跳彩球很有趣,但接著我們要加入使用者可控制的「邪惡圈」,在碰到彩球之後隨即吃掉彩球,添加更多互動性。也希望透過邪惡圈與彩球所繼承的通用 Shape()
物件,測試你的物件技術。最後還要加上計分功能,顯示尚未吃掉的彩球數量。
下方擷圖則讓你了解最終成品的樣子:
可先參考完成範例讓心裡有個底 (別偷看原始碼啊!)
下列段落將逐步說明。
首先將現有的 Ball()
建構子變更為 Shape()
建構子,以及新的 Ball()
建構子:
Shape()
建構子對 x
、y
、velX
、velY
屬性的定義方式,就如同 Ball()
建構子所用的方式。exists
屬性,用以追蹤球體是否存在於程式之中 (也就是尚未遭邪惡圈所吃掉)。此屬性必為布林值 (Boolean),初始值為 true
。Ball()
建構子應從 Shape()
建構子繼承 x
、y
、velX
、velY
、exists
等屬性。另必須將這些屬性定義為參數以利呼叫之。color
與 size
屬性各 1 組,且由於兩者均來自於原始的 Ball()
建構子之中,所以剛開始的隨機值亦須相同。Ball()
建構子的 prototype
與 constructor
。彩球的 draw()
、update()
、collisionDetect()
函式定義,均與之前完全相同。
到此為止可重新載入程式碼,搭配重新設計的物件也應該運作無誤。
再來見見這個壞蛋 — EvilCircle()
!這個遊戲要加入 1 個會吃球的邪惡圈,而且要透過繼承自 Shape()
的建構子來定義這個邪惡圈。你可能也想添增另個讓第二個玩家控制的圈圈,或許多加幾個由電腦控制的邪惡圈。當然,光一個邪惡圈並無法統治世界,但可為此遊戲增添不少樂趣。
EvilCircle()
建構子應繼承 Shape()
的 x、
y、
exists。
亦可定義自有的屬性如下:
color
— 'white'
size
— 10
velX
— 20
velY
— 20
再次提醒,請記得要將所繼承的屬性在建構子中定義為參數,並應正確設定 prototype
與 constructor
屬性。
EvilCircle()
應具備 4 個函式,如下:
draw()
此函式的功能與 Ball()
的 draw()
函式相同,就是在 canvas 上繪製物件實體;且運作的方式也類似,所以你可以複製 Ball.prototype.draw
定義來開始。接著要完成下列改變:
fillStyle
與 fill()
更新成 strokeStyle
與 stroke()
即可辦到。beginPath()
呼叫之後的某個地方設定 lineWidth
的值 (「3」就可以) 即可。checkBounds()
此函式功能就與 Ball()
的 update()
函式第一部分相同,負責邪惡圈是否跳出螢幕邊界之外並適時阻止。同樣的,你還是可以複製 Ball.prototype.update
定義來用,但須更改下列:
if()
statements, if the tests return true we don't want to update velX
/velY
; we want to instead change the value of x
/y
so the evil circle is bounced back onto the screen slightly. Adding or subtracting (as appropriate) the evil circle's size
property would make sense.setControls()
This method will add an onkeydown
event listener to the window
object so that when certain keyboard keys are pressed, we can move the evil circle around. The following code block should be put inside the method definition:
var _this = this; window.onkeydown = function(e) { if(e.keyCode === 65) { _this.x -= _this.velX; } else if(e.keyCode === 68) { _this.x += _this.velX; } else if(e.keyCode === 87) { _this.y -= _this.velY; } else if(e.keyCode === 83) { _this.y += _this.velY; } }
So when a key is pressed, the event object's keyCode property is consulted to see which key is pressed. If it is one of the four represented by the specified keycodes, then the evil circle will move left/right/up/down.
var _this = this;
in the position it is in? It is something to do with function scope.collisionDetect()
This method will act in a very similar way to Ball()
's collisionDetect()
method, so you can use a copy of that as the basis of this new method. But there are a couple of differences:
if
statement, you no longer need to check whether the current ball in the iteration is the same as the ball that is doing the checking — because it is not longer a ball, it is the evil circle! Instead, you need to do a test to see if the ball being checked exists (with which property could you do this with?). If it doesn't exist, it has already been eaten by the evil circle, so there is no need to check it again.if
statement, you no longer want to make the objects change color when a collision is detected — instead, you want to set any balls that collide with the evil circle to not exist any more (again, how do you think you'd do that?).Now we've defined the evil circle, we need to actually make it appear in our scene. To do this, you need to make some changes to the loop()
function.
setControls()
method. You only need to do these two things once, not on every iteration of the loop.draw()
, update()
, and collisionDetect()
functions for each one, make it so that these functions are only called if the current ball exists.draw()
, checkBounds()
, and collisionDetect()
methods on every iteration of the loop.To implement the score counter, follow the following steps:
p { position: absolute; margin: 0; top: 35px; right: 5px; color: #aaa; }
如果你是在某個課堂上操作這份作業,那麼請將成品交給您的老師 / 助教;如果您是自學者,在我們的專屬討論區或 Mozilla IRC 上的 #mdn 頻道都可以很輕鬆地找到人給予指教。記得先認真做一下習題,要怎麼收獲先那麼栽呀!
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_building_practice", "", "Learn/JavaScript/Objects")}}