记得收藏【国际动画教程网】,赶快注册吧!
注册

合作站点账号登陆

QQ登录

只需一步,快速开始

扫一扫,访问微社区

快捷导航
查看: 35051|回复: 0
收起左侧

[HTML5/CSS3] 使用HTML5开发Kinect体感游戏[含7P]

[复制链接]
发表于 2017-8-27 22:57:20 | 显示全部楼层 |阅读模式
<article>
                                        <html>
<head></head>
<body>
  <h3><strong>一、简介</strong></h3>
  <p>我们要做的是怎样一款游戏?</p>
  <p>在前不久成都TGC2016展会上,我们开发了一款《火影忍者手游》的体感游戏,主要模拟手游章节《九尾袭来&nbsp;》,用户化身四代,与九尾进行对决,吸引了大量玩家参与。 表面上看,这款游戏与其它体感体验无异,实际上,它一直运行于浏览器Chrome下,也就是说,我们只需要掌握前端相应技术,就可以开发基于Kinect的网页体感游戏。</p>
  <h3><strong>二、实现原理</strong></h3>
  <p>实现思路是什么?</p>
  <p>使用H5开发基于Kinect的体感游戏,其实工作原理很简单,由Kinect采集到玩家及环境数据,比如人体骨骼,使用某种方式,使浏览器可以访问这些数据。</p>
  <p>1、采集数据</p>
  <p>Kinect有三个镜头,中间镜头类似普通摄像头,获取彩色图像。左右两边镜头则是通过红外线获取深度数据。我们使用微软提供的SDK去读取以下类型数据:</p>
  <ul>
   <li>色彩数据:彩色图像;</li>
   <li>深度数据:颜色尝试信息;</li>
   <li>人体骨骼数据:基于以上数据经计算,获取到人体骨骼数据。</li>
  </ul>
  <p>2、使浏览器可访问到Kinect数据</p>
  <p>我尝试和了解过的框架,基本上是以socket让浏览器进程与服务器进行通信 ,进行数据传输:</p>
  <ul>
   <li><a href="https://github.com/LightBuzz/Kinect-HTML5" rel="nofollow,noindex">Kinect-HTML5</a> &nbsp;用C#搭建服务端,色彩数据、尝试数据、骨骼数据均有提供;</li>
   <li><a href="http://zigfu.com/en/zdk/overview/" rel="nofollow,noindex">ZigFu</a> &nbsp;支持H5、U3D、Flash进行开发,API较为完整,貌似收费;</li>
   <li><a href="https://github.com/doug/depthjs" rel="nofollow,noindex">DepthJS</a> &nbsp; 以浏览器插件形式提供数据访问;</li>
   <li><a href="https://github.com/wouterverweirder/kinect2" rel="nofollow,noindex">Node-Kinect2</a> &nbsp;以Nodejs搭建服务器端,提供数据比较完整,实例较多。</li>
  </ul>
  <p>我最终选用Node-Kinect2,虽然没有文档,但是实例较多,使用前端工程师熟悉的Nodejs,另外作者反馈比较快。</p>
  <p style="text-align:center"></p>
  <ul>
   <li>Kinect: 捕获玩家数据,比如深度图像、彩色图像等;</li>
   <li>Node-Kinect2: 从Kinect获取相应数据,并进行二次加工;</li>
   <li>浏览器: 监听node应用指定接口,获取玩家数据并完成游戏开发。</li>
  </ul>
  <h3><strong>三、准备工作</strong></h3>
  <p>先得买个Kinect啊</p>
  <p>1、系统要求:</p>
  <p>这是硬性要求,我曾在不符合要求的环境下浪费太多时间。</p>
  <ul>
   <li>USB3.0</li>
   <li>支持DX11的显卡</li>
   <li>win8及以上系统</li>
   <li>支持Web Sockets的浏览器</li>
   <li>当然Kinect v2传感器是少不了的</li>
  </ul>
  <p>2、环境搭建流程:</p>
  <ol>
   <li>连接上Kinect v2</li>
   <li>安装&nbsp; <a href="https://download.microsoft.com/download/F/2/D/F2D1012E-3BC6-49C5-B8B3-5ACFF58AF7B8/KinectSDK-v2.0_1409-Setup.exe" rel="nofollow,noindex">KinectSDK-v2.0</a></li>
   <li>安装 <a href="https://nodejs.org/en/" rel="nofollow,noindex">Nodejs</a></li>
   <li>安装 Node-Kinect2&nbsp;</li>
  </ol>
  <pre>
