mcpField
P
phaser-mobile

phaser-mobile

github.com/yakoub-ai開発29198 回インストール9 週間前に更新
リポジトリ

phaser-mobile について

phaser-mobileは、Phaser 4のゲームをモバイル端末やさまざまな画面サイズに対応させる開発者向けガイドです。スケーリング、タッチ入力、パフォーマンス、Capacitorでのデプロイ、PWA設定を扱います。

主な機能

  1. 01Scale Managerの設定
  2. 02タッチ操作とレスポンシブレイアウト
  3. 03ブラウザー操作の抑止
  4. 04モバイル性能の指針
  5. 05Capacitorでのデプロイ手順
  6. 06PWA設定ガイド

活用シーン

  1. 01異なる解像度にゲームUIを適応
  2. 02モバイルゲームへのタッチ操作追加
  3. 03CapacitorでのPhaserゲーム公開準備
  4. 04インストール可能なPWAの設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yakoub-ai/phaser4-gamedev --skill phaser-mobile

インストール後、Claude Code で/phaser-mobileを入力して読み込まれたか確認します。

同梱ファイルreferences/device-profiles.mdreferences/mobile-patterns.md

詳細

Phaser 4 モバイル & レスポンシブ

このガイドでは、Scale Manager の設定、タッチ操作、ブラウザのジェスチャー防止、モバイルでのパフォーマンス、Capacitor へのデプロイ、PWA のセットアップについて説明します。

Scale Manager の設定

Scale Manager は、Phaser が内部解像度を画面にどのようにマッピングするかを制御します。

// In GameConfig:
const config: Phaser.Types.Core.GameConfig = {
  scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    width: 800,
    height: 600,
  },
};
スケールモード
モード動作用途
Phaser.Scale.FITレターボックス表示、アスペクト比を維持固定解像度のゲーム(最も一般的)
Phaser.Scale.ENVELOP画面を埋める、端が切れる場合がある背景、カジュアルゲーム
Phaser.Scale.RESIZECanvas がウィンドウの正確なサイズにリサイズされる適応型 UI のゲーム(複雑)
Phaser.Scale.NONEスケーリングなし、元のピクセルサイズデスクトップ専用ゲーム

FIT はほとんどのゲームに適しています。Canvas はアスペクト比を維持しながらコンテナに収まるようにスケーリングされ、必要に応じてレターボックスバーが追加されます。

RESIZE は流動的な Canvas を提供しますが、すべての UI とレイアウトのコードがサイズ変更に対応する必要があります。ゲームをあらゆるデバイスの形状にぴったり収める必要が本当にある場合にのみ使用してください。

動的レイアウトを伴う RESIZE モード
// GameConfig for RESIZE:
scale: {
  mode: Phaser.Scale.RESIZE,
  autoCenter: Phaser.Scale.CENTER_BOTH,
  // No fixed width/height — canvas matches window
}

// In the scene:
create(): void {
  this.layoutUI(this.scale.width, this.scale.height);

  // Re-layout whenever window resizes (orientation change, keyboard appearing, etc.)
  this.scale.on('resize', (size: Phaser.Structs.Size) => {
    this.cameras.main.setSize(size.width, size.height);
    this.layoutUI(size.width, size.height);
  });
}

private layoutUI(w: number, h: number): void {
  this.scoreText?.setPosition(w * 0.05, h * 0.05);
  this.healthBar?.setPosition(w * 0.5, h * 0.95);
  this.pauseBtn?.setPosition(w - 40, 40);
}

タッチ操作

Phaser のポインターイベントはモバイルでも自動的に動作します — pointerdown、pointerup、pointermove はすべてタッチに対して発火します。

タップターゲットの最小サイズ: 44×44 論理ピクセル。これより小さいターゲットはモバイルでタップの失敗を引き起こします。

// Simple tap interaction (works on both mouse and touch)
const btn = this.add.image(x, y, 'button')
  .setInteractive()
  .on('pointerdown', () => this.handleTap())
  .on('pointerover', () => btn.setTint(0xdddddd))
  .on('pointerout',  () => btn.clearTint());

モバイルでの方向制御については、references/mobile-patterns.md を参照してください — VirtualGamepad クラス。

レスポンシブレイアウト

UI 要素は画面サイズの割合として配置し、あらゆる解像度で機能するようにします。

create(): void {
  const { width, height } = this.scale;

  // Top-left HUD
  this.scoreText = this.add.text(width * 0.05, height * 0.05, 'Score: 0', {
    fontSize: `${Math.round(height * 0.05)}px`,
    color: '#ffffff',
  });

  // Bottom-center action button
  this.actionBtn = this.add.image(width * 0.5, height * 0.9, 'btn-action')
    .setInteractive()
    .setDisplaySize(width * 0.15, width * 0.15); // square button, proportional to width
}

フォントサイズのヒント: フォントサイズは height * 0.04~0.06 にスケーリングしてください。固定ピクセルサイズは小さな画面では巨大に見えます。

