移動觸控

移動遊戲行業的未來無疑是 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 變數,並透過減去 Canvas 的偏移量(Canvas 與螢幕邊緣的距離)以及玩家寬度和高度的一半來設定螢幕上玩家飛船的位置。

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

為了檢視它是否正常工作,我們可以使用 output 元素輸出 xy 位置。preventDefault() 函式是必需的,以防止瀏覽器進行移動——如果沒有它,您將獲得預設行為,Canvas 會在頁面上拖動,這將顯示瀏覽器捲軸,看起來很雜亂。

Phaser 中的觸控事件

我們不必自己做這些;像 Phaser 這樣的框架提供了用於管理觸控事件的系統——請參閱 管理觸控事件

指標理論

一個 指標代表觸控式螢幕上的單個手指。Phaser 預設啟動兩個指標,因此兩個手指可以同時執行一個操作。Captain Rogers 是一個簡單的遊戲——它可以由兩個手指控制,左手指移動飛船,右手指控制飛船的槍。沒有多點觸控或手勢——一切都由單個指標輸入處理。

您可以透過使用 `this.game.input.addPointer` 來為遊戲新增更多指標,最多可以同時管理十個指標。最近使用的指標可在 `this.game.input.activePointer` 物件中找到——螢幕上最近活動的那個手指。

如果您需要訪問特定指標,所有指標都可以在 `this.game.input.pointer1`、`this.game.input.pointer2` 等處找到。它們是動態分配的,因此如果您用三個手指觸控式螢幕幕,則 pointer1pointer2pointer3 將處於活動狀態。例如,移除第二個手指不會影響其他兩個,再次設定它將使用第一個可用的屬性,因此 pointer2 將再次被使用。

您可以透過 `this.game.input.x` 和 `this.game.input.y` 變數快速獲取最近活動指標的座標。

輸入事件

除了直接使用指標之外,還可以監聽 `this.game.input` 事件,例如 `onDown`、`onUp`、`onTap` 和 `onHold`。

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

function itemTouched(pointer) {
  // Do something
}

當透過觸控式螢幕幕分發 `onDown` 事件時,將執行 `itemTouched()` 函式。pointer 變數將包含啟用事件的指標資訊。

這種方法使用了通用可用的 `this.game.input` 物件,但您也可以透過使用 `onInputOver`、`onInputOut`、`onInputDown`、`onInputUp`、`onDragStart` 或 `onDragStop` 來檢測任何遊戲物件(如精靈或按鈕)上的操作。

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);

上面的程式碼將建立一個新的按鈕,使用一個透明圖片覆蓋螢幕的右半部分。如果您想執行更復雜的操作,可以分別分配 `on input down` 和 `on input up` 的函式,但在此遊戲中,觸控式螢幕幕的右側將向右發射子彈——這就是我們在此案例中需要的所有內容。

可以透過建立四個方向按鈕來管理玩家的移動,但我們可以利用觸控式螢幕的優勢,透過拖動玩家的飛船來移動。

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 外掛目錄,看看是否有適合您需求的內容。

總結

這涵蓋了為移動裝置新增觸控控制元件的內容;在下一篇文章中,我們將瞭解如何新增鍵盤和滑鼠支援。