<code class="language-java">npm install kinect2</code></pre>
  <h3><strong>四、实例演示</strong></h3>
  <p>说什么都不如给我一个例子!</p>
  <p>如下图所示,我们演示如何获取人体骨骼,并标识脊椎中段及手势:</p>
  <p style="text-align:center"></p>
  <p>1、服务器端</p>
  <p>创建web服务器,并将骨骼数据发送到浏览器端,代码如下:</p>
  <pre>
<code class="language-java">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();
}</code></pre>
  <p>2、浏览器端</p>
  <p>浏览器端获取骨骼数据,并用canvas描绘出来,关键代码如下:</p>
  <pre>
<code class="language-java">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++;
                }
        });
});</code></pre>
  <p>很简单的几行代码,我们便完成了玩家骨骼捕获,有一定 javascript基础的同学应该很容易能看明白,但不明白的是我们能获取哪些数据?如何获取?骨骼节点名称分别是什么?而node-kienct2并没有文档告诉我们这些。</p>
  <h3><strong>五、开发文档</strong></h3>
  <p>Node-Kinect2并没有提供文档,我将我测试总结的文档整理如下:</p>
  <p>1、服务器端能提供的数据类型;</p>
  <pre>
<code class="language-java">kinect.on('bodyFrame', function(bodyFrame){}); //还有哪些数据类型呢?</code></pre>
  <table>
   <tbody>
    <tr>
     <td>bodyFrame</td>
     <td>骨骼数据</td>
    </tr>
    <tr>
     <td>infraredFrame</td>
     <td>红外数据</td>
    </tr>
    <tr>
     <td>longExposureInfraredFrame</td>
     <td>类似infraredFrame,貌似精度更高,优化后的数据</td>
    </tr>
    <tr>
     <td>rawDepthFrame</td>
     <td>未经处理的景深数据</td>
    </tr>
    <tr>
     <td>depthFrame</td>
     <td>景深数据</td>
    </tr>
    <tr>
     <td>colorFrame</td>
     <td>彩色图像</td>
    </tr>
    <tr>
     <td>multiSourceFrame</td>
     <td>所有数据</td>
    </tr>
    <tr>
     <td>audio</td>
     <td>音频数据,未测试</td>
    </tr>
   </tbody>
  </table>
  <p>2、骨骼节点类型</p>
  <pre>