ブラウザのジェスチャー防止

モバイルブラウザは、ズーム、スクロール、コンテキストメニューのためにタッチイベントを横取りします。ネイティブのような操作感のゲームにするために、これらを防止してください。

// In your game's index.html or a boot script (not inside Phaser scenes):

// Prevent pinch-to-zoom and scroll
document.addEventListener('touchmove', (e) => e.preventDefault(), { passive: false });

// Prevent double-tap zoom on iOS
let lastTap = 0;
document.addEventListener('touchend', (e) => {
  const now = Date.now();
  if (now - lastTap < 300) e.preventDefault();
  lastTap = now;
});

// Prevent right-click / long-press context menu
document.addEventListener('contextmenu', (e) => e.preventDefault());
// Inside a Phaser scene:
// Disable right-click context menu on the canvas
this.input.mouse?.disableContextMenu();
// Lock to landscape orientation (for most action games)
// Must be triggered from a user interaction (button press), not on load
async lockOrientation(): Promise<void> {
  try {
    await screen.orientation?.lock?.('landscape-primary');
  } catch {
    // Not supported on all browsers/platforms — fail silently
  }
}

モバイルでのオーディオのアンロック

iOS と Android は、ユーザーがページを操作するまでオーディオの再生をブロックします。Phaser は、最初のポインター/キーイベントを監視し、その時点で AudioContext を再開することで、これを自動的に処理します。

読み込み直後にオーディオを再生するゲーム(オープニングジングル、環境音)では、「タップしてスタート」オーバーレイを表示します:

create(): void {
  this.bgMusic = this.sound.add('music-main', { loop: true, volume: 0.6 });

  if (this.sound.locked) {
    // Show overlay — audio will unlock when user taps
    const overlay = this.add.rectangle(
      this.scale.width / 2, this.scale.height / 2,
      this.scale.width, this.scale.height,
      0x000000, 0.75
    ).setInteractive().setDepth(100);

    this.add.text(
      this.scale.width / 2, this.scale.height / 2,
      'TAP TO START', { fontSize: '32px', color: '#ffffff' }
    ).setOrigin(0.5).setDepth(101);

    this.sound.once(Phaser.Sound.Events.UNLOCKED, () => {
      overlay.destroy();
      this.bgMusic.play();
    });
  } else {
    this.bgMusic.play();
  }
}

デバイスの検出

create(): void {
  const device = this.sys.game.device;
  const isMobile = device.os.android || device.os.iOS;
  const isTablet = isMobile && Math.min(window.innerWidth, window.innerHeight) > 600;
  const hasTouch = device.input.touch;

  if (isMobile) {
    this.createVirtualControls();
  } else {
    // Show keyboard hint for desktop
    this.add.text(16, this.scale.height - 30, 'WASD / Arrow Keys to move', {
      fontSize: '14px', color: '#aaaaaa',
    });
  }
}

private createVirtualControls(): void {
  // See references/mobile-patterns.md for VirtualGamepad class
}

その他の便利なデバイスフラグ:

  • device.os.windows、device.os.macOS、device.os.linux
  • device.browser.chrome、device.browser.safari、device.browser.firefox
  • device.features.webGL — WebGL サポートのチェック

モバイルでのパフォーマンス

モバイルの GPU と CPU はデスクトップよりも大幅に非力です。ミッドレンジのデバイスで 60fps を維持するために、以下の制約を適用してください。

テクスチャ:

  • アトラスの最大サイズ: 2048×2048 px。古いデバイスとの互換性のために 1024×1024 を推奨します。
  • テクスチャアトラスを使用してください — ドローコール間の GPU ステート切り替えを最小化できます。
  • 一意のテクスチャの総数を減らしてください: 一意のテクスチャが少ないほど、ドローコールが減ります。

物理演算:

  • 動的ボディが少ないほど FPS が向上します。モバイルでは同時に 50 個未満の動的ボディを目標にしてください。
  • 画面外のオブジェクトでは物理演算を無効にしてください。

パーティクル:

  • すべてのエミッターで maxParticles を設定してください。モバイルでの厳しい上限: 最大 50~100 パーティクル。
  • シンプルなパーティクルテクスチャ(単色の四角形/円)を使用してください — 複雑なテクスチャはコストが高くなります。

カメラエフェクト:

  • camera.shake() や急速な camera.zoom は GPU 負荷が高いです。控えめに使用してください。
  • シンプルな画面フラッシュ(camera.flash())は問題ありません。

ピクセルアートゲーム:

const config: Phaser.Types.Core.GameConfig = {
  pixelArt: true,       // disables antialiasing — required for crisp pixel art
  roundPixels: true,    // prevents sub-pixel rendering artifacts
  // These two together are a free performance win for pixel-art games
};

デバッグビルドでの FPS 監視:

