使用HTML5開發Kinect體感游戲


https://tgideas.qq.com/webplat/info/news_version3/804/7104/7106/m5723/201612/538908.shtml

 

一、簡介
我們要做的是怎樣一款游戲?
  在前不久成都TGC2016展會上,我們開發了一款《火影忍者手游》的體感游戲,主要模擬手游章節《九尾襲來 》,用戶化身四代,與九尾進行對決,吸引了大量玩家參與。 表面上看,這款游戲與其它體感體驗無異,實際上,它一直運行於瀏覽器Chrome下,也就是說,我們只需要掌握前端相應技術,就可以開發基於Kinect的網頁體感游戲。


 
二、實現原理
實現思路是什么?
  使用H5開發基於Kinect的體感游戲,其實工作原理很簡單,由Kinect采集到玩家及環境數據,比如人體骨骼,使用某種方式,使瀏覽器可以訪問這些數據。

1、采集數據
  Kinect有三個鏡頭,中間鏡頭類似普通攝像頭,獲取彩色圖像。左右兩邊鏡頭則是通過紅外線獲取深度數據。我們使用微軟提供的SDK去讀取以下類型數據:

 

  • 色彩數據:彩色圖像;
  • 深度數據:顏色嘗試信息;
  • 人體骨骼數據:基於以上數據經計算,獲取到人體骨骼數據。

 


2、使瀏覽器可訪問到Kinect數據
我嘗試和了解過的框架,基本上是以socket讓瀏覽器進程與服務器進行通信 ,進行數據傳輸:
  • Kinect-HTML5 用C#搭建服務端,色彩數據、嘗試數據、骨骼數據均有提供;
  • ZigFu 支持H5、U3D、Flash進行開發,API較為完整,貌似收費;
  • DepthJS  以瀏覽器插件形式提供數據訪問;
  • Node-Kinect2 以Nodejs搭建服務器端,提供數據比較完整,實例較多。
我最終選用Node-Kinect2,雖然沒有文檔,但是實例較多,使用前端工程師熟悉的Nodejs,另外作者反饋比較快。
  • Kinect: 捕獲玩家數據,比如深度圖像、彩色圖像等;
  • Node-Kinect2: 從Kinect獲取相應數據,並進行二次加工;
  • 瀏覽器: 監聽node應用指定接口,獲取玩家數據並完成游戲開發。
 
三、准備工作
先得買個Kinect啊
1、系統要求:
這是硬性要求,我曾在不符合要求的環境下浪費太多時間。
  • USB3.0
  • 支持DX11的顯卡
  • win8及以上系統
  • 支持Web Sockets的瀏覽器
  • 當然Kinect v2傳感器是少不了的
 
2、環境搭建流程:
  1. 連接上Kinect v2
  2. 安裝 KinectSDK-v2.0
  3. 安裝 Nodejs
  4. 安裝 Node-Kinect2 
npm install kinect2

四、實例演示
說什么都不如給我一個例子!
如下圖所示,我們演示如何獲取人體骨骼,並標識脊椎中段及手勢:

1、服務器端
創建web服務器,並將骨骼數據發送到瀏覽器端,代碼如下:
var Kinect2 = require('../../lib/kinect2'),
	express = require('express'),
	app = express(),
	server = require('http').createServer(app),
	io = require('socket.io').listen(server);

var kinect = new Kinect2();
// 打開kinect
if(kinect.open()) {
	// 監聽8000端口
	server.listen(8000);
	// 指定請求指向根目錄
	app.get('/', function(req, res) {
		res.sendFile(__dirname + '/public/index.html');
	});
	// 將骨骼數據發送給瀏覽器端
	kinect.on('bodyFrame', function(bodyFrame){
		io.sockets.emit('bodyFrame', bodyFrame);
	});
	// 開始讀取骨骼數據
	kinect.openBodyReader();
}

  

2、瀏覽器端
瀏覽器端獲取骨骼數據,並用canvas描繪出來,關鍵代碼如下:
var socket = io.connect('/');
var ctx = canvas.getContext('2d');
socket.on('bodyFrame', function(bodyFrame){
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	var index = 0;
	// 遍歷所有骨骼數據
	bodyFrame.bodies.forEach(function(body){
		if(body.tracked) {
			for(var jointType in body.joints) {
				var joint = body.joints[jointType];
				ctx.fillStyle = colors[index];
				// 如果骨骼節點為脊椎中點
				if(jointType == 1) {
					ctx.fillStyle = colors[2];
				}
				ctx.fillRect(joint.depthX * 512, joint.depthY * 424, 10, 10);
			}
			// 識別左右手手勢
			updateHandState(body.leftHandState, body.joints[7]);
			updateHandState(body.rightHandState, body.joints[11]);
			index++;
		}
	});
});

  

很簡單的幾行代碼,我們便完成了玩家骨骼捕獲,有一定 javascript基礎的同學應該很容易能看明白,但不明白的是我們能獲取哪些數據?如何獲取?骨骼節點名稱分別是什么?而node-kienct2並沒有文檔告訴我們這些。

