使用Hand.js创建跨浏览器基于触摸的游戏杆

我目前正在为现代浏览器和Windows 8 Store项目开发多个游戏项目。 他们中的一些人以HTML5为基础,以简化多设备定位。 然后,我正在寻找一种统一的方法来处理所有平台上的所有输入:Windows 8 / RT,Windows Phone 8,iPad,Android和FirefoxOS。

正如您在我之前的文章《 统一触摸和鼠标—指针事件如何使跨浏览器的触摸支持变得容易》中发现的那样 Windows 8 / RT和Windows Phone 8上的IE10实现了我们提交给W3C的Pointer Events模型。 为了以统一的方式解决这个Pointer Events模型以及在基于WebKit的浏览器中实现的问题,我们将使用David Catuhe的HandJS库。 在这里查看他的博客文章: HandJS一个polyfill,用于在每个浏览器上支持指针事件 。 这个想法是针对Pointer模型的,并且库会将触摸事件传播到所有平台细节。

掌握了所有技术知识后,我正在寻找一种在游戏中实现虚拟触摸操纵杆的好方法。 我不是要触摸箭头键的忠实粉丝。 另一方面,虚拟类比垫通常放置得不太好。 但是我终于发现, Seb Lee-Delisle已经消化了这一点,并创造了一个很棒的概念,该概念在iPad的JavaScript / HTML5中的多点触控游戏控制器中有所描述。 该代码可在GitHub上找到: JSTouchController

当时的想法是采用他的代码并重构触摸部分,使其以Pointer模型为目标,而不是原始的WebKit Touch方法。 在几个月前进行这项工作时,我发现Google的Boris Smus已经或多或少开始这样做。 正如他在跨设备网络上的文章Generalized input中所描述的那样,当他在自己的Pointer.js库上工作时就已经完成了。 但是,当时Boris模仿的是IE10指针事件实现的旧版本,并且他的库不在IE10中工作。 这就是为什么即使鲍里斯(Boris)的作品很棒,我们仍然决定使用自己的版本。 确实,David的库当前针对的是最新的W3C版本(当前在最新的通话草案中) 。 如果您同时查看两个库,您还将看到HandJS在代码的多个部分中使用了一些不同的方法。 然后,我们将在本文中使用HandJS来构建我们的触摸操纵杆。

示例1:指针跟踪器

此样本可帮助您跟踪屏幕上的各种输入。 它跟踪并跟随按下画布元素的各种手指。 它基于GitHub上提供的Seb示例: Touches.html

感谢Hand.js,我们将使其与所有浏览器兼容。 它甚至还可以根据您当前正在测试的硬件类型来跟踪触控笔和/或鼠标!

这是在Windows 8下运行IE10的结果的HTML5视频。您会发现看到一些青色的圆圈跟踪手指,然后是一个红色的圆圈跟踪鼠标,一个绿色的圆圈跟踪笔:

下载视频: MP4WebM , VideoJS的HTML5视频播放器

在Windows 8或iOS / Android / FirefoxOS设备上的Chrome下,同一网页提供的结果非常相同(除了IE10仅支持笔)。 多亏了HandJS,只需编写一次就可以在任何地方运行! 精神

您已经在视频中看到,青色指针的类型为“ TOUCH”,而红色指针的类型为“ MOUSE”。 如果您有触摸屏,则可以通过测试此iframe中嵌入的以下页面来获得相同的结果:

该示例可在Windows 8 / RT触摸设备,Windows Phone 8,iPad / iPhone或Android / FirefoxOS设备上正常运行! 如果您没有触摸设备,HandJS将自动退回到鼠标。 然后,您应该可以用鼠标至少跟踪1个指针。

让我们看看如何以统一的方式获得此结果。 所有代码都保存在Touches.js中

 "use strict" ;

