株式会社CAENのロゴ

Love2D のゲームをスマホで遊ばせるまで|バーチャルゲームパッドと Retina 対応を AI に全部書かせる

この記事で分かること

Love2D で作ったゲームを、ブラウザ経由でスマホで遊ばせるまでの手順を AI 駆動開発で一発通しするテンプレです。

  • LÖVE 11.5 を love.js で Web ビルドすればブラウザで動く
  • スマホにはキーが無いので、バーチャルゲームパッド JS をインジェクトしてタッチを keydown / keyup に変換する
  • Retina と Emscripten の canvas スケーリングで必ず詰まる。罠を先に AI へ渡せば一発で通る

僕(大森翔吾)が Love2D × AI駆動開発の入門記事 で触れた FLAPPY_BIRD などのプロトで検証し、そのまま _template_love2d に落とし込んだ知見を共有します。

なぜ Love2D は「そのまま」ではスマホで遊べないのか

LÖVE(Love2D)は Lua で 2D ゲームを書ける軽量エンジン。love.graphics でコードオンリー描画ができて、AI 駆動開発との相性が抜群です。ただし本来は PC デスクトップ前提で、スマホ対応は自前で用意する必要があります。

壁は3つ。

  1. 入力:PC は矢印キー + Z/X(A/B ボタン)想定。スマホにはキーが無い
  2. Retina / 高 DPIhighdpi = true にすると論理 960x540 でもピクセルバッファが実 2倍に膨らむ
  3. Web canvas スケーリング:Emscripten SDL2 は canvas の CSS サイズと描画バッファを勝手に同期するので、スマホ画面に合わせて CSS を変えると描画が壊れる

この3つに最初から釘を刺したプロンプトを AI に渡せば、余計な往復なく通ります。Godot でのゲーム開発は Godot × AI駆動開発で2Dゲームを作る、Love2D で AI にコードを書かせる基本は Love2D × AI駆動開発|Lua はむしろ AI と相性がいい にまとめています。

問題1:入力をどうするか(バーチャルゲームパッド)

love.keyboard.isDown("up") のような既存コードを 書き換えずに スマホ対応したい。解は「タッチイベントを KeyboardEvent に変換して document にディスパッチする」こと。Emscripten SDL2 がキーイベントを拾うので、Lua 側のコードはそのまま動きます。

// web/gamepad.js(抜粋)
var KEY_MAP = {
  up: { key: "ArrowUp", code: "ArrowUp", keyCode: 38 },
  a:  { key: "z",       code: "KeyZ",    keyCode: 90 },
  b:  { key: "x",       code: "KeyX",    keyCode: 88 },
  // ...
};

function sendKey(button, pressed) {
  var km = KEY_MAP[button];
  var evt = new KeyboardEvent(pressed ? "keydown" : "keyup", {
    key: km.key, code: km.code, keyCode: km.keyCode,
    bubbles: true, cancelable: true,
  });
  document.dispatchEvent(evt);
}

DOM 側に D-pad と A/B ボタンを組み、touchstart / touchendsendKey を呼ぶだけ。これで Lua のゲームロジックには一切手を入れず、スマホで love.keyboard.isDown("z") が反応します。

問題2:Retina / 高 DPI で絵が右下にズレる

conf.luat.window.highdpi = true を入れると、Retina ディスプレイでは love.graphics.getWidth() が論理幅(960)より大きい値を返します。何も対策しないと「960x540 向けに描いたのに画面の左上1/4しか使われない」状態になります。

対処は SCALE パターンlove.load で比率を計算し、love.draw の先頭で love.graphics.scale を効かせるだけ。

local SCALE = 1

function love.load()
  SCALE = love.graphics.getWidth() / 960
end

function love.draw()
  love.graphics.scale(SCALE, SCALE)
  -- 以降は 960x540 の論理座標で描画してOK
end

-- マウス座標を使う場合は論理座標へ戻す
local mx, my = love.mouse.getPosition()
mx, my = mx / SCALE, my / SCALE

