年に1回くらい、ふと遊びたくなるゲーム「Slither.io(スリザリオ)」
PCブラウザでやるとマウスやトラックパッドでのカーソル操作なのですが、とても操作しにくいのでゲーム用のコントローラーで動かせるようにします。
Gamepad APIでコントローラーの情報を取得
ブラウザに用意されているGamepad APIを使用すると結構簡単にコントローラーの入力が取得できます。
コントローラーを接続するとイベントが発火して、コントローラーを認識する
window.addEventListener("gamepadconnected", (e) => {
console.log("Gamepad connected!", e.gamepad);
});
各ボタンはこんな感じで取得可能です。
navigator.getGamepads().forEach((gp, index) => {
let xAxis = gp.axes[0];
let yAxis = gp.axes[1];
let aButton = gp.buttons[0].pressed;
}
今回はPCへの接続が簡単な、XBOXコントローラーを使用しましたが
コントローラーの入力をマウスイベントに変換
MouseEventをつくり発火させることで、マウス操作を再現可能です。
const target = document.elementFromPoint(center.x, center.y);
const event = new MouseEvent("mousemove", {
bubbles: true,
clientX: x,
clientY: y,
});
target.dispatchEvent(event);
Slither.ioの操作では画面の中央からどの向きにカーソルがあるかで、移動先を判断しているようなので
スティックの入力向きを変換しています。
完成形
Aボタンの押下中でブーストを使用されるようにしました。
ゲームオーバーになっても、Aボタンですぐに再開できます。
const SPEED = 20;
let x = window.innerWidth / 2;
let y = window.innerHeight / 2;
let wasPressed = false;
function fireMouseEvent(type, x, y, target) {
console.log(`Firing ${type} at (${x}, ${y})`);
const event = new MouseEvent(type, {
bubbles: true,
clientX: x,
clientY: y,
});
target.dispatchEvent(event);
}
function update() {
let center = {
x: window.innerWidth / 2,
y: window.innerHeight / 2,
};
const gamepads = navigator.getGamepads();
gamepads.forEach((gp, index) => {
if (!gp) {
return;
}
let xAxis = gp.axes[0];
let yAxis = gp.axes[1];
if (xAxis < 0.1 && xAxis > -0.1) xAxis = 0;
if (yAxis < 0.1 && yAxis > -0.1) yAxis = 0;
console.log(`Gamepad ${index}:`, xAxis, yAxis);
x = center.x + xAxis * SPEED;
y = center.y + yAxis * SPEED;
const target = document.elementFromPoint(center.x, center.y);
fireMouseEvent("mousemove", x, y, target || document.body);
const isPressed = gp.buttons[0].pressed; // Aボタン
if (isPressed && !wasPressed) {
if (document.querySelector("#login")?.style.display !== "none") {
document.querySelector("#playh .btnt")?.click();
return;
}
fireMouseEvent("mousedown", x, y, document.body);
} else if (!isPressed && wasPressed) {
fireMouseEvent("mouseup", x, y, document.body);
}
wasPressed = isPressed;
});
requestAnimationFrame(update);
}
window.addEventListener("gamepadconnected", () => {
console.log("Gamepad connected!", navigator.getGamepads());
alert("Gamepad connected!");
update();
});