HTML5开发Kinect体感游戏的实例应用

一、简介
我们要做的是怎样一款游戏?
在前不久成都TGC2016展会上,我们开发了一款《火影忍者手游》的体感游戏,主要模拟手游章节《九尾袭来 》,用户化身四代,与九尾进行对决,吸引了大量玩家参与。 表面上看,这款游戏与其它体感体验无异,实际上,它一直运行于浏览器Chrome下,也就是说,我们只需要掌握前端相应技术,就可以开发基于Kinect的网页体感游戏。
二、实现原理
实现思路是什么?
使用H5开发基于Kinect的体感游戏,其实工作原理很简单,由Kinect采集到玩家及环境数据,比如人体骨骼,使用某种方式,使浏览器可以访问这些数据。
1、采集数据
Kinect有三个镜头,中间镜头类似普通摄像头,获取彩色图像。左右两边镜头则是通过红外线获取深度数据。我们使用微软提供的SDK去读取以下类型数据:
2、使浏览器可访问到Kinect数据
我尝试和了解过的框架,基本上是以socket让浏览器进程与服务器进行通信 ,进行数据传输:
我最终选用Node-Kinect2,虽然没有文档,但是实例较多,使用前端工程师熟悉的Nodejs,另外作者反馈比较快。
三、准备工作
先得买个Kinect啊
1、系统要求:
这是硬性要求,我曾在不符合要求的环境下浪费太多时间。
2、环境搭建流程:
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){}); //还有哪些数据类型呢?
| bodyFrame | 骨骼数据 |
| infraredFrame | 红外数据 |
| longExposureInfraredFrame | 类似infraredFrame,貌似精度更高,优化后的数据 |
| rawDepthFrame | 未经处理的景深数据 |
| depthFrame | 景深数据 |
| colorFrame | 彩色图像 |
| multiSourceFrame | 所有数据 |
| audio | 音频数据,未测试 |
2、骨骼节点类型
body.joints[11] // joints包括哪些呢?
| 节点类型 | JointType | 节点名称 |
| 0 | spineBase | 脊椎基部 |
| 1 | spineMid | 脊椎中部 |
| 2 | neck | 颈部 |
| 3 | head | 头部 |
| 4 | shoulderLeft | 左肩 |
| 5 | elbowLeft | 左肘 |
| 6 | wristLeft | 左腕 |
| 7 | handLeft | 左手掌 |
| 8 | shoulderRight | 右肩 |
| 9 | elbowRight | 右肘 |
| 10 | wristRight | 右腕 |
| 11 | handRight | 右手掌 |
| 12 | hipLeft | 左屁 |
| 13 | kneeLeft | 左膝 |
| 14 | ankleLeft | 左踝 |
| 15 | footLeft | 左脚 |
| 16 | hipRight | 右屁 |
| 17 | kneeRight | 右膝 |
| 18 | ankleRight | 右踝 |
| 19 | footRight | 右脚 |
| 20 | spineShoulder | 颈下脊椎 |
| 21 | handTipLeft | 左手指(食中无小) |
| 22 | thumbLeft | 左拇指 |
| 23 | handTipRight | 右手指 |
| 24 | thumbRight | 右拇指 |
3、手势,据测识别并不是太准确,在精度要求不高的情况下使用
| 0 | unknown | 不能识别 |
| 1 | notTracked | 未能检测到 |
| 2 | open | 手掌 |
| 3 | closed | 握拳 |
| 4 | lasso | 剪刀手,并合并中食指 |
4、骨骼数据
body [object] {
bodyIndex [number]:索引,允许6人
joints [array]:骨骼节点,包含坐标信息,颜色信息
leftHandState [number]:左手手势
rightHandState [number]:右手手势
tracked [boolean]:是否捕获到
trackingId
}
5、kinect对象
| on | 监听数据 |
| open | 打开Kinect |
| close | 关闭 |
| openBodyReader | 读取骨骼数据 |
| open**Reader | 类似如上方法,读取其它类型数据 |
六、实战总结
火影体感游戏经验总结
接下来,我总结一下TGC2016《火影忍者手游》的体感游戏开发中碰到的一些问题。
1、讲解之前,我们首先需要了解下游戏流程。
|
1.1、通过手势触发开始游戏 |
1.2、玩家化身四代,左右跑动躲避九尾攻击 |
|
1.3、摆出手势“奥义”,触发四代大招 |
1.4、用户扫描二维码获取自己现场照片 |
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、结合移动设备,太可以挖掘的东西了……想想都激动不是么!
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# HTML5
# Kinect体感游戏
# Kinect体感游戏的实现
# Kinect体感游戏的应用
# 的是
# 文档
# 客户端
# 四代
# 很简单
# 较多
# 比较大
# 过大
# 游戏开发
# 这是
# 给我
# 太多
# 如有
# 说什么
# 则是
# 遍历
# 左右手
# 这款
# 有一定
# 很容易
相关文章:
建站之星会员如何解锁更多建站功能?
义乌企业网站制作公司,请问义乌比较好的批发小商品的网站是什么?
制作网站建设的公司有哪些,网站建设比较好的公司都有哪些?
网站制作软件有哪些,制图软件有哪些?
c++23 std::expected怎么用 c++优雅处理函数错误返回【详解】
如何登录建站主机?访问步骤全解析
网站制作话术技巧,网站推广做的好怎么话术?
沈阳制作网站公司排名,沈阳装饰协会官方网站?
全景视频制作网站有哪些,全景图怎么做成网页?
常州企业网站制作公司,全国继续教育网怎么登录?
如何快速上传自定义模板至建站之星?
如何通过万网虚拟主机快速搭建网站?
免费ppt制作网站,有没有值得推荐的免费PPT网站?
重庆网站制作公司哪家好,重庆中考招生办官方网站?
大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?
如何在IIS中配置站点IP、端口及主机头?
如何在云主机上快速搭建多站点网站?
长春网站建设制作公司,长春的网络公司怎么样主要是能做网站的?
建站之星导航配置指南:自助建站与SEO优化全解析
高端建站如何打造兼具美学与转化的品牌官网?
网站制作大概多少钱一个,做一个平台网站大概多少钱?
番禺网站制作公司哪家值得合作,番禺图书馆新馆开放了吗?
微信推文制作网站有哪些,怎么做微信推文,急?
制作网站外包平台,自动化接单网站有哪些?
如何制作网站标识牌,动态网站如何制作(教程)?
定制建站平台哪家好?企业官网搭建与快速建站方案推荐
打鱼网站制作软件,波克捕鱼官方号怎么注册?
家具网站制作软件,家具厂怎么跑业务?
深圳 网站制作,深圳招聘网站哪个比较好一点啊?
建站之星代理如何获取技术支持?
,交易猫的商品怎么发布到网站上去?
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
零服务器AI建站解决方案:快速部署与云端平台低成本实践
如何自定义建站之星模板颜色并下载新样式?
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
潮流网站制作头像软件下载,适合母子的网名有哪些?
小建面朝正北,A点实际方位是否存在偏差?
如何配置IIS站点权限与局域网访问?
网站制作服务平台,有什么网站可以发布本地服务信息?
如何快速重置建站主机并恢复默认配置?
如何选择适配移动端的WAP自助建站平台?
南京网站制作费用,南京远驱官方网站?
陕西网站制作公司有哪些,陕西凌云电器有限公司官网?
建站主机如何安装配置?新手必看操作指南
如何在Golang中使用encoding/gob序列化对象_存储和传输数据
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
南京做网站制作公司,南京哈发网络有限公司,公司怎么样,做网页美工DIV+CSS待遇怎么样?
建站主机服务器选购指南:轻量应用与VPS配置解析
如何选择适合PHP云建站的开源框架?
如何解决ASP生成WAP建站中文乱码问题?
*请认真填写需求信息,我们会在24小时内与您取得联系。