// Show FPS counter during development
create(): void {
  if (import.meta.env.DEV) {
    this.add.text(4, 4, '', { fontSize: '12px', color: '#00ff00' })
      .setDepth(999)
      .setScrollFactor(0);
    // Update in update():
    // fpsText.setText(`FPS: ${Math.round(this.game.loop.actualFps)}`);
  }
}

60fps を目標にしてください。60 を維持できない場合は、physics.arcade.fps: 30 と this.game.loop.targetFps = 30 で 30fps を受け入れてください — カクカクした 45fps は、滑らかな 30fps よりも悪いです。

Capacitor へのデプロイ(iOS / Android)

Capacitor は Web ビルドをネイティブの WebView でラップし、実際のアプリストア用バイナリを提供します。

# Install Capacitor
npm install @capacitor/core @capacitor/cli @capacitor/ios @capacitor/android

# Initialize (run once)
npx cap init "My Game" com.mygame.app --web-dir=dist

# Build the web project
npm run build

# Add native platforms (run once)
npx cap add ios
npx cap add android

# Sync web build into native projects
npx cap sync

# Open in Xcode (requires macOS + Xcode)
npx cap open ios

# Open in Android Studio
npx cap open android

cap open ios の後、Xcode の Signing & Capabilities タブで Team を設定し、デバイスまたはシミュレーターにビルドします。

Capacitor 設定(capacitor.config.ts)
import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.mygame.app',
  appName: 'My Game',
  webDir: 'dist',
  server: {
    androidScheme: 'https',
  },
  plugins: {
    SplashScreen: {
      launchShowDuration: 0,  // Hide splash immediately for games with their own loading screen
    },
  },
};

export default config;
Capacitor プラグインによるネイティブ機能
import { Haptics, ImpactStyle } from '@capacitor/haptics';
import { ScreenOrientation } from '@capacitor/screen-orientation';

// Vibration on hit
await Haptics.impact({ style: ImpactStyle.Medium });

// Lock orientation (native — more reliable than screen.orientation.lock on mobile)
await ScreenOrientation.lock({ orientation: 'landscape' });

PWA セットアップ

2 つのファイルで、ゲームをインストール可能なプログレッシブウェブアプリにできます。

// public/manifest.json
{
  "name": "My Game",
  "short_name": "MyGame",
  "description": "An awesome Phaser 4 game",
  "display": "fullscreen",
  "orientation": "landscape",
  "background_color": "#000000",
  "theme_color": "#000000",
  "start_url": "./index.html",
  "icons": [
    { "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

<link rel="manifest" href="./manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="theme-color" content="#000000">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

オフラインサポートを備えた完全な PWA にするには、サービスワーカーを追加します(Vite の vite-plugin-pwa がこれを自動的に処理します)。

npm install -D vite-plugin-pwa
// vite.config.ts
import { VitePWA } from 'vite-plugin-pwa';

export default {
  plugins: [
    VitePWA({
      registerType: 'autoUpdate',
      manifest: false, // use your own public/manifest.json
      workbox: {
        globPatterns: ['**/*.{js,css,html,png,ogg,mp3,json}'],
      },
    }),
  ],
};

デバイス固有のプロファイル

注意点と推奨設定を含む完全なプロファイルについては、references/device-profiles.md を参照してください。

クイック判断マトリクス
判断項目iOS SafariAndroid ChromeデスクトップCapacitorPWA
音声フォーマットの優先度mp3(ogg 非対応)mp3+ogg任意mp3+oggmp3+ogg
最大アトラスサイズ2048x20482048x2048(ローエンド)4096+2048x20482048x2048
画面の向きの固定Capacitor のみFullscreen API該当なしネイティブプラグインmanifest.json
仮想コントロール必須必須任意必須必須
触覚フィードバックなしなしなしあり(プラグイン)なし
オフラインサポート限定的(50MB)あり該当なし組み込みサービスワーカー
インストールプロンプトホーム画面に追加インストールバナー該当なしApp Storeブラウザのプロンプト
主要プラットフォームの選択
  • Web のみ(itch.io、ポートフォリオ): デスクトップ優先の設定、FIT スケールモード、800x600 または 1280x720
  • モバイル優先: FIT スケールモード、640x360 横向きまたは 360x640 縦向き、仮想コントロール
  • クロスプラットフォーム: FIT スケールモード、実行時にデバイスを検出し、コントロールとアセット品質を適応させる
  • ネイティブアプリ(App Store / Play Store): Capacitor + プラットフォーム固有の最適化

その他のリソース

リファレンスファイル
  • references/mobile-patterns.md — 完全なレスポンシブ GameConfig、VirtualGamepad クラス(ジョイスティック + ボタン)、OrientationGuard オーバーレイ、MobileAudioUnlock コンポーネント、DeviceCapabilities 検出、Capacitor ネイティブプラグイン統合
  • references/device-profiles.md — iOS Safari、Android Chrome、デスクトップ、Capacitor(iOS/Android)、PWA 向けのデバイス固有の最適化プロファイルと、ターゲットごとに推奨される GameConfig

コメント