嘿,今天又是實作環節!比起單純的範例,這個環節是希望把我們聊的模組/功能實際應用,讓大家看看應用在實際的程式上,這些函式/屬性會怎麼被運用。
雖然這個實例可能不一定大家都會嘗試,但如果可以,希望大家都能有個機會自己嘗試發想一個題目。如我們一開始講得一樣,有目的的學習,才會讓你在了解這些知識的時候更有方向、更有印象。
開始前一樣來看一下需求清單,今天要利用前面兩天的時間控制與鏡頭控制,來實現下面我們畫粗體的功能。
啊,如果讀者比對我們先前的清單會發現筆者多劃掉了下面這一項。
因為這項其實在我們有 runner button 的部分的當下就已經完成了,所以我先把它劃掉了。
今天預計要實作得這項外,我們會根據原始影片再加上一項:
為了最後的效果,筆者其實加了比上面列的東西更多,但不會刻意去提,畢竟主角是 Matter.js ,只會在有和 Matter.js 相關的功能上著墨多些,若對實作效果,可以另外留言詢問。
那麼事不宜遲,我們直接開始今天的部分。
今天 Demo 的原始碼有點進去的讀者應該會發現我拆成資料夾了,畢竟整個檔案內容越來越多,把所有 js 擠進去會讓後續整理或修改麻煩得多。目前筆者是 By feature 做分檔,重點的範例碼還是會複製出來,大家不用擔心。
首先我們看到視角控制的部分,邏輯上是要跟著鏡頭走,所以要做在 frameUpdate 的函式裡。
function frameUpdated()
{
var cameraSpeedByFrame = 1.5 * isZoomIn? 0.3 : 1;
Bounds.translate(render.bounds, {x:0,y:1.5});
checkClosedToAnyColorBlock(blockList, mainBall);
}
這裡面的 cameraSpeedByFrame 就是鏡頭每個 frame 移動的 y 座標距離,也就是移動速度,isZoomIn 會是我們稍後提到判斷是否目前是球靠近顏色釘、時間減緩流逝的狀態。當時間減緩流逝的時候,鏡頭的移動距離也要乘上對應的倍率,才不會發生鏡頭過於迅速的往下的問題。
這裡我們用的是 Day13 提到的 Bounds 模組裡的 translate,來以增量平移 bounds 的座標,我們平移的對象就是我們的 render.bounds ,也就是直接影響我們畫面顯示的 bounds。
到這行為止,其實每秒就已經會讓鏡頭垂直向下了,可以注意到我們 x 不要變動,這是刻意的,在原本的需求裡面鏡頭就只會垂直移動。
下一行的 checkClosedToAnyColorBlock 其實就是來判斷是否進入靠近距離的部分,距離靠近,就要觸發減緩時間流逝與聚光燈效果。
function checkClosedToAnyColorBlock(blockList,mainBall)
{
for(var i in blockList)
{
if(blockList[i])
{
var label = blockList[i].label;
if(label == "GoldBlock" || label == "RainbowBlock")
{
if(getDistanceWithPoints({x:blockList[i].position .x, y:blockList[i].position .y},{x:mainBall.position .x,y:mainBall.position .y}) < zoomInDistance)
{
triggerZoomIn();
isZoomIn = true;
return;
}
}
}
}
isZoomIn = false;
triggerZoomOut();
}
這邊是我們判斷距離的邏輯,會針對現存所有釘子做距離判斷,只要球和任一根顏色釘距離接近到定義距離內的時候,就會觸發 ZoomIn,並改變當下 isZoomIn狀態。如果沒有靠近任何一顆顏色釘,則解除 ZoomIn 狀態,回到正常狀態。
function triggerZoomIn()
{
engine.timing.timeScale = 0.3;
var mainBallX = mainBall.position.x;
var mainBallY = mainBall.position.y - render.bounds.min.y;
var backgroundString = "radial-gradient(10px 10px at " + mainBallX + "px " + mainBallY + "px, transparent 0, transparent 60px, rgba(0, 0, 0, 0.5) 65px)";
var style = document.getElementById("overlayDiv").style;
style.background = backgroundString;
style.display = "block";
}
function triggerZoomOut()
{
engine.timing.timeScale = 1;
var style = document.getElementById("overlayDiv").style;
style.display = "none";
}
這兩段就是處理緩慢時間流逝或復原時間流逝的部分,可以看到如果要緩慢的話我們會把時間流逝速度變成 0.3 倍,也就是我們上面同步對鏡頭做緩慢的倍率。要復原的話,就是把鏡頭速度變回 1 倍,時間流逝就會正常。
這邊稍稍提一下聚光燈的實作,是用一個 div 蓋在 canvas 上,透過 position : absolute 做到相疊,且插入 div 順序要在插入 canvas 之後。至於聚光燈的 css,相信 google 就會有各種實作了。最後就是把聚光燈的位置透過變數組成字串,在安到上層的 div style上,就能達成聚光在球上的效果。
要注意的是聚光燈的位置要扣掉鏡頭的 Y 軸向位移,否則會發現怎麼鏡頭一直在球的下方,可以看到我們 mainBallY 有減去 render.bounds.min.y ,也就是我們目前 render.bounds 的上方 y 邊界座標。
到這裡為止,除了一些美術,我們已經完全還原了當初我們的目標,也就是那個影片的效果。其實這個實作也算告一段落。但在影片中,球的落下是不可控的,我們後面有段加碼,也就是剩下的最後一個需求:讓滑鼠/點擊行為能和畫面互動,讓我們來控制/影響球的落下。
明天,讓我們來了解一下 Matter.js 裡面滑鼠怎麼和物體產生互動。