---
name: phaser.aicomponent
description: Phaser 3 游戏引擎运行时 + 完整可运行空项目。提供 13 个文件：三场景生命周期（Boot→Preloader→Game）、自适应布局系统（750×1334 设计基准）、移动端适配（安全区/触摸/HiDPI）、音效工具。安装依赖后 `pnpm dev` 即可看到 Hello Phaser 画面。
triggers: 需要初始化一个新的 Phaser 试玩广告项目、添加游戏引擎基础层，或配置 Phaser 渲染器和 Scene 时触发。
---

# Phaser 3 游戏引擎（Phaser Engine + Complete Project Skeleton）

## 说明

本 skill 是所有其他 Phaser skill 的基础。它贡献的 **13 个文件**构成了一个**完整可运行的 Phaser 空项目**，开箱即用：

```bash
pnpm install
pnpm dev      # 开发模式 → 浏览器显示 "Hello Phaser!" + 布局参考信息
pnpm build    # 构建产物
```

**已内置的基础能力：**

- **三场景生命周期**：Boot（全局初始化）→ Preloader（进度条 + 资源加载）→ Game（主场景）
- **自适应布局系统**：`computeUiLayout()` 基于 750×1334 设计稿，计算 `uiScale` / `vScale` / `centerX` / `centerY`
- **移动端适配**：viewport meta（禁缩放）、安全区 padding、禁止选中/回弹/长按菜单、2× HiDPI 画布
- **音效工具**：`safePlaySound()` 封装安全音效播放
- **TypeScript 完整支持**：资源模块声明（`*.webp` / `*.mp3` 等）+ `__DEV__` 全局变量

**UI 布局数学**以 `responsive_2d_layout.aicomponent` 的 **SKILL.md 第一节 Layout Canon** 为规范原文；本 skill 的 `UiLayout.ts` 为该规范的 Phaser 绑定实现。

---

## Scaffold

