今天要來實際運用前兩天讀的碰撞概念,來為我們的彈珠台加上碰到後的處理與效果。
如果忘了我們的實作目標,影片附上讓大家Recap一下。
另外也搬運我們的需求,列出我們要做的事。
一樣,加上粗體的就是要做的項目。
首先是顏色釘的產生,顏色釘與其他釘本來的差異只有在被撞到的時候識別,我們這邊用 lable 屬性來處理,Lable屬性在API文件中有這樣提到
An arbitrary String name to help the user identify and manage bodies.
也就是拿來讓我們識別用的,記得我們昨天是比較粗糙的用 Id 來辨識,但 Id 主要是自動安排的,用label 來在創建方形的時候附上指定 Tag,碰撞事件發生時,就能夠使用 label 來知道碰撞的對象。
mainBall.label = "MainBall";
另外今天我們要創造兩種顏色釘,一種是黃色,一種是紅色。
這邊我們對之前的扣做一個小重構,原本的釘跟新的兩種釘只差在顏色與 label 屬性,我們不需要複製三次產生的代碼,應該要抽出來變成函式。
function formBlockWithOptionAndCount(blockCount,blockOptions,blockCoordinateList,labelName)
{
for(var i=0; i<blockCount; i++)
{
var blockCoordinate = getRandomCoordinateForBlocks(blockCoordinateList);
var block = Bodies.rectangle(blockCoordinate.x, blockCoordinate.y, blockSize, blockSize, blockOptions);
blockCoordinateList.push(blockCoordinate);
block.label = labelName;
Composite.add(engine.world, [block]);
}
}
依據傳入的參數來決定產生的數量、樣式與附加 label。
產生釘子的數量一樣用外部丟入,為了製造隨機性,我們共用這個數量分配給各種顏色釘,同時顏色釘為隨機分配數量。
//input total count as blockCount
var rainbowBlockCount = Math.floor(blockCount/10) + Math.floor(Math.random()*3) - Math.floor(Math.random()*2);
var goldBlockCount = Math.floor(blockCount/6) + Math.floor(Math.random()*3) - Math.floor(Math.random()*2);
var normalBlockCount = blockCount - rainbowBlockCount - goldBlockCount;
再來是碰撞偵測,我們在engine上附加一個偵測碰撞開始的事件。
Events.on(engine, "collisionStart", collisionTriggered);
function collisionTriggered(e)
{
if(e.name == 'collisionStart' && e.pairs.length > 0)
{
if(e.pairs[0].bodyB.label =="EndingBlock") triggerEnding();
if(e.pairs[0].bodyB.label !="GoldBlock" && e.pairs[0].bodyB.label !="RainbowBlock") return;
if(e.pairs[0].bodyB.label =="GoldBlock") ballStatus += 1;
if(e.pairs[0].bodyB.label =="RainbowBlock") ballStatus += 2;
Composite.remove(engine.world, e.pairs[0].bodyB);
}
console.log(ballStatus);
if(ballStatus == 1)
{
mainBall.render.fillStyle = "#FFD700";
}
if(ballStatus >= 2)
{
mainBall.render.fillStyle = "red";
}
}
在碰撞觸發後,我們會在碰撞時偵測碰撞對象的 label。
碰撞事件的 pair 有一個規律, id 小的物體會是 bodyA, id 即是創造的先後順序,所以一開始我們的球是一開始最先創建的。這方便我們偵測被球碰撞物體的時候,都針對 bodyB 做判斷就是判斷被球撞的對象了。
所以在這段碰撞扣裡面,我們都是用 bodyB 做判斷,另外有一個全域 BallStatus 的來判斷目前的碰撞次數。
由於顏色釘會讓球變色,在設計上(影片中)碰一次顏色釘就會消失,我們透過其中的這行來處理物件移除。
Composite.remove(engine.world, e.pairs[0].bodyB);
另外我們掉落到最後想要看到一個結束訊息,這樣比較有感覺,所以這邊我們創一個比較特別的方形,用到一些選項 isSensor + isStatic, isSensor 不會對碰到的物體產生碰撞改變,只會有碰撞反應。可以想像成是碰撞區域的概念,同時要加上 isStatic 讓他不會移動,最後加上 lable 來讓我們在碰撞時能夠偵測。
function formEndingTrigger()
{
var wallBottom = Bodies.rectangle(canvasWidth/2, canvasHeigh+400, canvasWidth, 100, { isSensor:true, isStatic:true, label:"EndingBlock" });
Composite.add(engine.world, [wallBottom]);
}
function triggerEnding()
{
var resultString;
switch (ballStatus)
{
case 0 : resultString = "White"; break;
case 1 : resultString = "Gold"; break;
case 2 : resultString = "Rainbow"; break;
default : resultString = "Rainbow"; break;
}
alert("Your result is [" + resultString + " Ball] !" );
}
透過以上的代碼,我們成功讓顏色釘可以碰撞變色,模仿影片中基本機制。
對了讀者應該有發現,我們的畫面美術上跟影片有些差距,那個不在這次主題的探討範圍中,最後實作會用 sprite 來覆蓋,30天的主要文章部分不會特別提到這個。
明天我們來看一下,怎麼樣操作鏡頭的部分,讓鏡頭運行可以更貼近影片中的樣子。