示例代碼:
let imageScore = 0 const rgba = imageData.data for (let i = 0; i < rgba.length; i += 4) { const r = rgba[i] / 3 const g = rgba[i + 1] / 3 const b = rgba[i + 2] / 3 const pixelScore = r + g + b // 如果該像素足夠明亮 if (pixelScore >= PIXEL_SCORE_THRESHOLD) { imageScore++ } } // 如果明亮的像素數量滿足一定條件 if (imageScore >= IMAGE_SCORE_THRESHOLD) { // 產生了移動 }
在上述案例中,你也許會注意到畫面是『綠色』的。其實,我們只需將每個像素的紅和藍設置為 0,即將 RGBA 的 r = 0; b = 0
即可。這樣就會像電影的某些鏡頭一樣,增加了科技感和神秘感。
體驗地址>>
const rgba = imageData.data for (let i = 0; i < rgba.length; i += 4) { rgba[i] = 0 // red rgba[i + 2] = 0 // blue } ctx.putImageData(imageData, 0, 0)
將 RGBA 中的 R 和 B 置為 0
跟蹤“移動物體”
有了明亮的像素后,我們就要找出其 x 坐標的最小值與 y 坐標的最小值,以表示跟蹤矩形的左上角。同理,x 坐標的最大值與 y 坐標的最大值則表示跟蹤矩形的右下角。至此,我們就能繪制出一個能包圍所有明亮像素的矩形,從而實現跟蹤移動物體的效果。
找出跟蹤矩形的左上角和右下角
體驗鏈接>>
示例代碼:
function processDiff (imageData) { const rgba = imageData.data let score = 0 let pixelScore = 0 let motionBox = 0 // 遍歷整個 canvas 的像素,以找出明亮的點 for (let i = 0; i < rgba.length; i += 4) { pixelScore = (rgba[i] + rgba[i+1] + rgba[i+2]) / 3 // 若該像素足夠明亮 if (pixelScore >= 80) { score++ coord = calcCoord(i) motionBox = calcMotionBox(montionBox, coord.x, coord.y) } } return { score, motionBox } } // 得到左上角和右下角兩個坐標值 function calcMotionBox (curMotionBox, x, y) { const motionBox = curMotionBox || { x: { min: coord.x, max: x }, y: { min: coord.y, max: y } } motionBox.x.min = Math.min(motionBox.x.min, x) motionBox.x.max = Math.max(motionBox.x.max, x) motionBox.y.min = Math.min(motionBox.y.min, y) motionBox.y.max = Math.max(motionBox.y.max, y) return motionBox } // imageData.data 是一個含有每個像素點 rgba 信息的一維數組。 // 該函數是將上述一維數組的任意下標轉為 (x,y) 二維坐標。 function calcCoord(i) { return { x: (i / 4) % diffWidth, y: Math.floor((i / 4) / diffWidth) } }
在得到跟蹤矩形的左上角和右下角的坐標值后,通過 ctx.strokeRect(x, y, width, height)
API 繪制出矩形即可。
ctx.lineWidth = 6 ctx.strokeRect( diff.motionBox.x.min + 0.5, diff.motionBox.y.min + 0.5, diff.motionBox.x.max - diff.motionBox.x.min, diff.motionBox.y.max - diff.motionBox.y.min )
這是理想效果,實際效果請打開 體驗鏈接
擴展:為什么上述繪制矩形的代碼中的 x、y
要加 0.5
呢?一圖勝千言:
性能縮小尺寸
在上一個章節提到,我們需要通過對 Canvas 每個像素進行處理,假設 Canvas 的寬為 0
,高為 480
,那么就需要遍歷 0 * 480 = 307200
個像素。而在監測效果可接受的前提下,我們可以將需要進行像素處理的 Canvas 縮小尺寸,如縮小 10 倍。這樣需要遍歷的像素數量就降低 100
倍,從而提升性能。
體驗地址>>
示例代碼:
const motionCanvas // 展示給用戶看 const backgroundCanvas // offscreen canvas 背后處理數據 motionCanvas.width = 0 motionCanvas.height = 480 backgroundCanvas.width = backgroundCanvas.height = 48
尺寸縮小 10 倍
定時器
我們都知道,當游戲以『每秒60幀』運行時才能保證一定的體驗。但對于我們目前的案例來說,幀率并不是我們追求的第一位。因此,每 100 毫秒(具體數值取決于實際情況)取當前幀與前一幀進行比較即可。
另外,因為我們的動作一般具有連貫性,所以可取該連貫動作中幅度最大的(即“分數”最高)或最后一幀動作進行處理即可(如存儲到本地或分享到朋友圈)。
延伸
至此,用 Web 技術實現簡易的“移動監測”效果已基本講述完畢。由于算法、設備等因素的,該效果只能以 2D 畫面為基礎來判斷物體是否發生“移動”。而微軟的 Xbox、索尼的 PS、任天堂的 Wii 等游戲設備上的體感游戲則依賴于硬件。以微軟的 Kinect 為例,它為開發者提供了可跟蹤最多六個完整骨骼和每人 25 個關節等強大功能。利用這些詳細的人體參數,我們就能實現各種隔空的『手勢操作』,如畫圈圈詛咒某人。
下面幾個是通過 Web 使用 Kinect 的庫:
通過 Node-Kinect2 獲取骨骼數據
文章至此就真的要結束了,如果你想知道更多玩法,請關注 凹凸實驗室。同時,也希望大家發掘更多玩法。
參考資料
使用HTML5開發Kinect體感游戲
MOTION DETECTION WITH JAVASCRIPT
如有疑問請留言或者到本站社區交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com