移动触摸控制

移动游戏的未来绝对是 Web,许多开发人员在其游戏开发过程中选择移动优先方法——在现代世界中,这通常也包括实现触摸控制。在本教程中,我们将了解在 HTML 游戏中实现移动控制是多么容易,并在支持触摸的移动设备上享受游戏乐趣。

注意:游戏Captain Rogers: Battle at Andromeda是使用 Phaser 构建的,控制管理是基于 Phaser 的,但也可以用纯 JavaScript 完成。使用 Phaser 的好处是它提供了辅助变量和函数,可以简化和加快开发速度,但完全取决于您选择哪种方法。

纯 JavaScript 方法

我们可以自己实现触摸事件——设置事件监听器并为其分配相关函数将非常简单

js
const el = document.querySelector("canvas");
el.addEventListener("touchstart", handleStart);
el.addEventListener("touchmove", handleMove);
el.addEventListener("touchend", handleEnd);
el.addEventListener("touchcancel", handleCancel);

这样,在移动屏幕上触摸游戏的<canvas>就会发出事件,因此我们可以以任何我们想要的方式操作游戏(例如,移动宇宙飞船)。事件如下

  • touchstart 当用户将手指放在屏幕上时触发。
  • touchmove 当他们在触摸屏幕时移动手指时触发
  • touchend 当用户停止触摸屏幕时触发
  • touchcancel 当触摸被取消时触发,例如当用户将手指移出屏幕时。

注意:触摸事件参考文章提供了更多示例和信息。

纯 JavaScript 演示

让我们在 GitHub 上提供的一个小演示中实现移动支持,以便我们通过在移动设备上触摸屏幕来移动玩家的飞船。