五、開發文檔
 
Node-Kinect2並沒有提供文檔,我將我測試總結的文檔整理如下:
1、服務器端能提供的數據類型;
kinect.on('bodyFrame', function(bodyFrame){}); //還有哪些數據類型呢?

 

 

2、骨骼節點類型
body.joints[11] // joints包括哪些呢?

 

 

 
             
            

 

 4、骨骼數據
body [object] {
  bodyIndex [number]:索引,允許6人
  joints [array]:骨骼節點,包含坐標信息,顏色信息
  leftHandState [number]:左手手勢
  rightHandState [number]:右手手勢
  tracked [boolean]:是否捕獲到
  trackingId

 
             
            

 

 

 

 2、服務器端
游戲需要玩家骨骼數據(移動、手勢),彩色圖像數據(某一手勢下觸發拍照),所以我們需要向客戶端發送這兩部分數據。值得注意的是,彩色圖像數據體積過大,需要進行壓縮。

var emitColorFrame = false;
io.sockets.on('connection', function (socket){
	socket.on('startColorFrame', function(data){
		emitColorFrame = true;
	});	
});
kinect.on('multiSourceFrame', function(frame){

	// 發送玩家骨骼數據
	io.sockets.emit('bodyFrame', frame.body);

	// 玩家拍照
	if(emitColorFrame) {
		var compression = 1;
		var origWidth = 1920;
		var origHeight = 1080;
		var origLength = 4 * origWidth * origHeight;
		var compressedWidth = origWidth / compression;
		var compressedHeight = origHeight / compression;
		var resizedLength = 4 * compressedWidth * compressedHeight;
		var resizedBuffer = new Buffer(resizedLength);
		// ...
		// 照片數據過大,需要壓縮提高傳輸性能
		zlib.deflate(resizedBuffer, function(err, result){
			if(!err) {
				var buffer = result.toString('base64');
				io.sockets.emit('colorFrame', buffer);
			}
		});			
		emitColorFrame = false;
	}
});
kinect.openMultiSourceReader({
	frameTypes: Kinect2.FrameType.body | Kinect2.FrameType.color
});

  

3、客戶端
客戶端業務邏輯較復雜,我們提取關鍵步驟進行講解。
3.1、用戶拍照時,由於處理的數據比較大,為防止頁面出現卡頓,我們需要使用web worker
(function(){
 importScripts('pako.inflate.min.js'); 

 var imageData;
 function init() {
 addEventListener('message', function (event) {
 switch (event.data.message) {
 case "setImageData":
 imageData = event.data.imageData;
 break;
 case "processImageData":
 processImageData(event.data.imageBuffer);
 break;
 }
 });
 }
 function processImageData(compressedData) {
 var imageBuffer = pako.inflate(atob(compressedData));
 var pixelArray = imageData.data;
 var newPixelData = new Uint8Array(imageBuffer);
 var imageDataSize = imageData.data.length;
 for (var i = 0; i < imageDataSize; i++) {
 imageData.data[i] = newPixelData[i];
 }
 for(var x = 0; x < 1920; x++) {
 for(var y = 0; y < 1080; y++) {
 var idx = (x + y * 1920) * 4;
 var r = imageData.data[idx + 0]; 
 var g = imageData.data[idx + 1]; 
 var b = imageData.data[idx + 2]; 
 }
 }
 self.postMessage({ "message": "imageReady", "imageData": imageData });
 }
 init();
})(); 

  

3.2、接投影儀后,如果渲染面積比較大,會出現白屏,需要關閉瀏覽器硬件加速。

3.3、現場光線較暗,其它玩家干擾,在追蹤玩家運動軌跡的過程中,可能會出現抖動的情況,我們需要去除干擾數據。(當突然出現很大位移時,需要將數據移除)

var tracks = this.tracks;
var len = tracks.length;

// 數據過濾
if(tracks[len-1] !== window.undefined) {
	if(Math.abs(n - tracks[len-1]) > 0.2) {
		return;
	}
}
this.tracks.push(n);

  

3.4、當玩家站立,只是左右少量晃動時,我們認為玩家是站立狀態。
// 保留5個數據
if(this.tracks.length > 5) {
	this.tracks.shift();
} else {
	return;
}

// 位移總量
var dis = 0;
for(var i = 1; i < this.tracks.length; i++) {
	dis += this.tracks[i] - this.tracks[i-1];
}
if(Math.abs(dis) < 0.01) {
	this.stand();
} else {
	if(this.tracks[4] > this.tracks[3]) {
		this.turnRight();
	} else {
		this.turnLeft();
	}
	this.run();
}

  

七、展望
 
1、使用HTML5開發Kinect體感游戲,降低了技術門檻,前端工程師可以輕松的開發體感游戲;
2、大量的框架可以應用,比如用JQuery、CreateJS、Three.js(三種不同渲染方式);
3、無限想象空間,試想下體感游戲結合webAR,結合webAudio、結合移動設備,太可以挖掘的東西了……想想都激動不是么!

評論

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM