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让游戏界面适配不同分辨率
  2. 02为移动游戏添加触控操作
  3. 03准备通过 Capacitor 部署 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.RESIZE画布调整为窗口的精确尺寸自适应 UI 游戏(较复杂)
Phaser.Scale.NONE不缩放,保持原始像素尺寸仅限桌面端的游戏

对大多数游戏来说,FIT 是正确的选择。画布会缩放以适应容器,同时保持宽高比;必要时会添加黑边。

RESIZE 可让画布灵活适应尺寸,但要求所有 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。如果无法稳定保持 60fps,可将 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 标签页中设置团队,然后构建并部署到设备或模拟器。

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 设置

只需两个文件,即可将游戏转变为可安装的渐进式 Web 应用(PWA)。

// 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全屏 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

评论

支持 Markdown