我们将使用两个事件:touchstarttouchmove,这两个事件都由一个函数处理。为什么?函数 touchHandler 将为飞船的位置分配适当的变量,以便我们可以将其用于这两种情况:当玩家触摸屏幕但没有移动它(touchstart)时,以及当手指在屏幕上移动时(touchmove

js
document.addEventListener("touchstart", touchHandler);
document.addEventListener("touchmove", touchHandler);

touchHandler 函数如下所示

js
function touchHandler(e) {
  if (e.touches) {
    playerX = e.touches[0].pageX - canvas.offsetLeft - playerWidth / 2;
    playerY = e.touches[0].pageY - canvas.offsetTop - playerHeight / 2;
    output.textContent = `Touch:\nx: ${playerX}, y: ${playerY}`;
    e.preventDefault();
  }
}

如果发生触摸(touches 对象不为空),那么我们将在该对象中获得所有我们需要的信息。我们可以获取第一个触摸(e.touches[0],我们的示例不支持多点触摸),提取 pageXpageY 变量,并通过减去画布偏移量(画布与屏幕边缘之间的距离)和玩家宽度和高度的一半来设置玩家飞船在屏幕上的位置。

Touch controls for the player's ship, with visible output of the x and y position.

要查看它是否正常工作,我们可以使用 output 元素输出 xy 位置。preventDefault() 函数需要防止浏览器移动——如果没有它,您将获得默认行为,并且画布将在页面上四处拖动,这将显示浏览器滚动条并显得杂乱无章。

Phaser 中的触摸事件

我们不必自己做这些;像 Phaser 这样的框架为我们提供了管理触摸事件的系统——请参阅管理触摸事件

指针理论

一个指针表示触摸屏上的一个手指。Phaser 默认启动两个指针,因此两个手指可以同时执行操作。Captain Rogers 是一个简单的游戏——它可以通过两个手指控制,左手指移动飞船,右手指控制飞船的枪。没有多点触摸或手势——一切都由单个指针输入处理。

您可以使用 this.game.input.addPointer 向游戏添加更多指针,最多可以同时管理十个指针。最近使用的指针可在 this.game.input.activePointer 对象中获得——屏幕上最近激活的手指。

如果您需要访问特定指针,它们都可以在 this.game.input.pointer1this.game.input.pointer2 等中获得。它们是动态分配的,因此如果您在屏幕上放置三个手指,则 pointer1pointer2pointer3 将处于活动状态。例如,移除第二个手指不会影响其他两个手指,将其放回原位将使用第一个可用属性,因此 pointer2 将再次被使用。

您可以通过 this.game.input.xthis.game.input.y 变量快速获取最近激活指针的坐标。

输入事件

除了直接使用指针之外,还可以监听 this.game.input 事件,例如 onDownonUponTaponHold

js
this.game.input.onDown.add(itemTouched, this);

function itemTouched(pointer) {
  // Do something
}

onDown 事件通过触摸屏幕调度时,将执行 itemTouched() 函数。pointer 变量将包含有关激活该事件的指针的信息。

此方法使用普遍可用的 this.game.input 对象,但您也可以通过使用 onInputOveronInputOutonInputDownonInputUponDragStartonDragStop 检测任何游戏对象(如精灵或按钮)上的操作

js
this.button.events.onInputOver.add(itemTouched, this);

function itemTouched(button, pointer) {
  // Do something
}

这样,您就可以将事件附加到游戏中的任何对象(例如玩家的飞船),并对用户执行的操作做出反应。

使用 Phaser 的另一个优点是,您创建的按钮将接收任何类型的输入,无论是移动设备上的触摸还是桌面上的点击——框架会在后台为您解决这个问题。

实施

添加一个交互式对象来监听用户输入的最简单方法是创建一个按钮

js
const buttonEnclave = this.add.button(
  10,
  10,
  "logo-enclave",
  this.clickEnclave,
  this,
);

此按钮在 MainMenu 状态中形成——它将放置在屏幕左上角的十个像素处,使用 logo-enclave 图像,并在触摸时执行 clickEnclave() 函数。这将在移动设备和桌面上开箱即用。主菜单中有一些按钮,包括启动游戏的按钮。

对于实际的游戏玩法,与其创建更多按钮并用它们覆盖小的移动屏幕,我们可以使用一些不同的东西:我们将创建对给定操作做出响应的不可见区域。从设计的角度来看,最好扩大活动区域,而不用按钮图像覆盖屏幕的一半。例如,点击屏幕右侧将发射武器

js
this.buttonShoot = this.add.button(
  this.world.width * 0.5,
  0,
  "button-alpha",
  null,
  this,
);
this.buttonShoot.onInputDown.add(this.goShootPressed, this);
this.buttonShoot.onInputUp.add(this.goShootReleased, this);

上面的代码将使用透明图像创建一个新的按钮,该图像覆盖屏幕的右侧一半。如果您想执行更复杂的操作,可以分别为输入按下和输入抬起分配函数,但在本游戏中,触摸屏幕右侧将向右发射子弹——在本例中,这正是我们需要的。

移动玩家可以通过创建四个方向按钮来管理,但我们可以利用触摸屏并四处拖动玩家的飞船

js
const player = this.game.add.sprite(30, 30, "ship");
player.inputEnabled = true;
player.input.enableDrag();
player.events.onDragStart.add(onDragStart, this);
player.events.onDragStop.add(onDragStop, this);

function onDragStart(sprite, pointer) {
  // Do something when dragging
}

我们可以四处拖动飞船并同时执行某些操作,并在拖动停止时做出反应。如果启用,Phaser 中的拖动将开箱即用——您不必手动设置精灵的位置,因此您可以将 onDragStart() 函数留空,或放置一些调试输出以查看它是否正常工作。pointer 元素包含存储拖动元素当前位置的 xy 变量。

专用插件

您可以使用专用插件以不同的方式处理触摸事件、渲染 UI 控件等等。以下是一些使用虚拟游戏手柄和操纵杆的插件示例

对于像虚拟游戏手柄这样的基本插件,您可以下载脚本并使其在您的页面中可用

html
<script src="js/phaser.min.js"></script>
<!-- https://github.com/ShawnHymel/phaser-plugin-virtual-gamepad -->
<script src="js/phaser-plugin-virtual-gamepad.js"></script>

然后将它们包含在你的脚本中,并像以下代码片段一样使用它们。

js
// Add the VirtualGamepad plugin to a Phaser 2 game
this.gamepad = this.game.plugins.add(Phaser.Plugin.VirtualGamepad);
// Add a joystick to the game
this.joystick = this.gamepad.addJoystick(100, 420, 1.2, "gamepad");
// Add a button to the game
this.button = this.gamepad.addButton(400, 420, 1.0, "gamepad");

有关更多信息,请查看Phaser 插件的非官方目录,看看是否有适合你需求的插件。

总结

这涵盖了为移动设备添加触摸控件;在下一篇文章中,我们将了解如何添加键盘和鼠标支持。