| 目标路径 | 来源 | 说明 |
|---------|-----|-----|
| `src/index.ts` | `ref/main.ts` | **webpack 入口**：import CSS → DOMContentLoaded → StartGame + resize reload |
| `src/index.html` | `ref/index.html` | **HTML 容器**：viewport meta + 安全区 + `#game-container` |
| `src/index.css` | `ref/index.css` | **全屏样式**：移动端适配、安全区 padding、禁止选中/回弹 |
| `globals.d.ts` | `ref/globals.d.ts` | **类型声明**：资源模块（图片/音频）+ `__DEV__` 全局 |
| `tsconfig.json` | `ref/tsconfig.json` | **TypeScript 配置**：target es5, moduleResolution bundler, paths assets/* |
| `src/game/index.ts` | `ref/index.ts` | **`StartGame(parent)`**：2× 设计分辨率 + Scale.FIT + 三场景注册 |
| `src/game/GameConfig.ts` | `ref/GameConfig.ts` | **游戏配置**：设计稿基准、UI 缩放范围、安全区、调试开关 |
| `src/game/SceneKeys.ts` | `ref/SceneKeys.ts` | **场景名枚举**：Boot / Preloader / Game |
| `src/game/scenes/BootScene.ts` | `ref/BootScene.ts` | **Boot 场景**：禁右键菜单 → 路由到 Preloader |
| `src/game/scenes/PreloaderScene.ts` | `ref/PreloaderScene.ts` | **Preloader 场景**：进度条 + Loading 文字 → 切到 Game |
| `src/game/scenes/GameScene.ts` | `ref/GameScene.ts` | **Game 场景**：Hello Phaser + 布局参考（十字线/安全区框/坐标信息） |
| `src/game/utils/UiLayout.ts` | `ref/UiLayout.ts` | **自适应布局**：`computeUiLayout()` — uiScale/vScale/centerX/centerY |
| `src/game/utils/SoundUtils.ts` | `ref/SoundUtils.ts` | **音效工具**：`safePlaySound()` — 缺失键静默返回 |

`package.json` 新增：`"phaser": "^3.90.0"`

---

## 空项目文件结构

安装本 skill 后 Remix 项目的完整文件树：

```
<remix-project>/
├── src/
│   ├── index.ts              ← webpack 入口
│   ├── index.html            ← HTML 容器（含 viewport meta）
│   ├── index.css             ← 全屏 + 移动端适配样式
│   └── game/
│       ├── index.ts          ← StartGame()
│       ├── GameConfig.ts     ← 通用游戏配置
│       ├── SceneKeys.ts      ← 场景名枚举
│       ├── scenes/
│       │   ├── BootScene.ts      ← 全局初始化 → 路由
│       │   ├── PreloaderScene.ts ← 资源加载 + 进度条
│       │   └── GameScene.ts      ← Hello Phaser（主场景骨架）
│       └── utils/
│           ├── UiLayout.ts       ← 自适应布局系统
│           └── SoundUtils.ts     ← 安全音效播放
├── assets/                   ← 资产目录（游戏图片/音频放这里）
├── globals.d.ts              ← 完整类型声明
├── tsconfig.json             ← TypeScript 配置
├── package.json              ← phaser 依赖
└── webpack.config.js         ← 构建配置（由 webpack_build.aicomponent 提供）
```

---

## 文件详解

### src/index.ts（webpack 入口）

```typescript
import "./index.css";
import { StartGame } from "./game";

document.addEventListener("DOMContentLoaded", () => {
  StartGame("game-container");

  window.addEventListener("resize", () => {
    window.location.reload();
  });
});
```

### src/game/index.ts（StartGame 函数）

```typescript
import { BootScene } from "./scenes/BootScene";
import { PreloaderScene } from "./scenes/PreloaderScene";
import { GameScene } from "./scenes/GameScene";

export const StartGame = (parent: string): Phaser.Game => {
  const DESIGN_SCALE = 2;
  const designWidth  = Math.round((window.innerWidth  || 375) * DESIGN_SCALE);
  const designHeight = Math.round((window.innerHeight || 667) * DESIGN_SCALE);

  const config: Phaser.Types.Core.GameConfig = {
    type: Phaser.WEBGL,
    parent,
    width: designWidth,
    height: designHeight,
    backgroundColor: 0xffffff,
    scene: [BootScene, PreloaderScene, GameScene],
    scale: {
      mode: Phaser.Scale.FIT,
      autoCenter: Phaser.Scale.CENTER_BOTH,
    },
    input: { activePointers: 2 },
  };

  return new Phaser.Game(config);
};
```

### src/game/GameConfig.ts（通用配置）

```typescript
export const GameConfig = {
  baseWidth: 750,        // 设计稿宽度
  baseHeight: 1334,      // 设计稿高度
  minScale: 0.7,         // UI 最小缩放
  maxScale: 1.4,         // UI 最大缩放
  safeAreaTop: 0,        // 安全区顶部偏移 (dp)
  safeAreaBottom: 0,     // 安全区底部偏移 (dp)
  debugScene: "game",    // 启动场景路由
  showFps: false,        // FPS 显示开关
  settings: {
    musicOn: true,
    soundOn: true,
  },
};
```

### src/game/utils/UiLayout.ts（自适应布局）

```typescript
export function computeUiLayout(sceneOrCamera, config = GameConfig): UiLayoutResult {
  // uiScale = clamp(min(Wr/Wd, Hr/Hd), minScale, maxScale)
  // vScale  = Hr/Hd
  // centerX = Wr/2, centerY = Hr/2
  // isLandscape = width > height
}
```

**使用示例**：

```typescript
import { computeUiLayout } from '../utils/UiLayout';

// 在任意 Scene.create() 中
const { width, height, uiScale, vScale, centerX, centerY, isLandscape } = computeUiLayout(this);

// UI 元素定位
const buttonY = height - 80 * vScale;    // 距底 80dp
const iconSize = 48 * uiScale;           // 48dp 图标
const title = this.add.text(centerX, 100 * vScale, 'Title', {
  fontSize: `${32 * uiScale}px`,
}).setOrigin(0.5);
```

### src/game/scenes/BootScene.ts（全局初始化）

```typescript
export class BootScene extends Scene {
  create(): void {
    this.input.mouse?.disableContextMenu();
    this.scene.start(SceneKeys.Preloader);  // 或根据 GameConfig.debugScene 路由
  }
}
```

### src/game/scenes/PreloaderScene.ts（通用加载）

```typescript
export class PreloaderScene extends Scene {
  preload(): void {
    // 自动显示进度条
    // 在此处添加 this.load.image / this.load.audio 调用
  }
  create(): void {
    this.scene.start(SceneKeys.Game);
  }
}
```

### src/game/scenes/GameScene.ts（空白主场景）

显示 "Hello Phaser!" + 十字参考线 + 安全区框 + 布局参数信息。
作为开发起点，替换 `create()` 内容即可开始编写游戏逻辑。

---

## 坐标系与布局规范

### 坐标系

- **原点**：画布左上角 (0, 0)
- **x 向右，y 向下**（标准 2D 画布/Phaser 约定）
- **设计空间**：750 × 1334 dp（竖屏基准）

### 缩放系数（Layout Canon）

| 系数 | 公式 | 用途 |
|------|------|------|
| `scaleX` | `Wr / Wd` | 水平缩放比 |
| `scaleY` | `Hr / Hd` | 垂直缩放比 |
| `uiScale` | `clamp(min(scaleX, scaleY), minScale, maxScale)` | **UI 元素尺寸**（字号、间距、图标边长） |
| `vScale` | `scaleY` | **垂向距离**（距底/距顶的 dp 换算） |
| `centerX/Y` | `Wr/2, Hr/2` | 居中锚点 |

### 使用规则

1. **同一画面只算一次** `computeUiLayout()`，全 UI 共用
2. 宽高/圆角/字号 → 乘 `uiScale`
3. 距底/距顶距离 → 乘 `vScale`
4. 居中偏移 → `centerX/Y` + `偏移 × uiScale`
5. 安全区先扣减 `safeAreaTop/Bottom`，再代入公式

---

## 移动端适配清单

| 特性 | 实现位置 | 说明 |
|------|---------|------|
| **HiDPI 清晰度** | `StartGame()` | viewport × 2 设计分辨率 + Scale.FIT 缩小 |
| **禁止页面缩放** | `index.html` | `viewport` meta：`user-scalable=no, maximum-scale=1` |
| **iOS 全屏** | `index.html` | `apple-mobile-web-app-capable` + `black-translucent` 状态栏 |
| **安全区（刘海屏）** | `index.css` | `env(safe-area-inset-*)` padding |
| **禁止文字选中** | `index.css` | `user-select: none` |
| **禁止长按菜单** | `index.css` | `-webkit-touch-callout: none` |
| **禁止 iOS 回弹** | `index.css` | `overscroll-behavior: none; touch-action: none` |
| **禁止点击高亮** | `index.css` | `-webkit-tap-highlight-color: transparent` |
| **禁止右键菜单** | `BootScene.ts` | `this.input.mouse?.disableContextMenu()` |
| **双指支持** | `StartGame()` | `activePointers: 2`（pinch zoom 预留） |
| **横竖屏感知** | `UiLayout.ts` | `isLandscape` 字段 |
| **resize 响应** | `index.ts` | reload 重新计算设计分辨率 |

---

## Imports

本 skill 是所有 Phaser 组件的基础，无 skill 层依赖。

**配套 skill（推荐一起使用）：**
- `webpack_build.aicomponent` — 提供 webpack.config.js + devDependencies（pnpm dev / pnpm build）
- `playable_scripts_build.aicomponent` — 腾讯 playable-scripts 构建管线（替代 webpack_build）

**不再需要单独安装**（已内置）：
- ~~`typescript_module_patterns.aicomponent`~~ — 资源模块声明已合入 `globals.d.ts`
- ~~`responsive_2d_layout.aicomponent`~~ 的 scaffold 部分 — `UiLayout.ts` 已内置（SKILL.md 规范文档仍然有效）
- ~~`sound_utils.aicomponent`~~ 的基础版 — `SoundUtils.ts` 已内置（增强版如 BGM 管理仍可独立使用）
- ~~`phaser_scene_lifecycle.aicomponent`~~ 的场景骨架 — Boot/Preloader/Game 三场景已内置

## Skill Definition

```yaml
tools:
  - read_file
  - write_file
inputs:
  - source: ref/main.ts          (webpack entry)
  - source: ref/index.html       (HTML container with viewport meta)
  - source: ref/index.css        (fullscreen + mobile styles)
  - source: ref/globals.d.ts     (asset module declarations + __DEV__)
  - source: ref/tsconfig.json    (TypeScript config)
  - source: ref/index.ts         (StartGame function)
  - source: ref/GameConfig.ts    (generic game config)
  - source: ref/SceneKeys.ts     (scene key enum)
  - source: ref/BootScene.ts     (boot scene)
  - source: ref/PreloaderScene.ts (preloader scene)
  - source: ref/GameScene.ts     (game scene skeleton)
  - source: ref/UiLayout.ts      (responsive layout system)
  - source: ref/SoundUtils.ts    (safe sound playback)
outputs:
  - projectSkeleton: 13 files forming a complete runnable Phaser project
  - phaserDep: phaser ^3.90.0 npm dependency
  - sceneLifecycle: Boot → Preloader → Game three-scene architecture
  - responsiveLayout: computeUiLayout() with uiScale/vScale/center
  - mobileAdaptation: viewport meta, safe areas, touch handling, HiDPI
```

## Recipe

| 决策 | 原因 |
|-----|-----|
| **2× 设计分辨率** | 在 HiDPI 屏上用更高物理像素渲染，再通过 Scale.FIT 缩小，避免模糊 |
| **resize 时 reload** | 最简单可靠的方式重新计算设计分辨率；试玩广告不需要复杂的响应式 |
| **activePointers: 2** | 允许双指缩放手势（InputHandler 实现 pinch zoom） |
| **三场景架构** | Boot 负责全局初始化（只跑一次），Preloader 负责资源加载，Game 为主场景 |
| **Layout Canon 内置** | computeUiLayout 是所有 UI 定位的基础，不应是可选依赖 |
| **GameConfig 纯通用** | 去掉所有 arrow-puzzle 专属常量（棋盘/箭头/路径），具体游戏 skill 自行声明 |
| **globals.d.ts 合并模块声明** | 减少 skill 依赖数量，一个 skill 出完整项目 |

## Adapter

- **Role**: `phaserEngine` — Phaser 3 游戏引擎基座，所有 Phaser 系 skill 的运行时依赖
- **Provides**: `StartGame()` 启动入口、`SceneKeys` 枚举、`GameConfig` 通用配置、`computeUiLayout()` 自适应布局、`safePlaySound()` 安全音效
- **Requires**: 无 skill 层硬依赖（`webpack_build.aicomponent` 为配套工具，非运行时依赖）
- **Consumed by**: 所有以 `phaser.aicomponent` 为 Imports 硬依赖的 skill，包括 `grid_board_layout`、`path_renderer`、`path_input_handler`、`tray_container`、`level_lifecycle`、`game_scene` 等
- **Integration point**: `src/game/index.ts` 导出 `StartGame()`，由 `src/index.ts` 在 `DOMContentLoaded` 中调用