<code class="language-java">body.joints[11] // joints包括哪些呢?</code></pre>
  <table>
   <tbody>
    <tr>
     <td>节点类型</td>
     <td>JointType</td>
     <td>节点名称</td>
    </tr>
    <tr>
     <td>0</td>
     <td>spineBase</td>
     <td>脊椎基部</td>
    </tr>
    <tr>
     <td>1</td>
     <td>spineMid</td>
     <td>脊椎中部</td>
    </tr>
    <tr>
     <td>2</td>
     <td>neck</td>
     <td>颈部</td>
    </tr>
    <tr>
     <td>3</td>
     <td>head</td>
     <td>头部</td>
    </tr>
    <tr>
     <td>4</td>
     <td>shoulderLeft</td>
     <td>左肩</td>
    </tr>
    <tr>
     <td>5</td>
     <td>elbowLeft</td>
     <td>左肘</td>
    </tr>
    <tr>
     <td>6</td>
     <td>wristLeft</td>
     <td>左腕</td>
    </tr>
    <tr>
     <td>7</td>
     <td>handLeft</td>
     <td>左手掌</td>
    </tr>
    <tr>
     <td>8</td>
     <td>shoulderRight</td>
     <td>右肩</td>
    </tr>
    <tr>
     <td>9</td>
     <td>elbowRight</td>
     <td>右肘</td>
    </tr>
    <tr>
     <td>10</td>
     <td>wristRight</td>
     <td>右腕</td>
    </tr>
    <tr>
     <td>11</td>
     <td>handRight</td>
     <td>右手掌</td>
    </tr>
    <tr>
     <td>12</td>
     <td>hipLeft</td>
     <td>左屁</td>
    </tr>
    <tr>
     <td>13</td>
     <td>kneeLeft</td>
     <td>左膝</td>
    </tr>
    <tr>
     <td>14</td>
     <td>ankleLeft</td>
     <td>左踝</td>
    </tr>
    <tr>
     <td>15</td>
     <td>footLeft</td>
     <td>左脚</td>
    </tr>
    <tr>
     <td>16</td>
     <td>hipRight</td>
     <td>右屁</td>
    </tr>
    <tr>
     <td>17</td>
     <td>kneeRight</td>
     <td>右膝</td>
    </tr>
    <tr>
     <td>18</td>
     <td>ankleRight</td>
     <td>右踝</td>
    </tr>
    <tr>
     <td>19</td>
     <td>footRight</td>
     <td>右脚</td>
    </tr>
    <tr>
     <td>20</td>
     <td>spineShoulder</td>
     <td>颈下脊椎</td>
    </tr>
    <tr>
     <td>21</td>
     <td>handTipLeft</td>
     <td>左手指(食中无小)</td>
    </tr>
    <tr>
     <td>22</td>
     <td>thumbLeft</td>
     <td>左拇指</td>
    </tr>
    <tr>
     <td>23</td>
     <td>handTipRight</td>
     <td>右手指</td>
    </tr>
    <tr>
     <td>24</td>
     <td>thumbRight</td>
     <td>右拇指</td>
    </tr>
   </tbody>
  </table>
  <p>3、手势,据测识别并不是太准确,在精度要求不高的情况下使用</p>
  <table>
   <tbody>
    <tr>
     <td>0</td>
     <td>unknown</td>
     <td>不能识别</td>
    </tr>
    <tr>
     <td>1</td>
     <td>notTracked</td>
     <td>未能检测到</td>
    </tr>
    <tr>
     <td>2</td>
     <td>open</td>
     <td>手掌</td>
    </tr>
    <tr>
     <td>3</td>
     <td>closed</td>
     <td>握拳</td>
    </tr>
    <tr>
     <td>4</td>
     <td>lasso</td>
     <td>剪刀手,并合并中食指</td>
    </tr>
   </tbody>
  </table>
  <p>4、骨骼数据</p>
  <p>body [object] {</p>
  <p>bodyIndex [number]:索引,允许6人</p>
  <p>joints [array]:骨骼节点,包含坐标信息,颜色信息</p>
  <p>leftHandState [number]:左手手势</p>
  <p>rightHandState [number]:右手手势</p>
  <p>tracked [boolean]:是否捕获到</p>
  <p>trackingId</p>
  <p>}&nbsp;</p>
  <p>5、kinect对象</p>
  <table>
   <tbody>
    <tr>
     <td>on</td>
     <td>监听数据</td>
    </tr>
    <tr>
     <td>open</td>
     <td>打开Kinect</td>
    </tr>
    <tr>
     <td>close</td>
     <td>关闭</td>
    </tr>
    <tr>
     <td>openBodyReader</td>
     <td>读取骨骼数据</td>
    </tr>
    <tr>
     <td>open**Reader</td>
     <td>类似如上方法,读取其它类型数据</td>
    </tr>
   </tbody>
  </table>
  <h3><strong>六、实战总结</strong></h3>
  <p>火影体感游戏经验总结</p>
  <p>接下来,我总结一下TGC2016《火影忍者手游》的体感游戏开发中碰到的一些问题。</p>
  <p>1、讲解之前,我们首先需要了解下游戏流程。</p>
  <table>
   <tbody>
    <tr>
     <td> <p></p> <p>1.1、通过手势触发开始游戏</p> </td>
     <td> <p></p> <p>1.2、玩家化身四代,左右跑动躲避九尾攻击</p> </td>
    </tr>
    <tr>
     <td> <p></p> <p>1.3、摆出手势“奥义”,触发四代大招</p> </td>
     <td> <p></p> <p>1.4、用户扫描二维码获取自己现场照片</p> </td>
    </tr>
   </tbody>
  </table>
  <p>2、服务器端</p>
  <p>游戏需要玩家骨骼数据(移动、手势),彩色图像数据(某一手势下触发拍照),所以我们需要向客户端发送这两部分数据。值得注意的是,彩色图像数据体积过大,需要进行压缩。</p>
  <pre>
<code class="language-java">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
});</code></pre>
  <p>3、客户端</p>
  <p>客户端业务逻辑较复杂,我们提取关键步骤进行讲解。</p>
  <p>3.1、用户拍照时,由于处理的数据比较大,为防止页面出现卡顿,我们需要使用web worker</p>
  <pre>
<code class="language-java">(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 &lt; imageDataSize; i++) {
            imageData.data = newPixelData;
        }
        for(var x = 0; x &lt; 1920; x++) {
            for(var y = 0; y &lt; 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();
})();</code></pre>
  <p>3.2、接投影仪后,如果渲染面积比较大,会出现白屏,需要关闭浏览器硬件加速。</p>
  <p></p>
  <p>3.3、现场光线较暗,其它玩家干扰,在追踪玩家运动轨迹的过程中,可能会出现抖动的情况,我们需要去除干扰数据。(当突然出现很大位移时,需要将数据移除)</p>
  <pre>
