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天的主要文章部分不会特别提到这个。

明天我们来看一下,怎麽样操作镜头的部分,让镜头运行可以更贴近影片中的样子。


<<:  Day12-Express 的部署

>>:  买菸赔菸 - 零股买卖

爬虫怎麽爬 从零开始的爬虫自学 DAY11 python列表基础篇

前言 各位早安,书接上回我们练习了字串跟变数的应用,今天我们要来认识列表 List python列表...

DAY 21- 讯息监别码 MAC

「不是那个MAC。 不对,也不是汉堡。」 MAC能吃吗? 先前我们介绍了数位签章,今天我们要介绍的是...

Day12 hover应用(一)

虽然与网页互动的过程会花费更多的时间,但它却是吸引人很重要的一项因素,因此在设计部落格版面的时候,有...

什麽是Vaadin - day01

Vaadin 简介 Vaadin 是一款由芬兰 Vaadin 公司所开发,用於建构网路应用程序和网站...

PHP Array Mapping

最近工作上常会需要对一个 array 做批次处理,所以整理一下 PHP 做 array mappin...