iT邦幫忙

2021 iThome 鐵人賽

DAY 12
0
Modern Web

在JS的世界碰碰撞撞乒乒乓乓!30天一起玩Matter.js!系列 第 12

Day12. 一起動手做彈珠台!(2)

今天要來實際運用前兩天讀的碰撞概念,來為我們的彈珠台加上碰到後的處理與效果。

如果忘了我們的實作目標,影片附上讓大家Recap一下。

另外也搬運我們的需求,列出我們要做的事。

  • 建立方釘顏色釘
  • 方釘顏色釘會是固定的
  • 建立圓球
  • 圓球需要自由落體,受到重力影響
  • 圓球要能和方釘碰撞產生彈跳
  • 圓球和顏色釘碰撞的時候要偵測到碰撞並變色
  • 鏡頭移動,上帶到下
  • 在開始前,畫面要是靜止的,直到我們發出Signal
  • 滑鼠和球體互動 (追加功能)

一樣,加上粗體的就是要做的項目。

今日的Demo
今日的Demo原始碼
https://ithelp.ithome.com.tw/upload/images/20210927/20142057KiBDZjAuW1.png

首先是顏色釘的產生,顏色釘與其他釘本來的差異只有在被撞到的時候識別,我們這邊用 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天的主要文章部分不會特別提到這個。

明天我們來看一下,怎麼樣操作鏡頭的部分,讓鏡頭運行可以更貼近影片中的樣子。


上一篇
Day11. 人與人之間偶有摩擦,物體與物體之間叫做碰撞 - Collision(下)
下一篇
Day13. 對面的女孩看過來,我們的鏡頭看過來 - Bounds
系列文
在JS的世界碰碰撞撞乒乒乓乓!30天一起玩Matter.js!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言