これを毎回忘れるので、僕は AGENTS.md / CLAUDE.md のプロジェクトテンプレに埋め込んでいます。新規ゲームを scaffold した瞬間から AI が勝手にこのパターンで書いてくれる状態がゴール。

問題3:Emscripten canvas のスケーリングで全員ハマる

ここが一番深い罠。Emscripten SDL2 は canvas の CSS 表示サイズを読み取って、描画バッファ(canvas.width / canvas.height)をそれに同期する 仕様です。

つまりスマホ画面に合わせるつもりで、

// NG: これをやると描画バッファも 390x219 にされて絵が崩れる
gameCanvas.style.width = "390px";
gameCanvas.style.height = "219px";

とすると、Lua 側は 960x540 のつもりなのに実際の描画先が狭くなって、左上の一部しか描画されない。さらに canvas.width を直接上書きすると WebGL コンテキストがリセット されて画面が真っ黒になります。

正解は CSS transform: scale() を使うこと。transformwidth / height の算出値を変えないので、Emscripten のバッファ同期が発火しません。

var scale = window.innerWidth / 960;
gameCanvas.style.transformOrigin = "top left";
gameCanvas.style.transform = "scale(" + scale + ")";

さらにレースコンディション対策として、Emscripten の初期化完了を Module.postMainLoop フックで待ちます。gamepad.jslove.js より先に走ると、canvas のデフォルト 300x150 を読んでおかしな状態で固まるため。

var orig = Module.postMainLoop;
Module.postMainLoop = function () {
  Module.postMainLoop = orig || null; // ワンショット
  applyMobileLayout(); // この時点で love.load 完了・バッファ 960x540 確定
};

ここは検証で7〜8手法ダメ出しした結果の結論です。詳しくは Love2D を love.js でスマホ公開するまで で手順側からも書きました。

問題4:love.js は Lua 5.1 ベースという地雷

ネイティブの LÖVE は LuaJIT(5.1 + 拡張)で動くので goto 文や bit ライブラリが普通に使えます。ところが love.js素の Lua 5.1 ベース。PC で動いていたコードが Web ビルドでだけ落ちる、という事故が頻発します。

  • goto ラベル(::continue::)→ if not ... then ... end で代替
  • ビット演算ライブラリ bit → 自前で実装するか使わない設計に寄せる

AI に最初からこう伝えておけば、goto を書かれない。Lua の書き癖の入口は AI駆動開発のスマホ1台ワークフロー でも触れています。

AI 駆動開発 Tip:罠をテンプレ化してプロジェクトに常駐させる

ここまでの4問題、全部「知っていれば回避できる」ものです。なので CLAUDE.md / AGENTS.md に以下を固定で書いておく。

  • 対象は LÖVE 11.5 + love.js。12.x の API は使わない
  • love.draw の冒頭で love.graphics.scale(SCALE, SCALE) を呼ぶ
  • Web canvas のスケーリングは transform: scale() のみcanvas.style.width / canvas.width の直接変更禁止
  • Module.postMainLoop で初期化待ち してからレイアウト適用
  • gotobit は love.js で動かない。使わない

これを置いた状態で Claude Code に「_template_love2d で scaffold して、Web ビルドとバーチャルゲームパッド対応まで入れて」と頼めば、ほぼ一発で通ります。毎回の試行錯誤をゼロにするのが AI 駆動開発の本質。Claude Code 自体の使い方は Claude Code 完全ガイド を参照してください。

関連する記事

AI駆動開発のご相談・お仕事のご依頼

株式会社CAEN(代表:大森翔吾)では、Love2D や Godot を使った個人ゲーム開発の AI 駆動化、Web ビルドとスマホ公開のテンプレ整備、CLAUDE.md / AGENTS.md を使ったプロジェクト常駐ルール設計などのご相談を承ります。

「Love2D で作ったプロト版をスマホでも遊べるようにしたい」「自社ゲームの Web ビルドを AI 駆動で整備したい」など、お気軽にご相談ください。