// shim layer with setTimeout fallback
 window.requestAnimFrame = ( function  () {
    return  window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function  (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

var  pointers; // collections of pointers

 var  canvas,
c; // c is the canvas' context 2D

 document.addEventListener( "DOMContentLoaded" , init);

window.onorientationchange = resetCanvas;
window.onresize = resetCanvas;

function  init() {
    setupCanvas();
    pointers = new  Collection();
    canvas.addEventListener( 'pointerdown' , onPointerDown, false );
    canvas.addEventListener( 'pointermove' , onPointerMove, false );
    canvas.addEventListener( 'pointerup' , onPointerUp, false );
    canvas.addEventListener( 'pointerout' , onPointerUp, false );
    requestAnimFrame(draw);
}

function  resetCanvas(e) {
    // resize the canvas - but remember - this clears the canvas too.
     canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    //make sure we scroll to the top left.
     window.scrollTo(0, 0);
}

function  draw() {
    c.clearRect(0, 0, canvas.width, canvas.height);

    pointers.forEach( function  (pointer) {
        c.beginPath();
        c.fillStyle = "white" ;
        c.fillText(pointer.type + " id : "  + pointer.identifier + " x:" 
          + pointer.x + " y:"  + pointer.y, pointer.x + 30, pointer.y - 30);

        c.beginPath();
        c.strokeStyle = pointer.color;
        c.lineWidth = "6" ;
        c.arc(pointer.x, pointer.y, 40, 0, Math.PI * 2, true );
        c.stroke();
    });

    requestAnimFrame(draw);
}

function  createPointerObject(event) {
    var  type;
    var  color;
    switch  (event.pointerType) {
        case  event.POINTER_TYPE_MOUSE:
            type = "MOUSE" ;
            color = "red" ;
            break ;
        case  event.POINTER_TYPE_PEN:
            type = "PEN" ;
            color = "lime" ;
            break ;
        case  event.POINTER_TYPE_TOUCH:
            type = "TOUCH" ;
            color = "cyan" ;
            break ;
    }
    return  { identifier: event.pointerId, x: event.clientX, y: event.clientY, 
    type: type, color: color };
}

function  onPointerDown(e) {
    pointers.add(e.pointerId, createPointerObject(e));
}

function  onPointerMove(e) {
    if  (pointers.item(e.pointerId)) {
        pointers.item(e.pointerId).x = e.clientX;
        pointers.item(e.pointerId).y = e.clientY;
    }
}

function  onPointerUp(e) {
    pointers.remove(e.pointerId);
}

function  setupCanvas() {
    canvas = document.getElementById( 'canvasSurface' );
    c = canvas.getContext( '2d' );
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    c.strokeStyle = "#ffffff" ;
    c.lineWidth = 2;
}

好吧,我认为代码非常简单。 我正在注册指针向下/上移/向上事件,如我在MSPointer Events的介绍文章中所述 。 在pointerdown处理程序中,我捕获了在指针集合对象中动态生成的对象内的ID,X和Y坐标以及指针的类型(触摸,笔或鼠标)。 该集合由指针的ID索引。 集合对象在Collection.js中进行了描述。 然后draw()函数枚举此集合,以根据触摸屏幕的确切位置处的类型绘制一些青色/红色/石灰圆形。 它还在每个圆的侧面添加一些文本以显示指针的详细信息。 指针移动处理程序更新了集合中关联指针的坐标,而pointerup / out只是将其从集合中删除。 Hand.JS通过将pointerdown / move / up / out传播到关联的MSPointerDown / Move / Up / Out事件以及WebKit浏览器的touchstart / move / end事件,使此代码与IE10兼容。

如果您愿意,可以在此处查看完整的源代码: http : //david.blob.core.windows.net/html5/touchjoystick/Touches.html

示例2:具有简单飞船游戏的视频游戏控制器

现在,让我们看一下我最感兴趣的示例。如果您正在为HTML5游戏寻找虚拟模拟触摸板,则可能也会这样做。 想法是触摸屏幕左侧的任何位置。 在您触摸屏幕的确切位置,它将显示一个简单但非常有效的键盘。 移动手指将更新虚拟触摸板,并将移动一个简单的太空飞船。 触摸屏幕右侧将显示一些红色圆圈,这些圆圈将生成一些从子弹飞出的子弹。 再一次,它基于Seb的示例,可在GitHub上找到: TouchControl.html

这是Windows 8下IE10中更新的示例结果的视频:


如果您有触摸屏,则可以在此iframe中实时测试该页面:


否则,您只能通过单击屏幕左侧的鼠标来移动飞船,或者通过单击右侧的箭头来开火,但是您将无法同时完成这两项操作。 实际上,如果浏览器或平台不支持触摸,HandJS就会提供鼠标后备功能。

注意: iPad似乎有一个未知的错误,该错误会阻止第二个iframe正常工作。 直接在另一个标签中打开示例 ,使其在iPad上运行。

让我们再次看看如何以统一的方式获得此结果。 这次所有代码都保存在TouchControl.js中

 // shim layer with setTimeout fallback
 window.requestAnimFrame = ( function  () {
    return  window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function  (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

var  canvas,
c, // c is the canvas' context 2D
 container,
halfWidth,
halfHeight,
leftPointerID = -1,
leftPointerPos = new  Vector2(0, 0),
leftPointerStartPos = new  Vector2(0, 0),
leftVector = new  Vector2(0, 0);

var  pointers; // collections of pointers
 var  ship;
bullets = [],
spareBullets = [];

document.addEventListener( "DOMContentLoaded" , init);

window.onorientationchange = resetCanvas;
window.onresize = resetCanvas;

function  init() {
    setupCanvas();
    pointers = new  Collection();
    ship = new  ShipMoving(halfWidth, halfHeight);
    document.body.appendChild(ship.canvas);
    canvas.addEventListener( 'pointerdown' , onPointerDown, false );
    canvas.addEventListener( 'pointermove' , onPointerMove, false );
    canvas.addEventListener( 'pointerup' , onPointerUp, false );
    canvas.addEventListener( 'pointerout' , onPointerUp, false );
    requestAnimFrame(draw);
}

function  resetCanvas(e) {
    // resize the canvas - but remember - this clears the canvas too. 
     canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    halfWidth = canvas.width / 2;
    halfHeight = canvas.height / 2;

    //make sure we scroll to the top left. 
     window.scrollTo(0, 0);
}

function  draw() {
    c.clearRect(0, 0, canvas.width, canvas.height);

    ship.targetVel.copyFrom(leftVector);
    ship.targetVel.multiplyEq(0.15);
    ship.update();

    with  (ship.pos) {
        if  (x < 0) x = canvas.width;
        else if  (x > canvas.width) x = 0;
        if  (y < 0) y = canvas.height;
        else if  (y > canvas.height) y = 0;
    }

    ship.draw();

    for  ( var  i = 0; i < bullets.length; i++) {
        var  bullet = bullets[i];
        if  (!bullet.enabled) continue ;
        bullet.update();
        bullet.draw(c);
        if  (!bullet.enabled) {
            spareBullets.push(bullet);

        }
    }

    pointers.forEach( function  (pointer) {
        if  (pointer.identifier == leftPointerID) {
            c.beginPath();
            c.strokeStyle = "cyan" ;
            c.lineWidth = 6;
            c.arc(leftPointerStartPos.x, leftPointerStartPos.y, 40, 0, Math.PI * 2, true );
            c.stroke();
            c.beginPath();
            c.strokeStyle = "cyan" ;
            c.lineWidth = 2;
            c.arc(leftPointerStartPos.x, leftPointerStartPos.y, 60, 0, Math.PI * 2, true );
            c.stroke();
            c.beginPath();
            c.strokeStyle = "cyan" ;
            c.arc(leftPointerPos.x, leftPointerPos.y, 40, 0, Math.PI * 2, true );
            c.stroke();

        } else  {

            c.beginPath();
            c.fillStyle = "white" ;
            c.fillText( "type : "  + pointer.type + " id : "  + pointer.identifier + " x:"  + pointer.x + 
" y:" + pointer.y, pointer.x + 30, pointer.y - 30); c.beginPath(); c.strokeStyle = "red" ; c.lineWidth = "6" ; c.arc(pointer.x, pointer.y, 40, 0, Math.PI * 2, true ); c.stroke(); } }); requestAnimFrame(draw); } function makeBullet() { var bullet; if (spareBullets.length > 0) { bullet = spareBullets.pop(); bullet.reset(ship.pos.x, ship.pos.y, ship.angle); } else { bullet = new Bullet(ship.pos.x, ship.pos.y, ship.angle); bullets.push(bullet); } bullet.vel.plusEq(ship.vel); } function givePointerType(event) { switch (event.pointerType) { case event.POINTER_TYPE_MOUSE: return "MOUSE" ; break ; case event.POINTER_TYPE_PEN: return "PEN" ; break ; case event.POINTER_TYPE_TOUCH: return "TOUCH" ; break ; } } function onPointerDown(e) { var newPointer = { identifier: e.pointerId, x: e.clientX, y: e.clientY, type: givePointerType(e) }; if ((leftPointerID < 0) && (e.clientX < halfWidth)) { leftPointerID = e.pointerId; leftPointerStartPos.reset(e.clientX, e.clientY); leftPointerPos.copyFrom(leftPointerStartPos); leftVector.reset(0, 0); } else { makeBullet(); } pointers.add(e.pointerId, newPointer); } function onPointerMove(e) { if (leftPointerID == e.pointerId) { leftPointerPos.reset(e.clientX, e.clientY); leftVector.copyFrom(leftPointerPos); leftVector.minusEq(leftPointerStartPos); } else { if (pointers.item(e.pointerId)) { pointers.item(e.pointerId).x = e.clientX; pointers.item(e.pointerId).y = e.clientY; } } } function onPointerUp(e) { if (leftPointerID == e.pointerId) { leftPointerID = -1; leftVector.reset(0, 0); } leftVector.reset(0, 0); pointers.remove(e.pointerId); } function setupCanvas() { canvas = document.getElementById( 'canvasSurfaceGame' ); c = canvas.getContext( '2d' ); resetCanvas(); c.strokeStyle = "#ffffff" ; c.lineWidth = 2; }

代码再次非常简单,我不会花时间解释它。 您可以在此处查看完整的源代码: http : //david.blob.core.windows.net/html5/touchjoystick/TouchControl.html

总之,由于Seb Lee-DelisleDavid Catuhe所做的工作,您现在拥有了为HTML5游戏实现自己的虚拟触摸游戏手柄所需的所有组件。 结果将在所有支持HTML5的触摸设备上运行!

大卫

From: https://www.sitepoint.com/create-a-cross-browser-touch-based-joystick-with-hand-js/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值