<code class="language-java">var tracks = this.tracks;
var len = tracks.length;

// 数据过滤
if(tracks[len-1] !== window.undefined) {
        if(Math.abs(n - tracks[len-1]) &gt; 0.2) {
                return;
        }
}
this.tracks.push(n);</code></pre>
  <p>3.4、当玩家站立,只是左右少量晃动时,我们认为玩家是站立状态。</p>
  <pre>
<code class="language-java">// 保留5个数据
if(this.tracks.length &gt; 5) {
        this.tracks.shift();
} else {
        return;
}

// 位移总量
var dis = 0;
for(var i = 1; i &lt; this.tracks.length; i++) {
        dis += this.tracks - this.tracks[i-1];
}
if(Math.abs(dis) &lt; 0.01) {
        this.stand();
} else {
        if(this.tracks[4] &gt; this.tracks[3]) {
                this.turnRight();
        } else {
                this.turnLeft();
        }
        this.run();
}</code></pre>
  <h3><strong>七、展望</strong></h3>
  <p>1、使用HTML5开发Kinect体感游戏,降低了技术门槛,前端工程师可以轻松的开发体感游戏;</p>
  <p>2、大量的框架可以应用,比如用JQuery、CreateJS、Three.js(三种不同渲染方式);</p>
  <p>3、无限想象空间,试想下体感游戏结合webAR,结合webAudio、结合移动设备,太可以挖掘的东西了……想想都激动不是么!</p>
  <p>&nbsp;</p>
  <p>来自:http://www.cnblogs.com/wanbo/p/6222993.html</p>
  <p>&nbsp;</p>
</body>
</html><h2><strong>扩展阅读</strong></h2><a title=HTML5 游戏开发框架:phaser href="/lib/view/open1379474495490.html"><span class=title>HTML5 游戏开发框架:Phaser</span></a><br><a title=HTML5游戏开发引擎 - lufylegend href="/lib/view/open1339462432084.html"><span class=title>HTML5游戏开发引擎 - lufylegend</span></a><br><a title=JavaScript html5 游戏开发框架: hitagi.js href="/lib/view/open1443281759685.html"><span class=title>JavaScript HTML5 游戏开发框架: hitagi.js</span></a><br><a title=HTML5/JavaScript 游戏开发资源列表 href="/lib/view/open1387099001921.html"><span class=title>HTML5/JavaScript 游戏开发资源列表</span></a><br><a title=基于 html5 + javascript 技术开发游戏的框架:enchant.js href="/lib/view/open1392800644069.html"><span class=title>基于 HTML5 + JavaScript 技术开发游戏的框架:Enchant.js</span></a><br><h2><strong>为您推荐</strong></h2><a title=jquery-validation 学习总结 href="/lib/view/open1342179346214.html"><span class=title>jquery-validation 学习总结 </span></a><br><a title=jQuery验证控件jquery.validate.js使用说明+中文API href="/lib/view/open1455272688886.html"><span class=title>jQuery验证控件jquery.validate.js使用说明+中文API</span></a><br><a title=jQuery常用方法  href="/lib/view/open1373897144466.html"><span class=title>jQuery常用方法 </span></a><br><a title=jQuery常用方法一览 href="/lib/view/open1435845199247.html"><span class=title>jQuery常用方法一览</span></a><br><a title= JQuery经典总结  href="/lib/view/open1385172642464.html"><span class=title> JQuery经典总结 </span></a><br><h2><strong>更多</strong></h2><a title=HTML5 href="https://www.baidu.com/s?wd=site:open-open.com HTML5"><span class=title>HTML5</span></a><br><a title=游戏开发 href="https://www.baidu.com/s?wd=site:open-open.com 游戏开发"><span class=title>游戏开发</span></a><br>
                                </article>




上一篇:HTML5开源:ELF-灵活可扩展的 HTML5 构建工具
下一篇:10分钟读懂html5 多了啥?
一起共享资源,共建精品资源平台。记得一定要收藏我们网站。www.gjdhjc.com ||||| 还有我们的网址导航:www.58q8.com【链接失效可以留言看到第一时间补帖,如果懒的回复我也是没办法了】

QQ|QQ群:133515734|站长博客|百度网盘|许进龙|手机触屏版|国际动画教程网 ( ICP13037175 )