@playcraft/cli 0.0.53 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/README.md +21 -0
  2. package/REMIX.md +103 -19
  3. package/dist/atom-tree/fusion-hints.js +8 -3
  4. package/dist/atom-tree/fusion-intent.js +62 -0
  5. package/dist/atom-tree/selection.js +3 -1
  6. package/dist/commands/create.js +439 -159
  7. package/dist/commands/image.js +79 -69
  8. package/dist/commands/levels.js +29 -0
  9. package/dist/commands/patterns.js +75 -0
  10. package/dist/commands/remix.js +47 -0
  11. package/dist/commands/skills.js +48 -17
  12. package/dist/index.js +13 -6
  13. package/dist/remix/clone-source.js +208 -0
  14. package/dist/remix/clone.js +255 -0
  15. package/dist/scaffold/templates.js +35 -0
  16. package/dist/utils/agent-api-client.js +10 -3
  17. package/dist/utils/remix-image-attachments.js +4 -32
  18. package/dist/utils/sandbox-path.js +20 -0
  19. package/package.json +3 -3
  20. package/project-template/.claude/agents/creative-director.md +114 -0
  21. package/project-template/.claude/agents/developer.md +5 -2
  22. package/project-template/.claude/agents/game-designer.md +41 -20
  23. package/project-template/.claude/agents/refs/README.md +8 -0
  24. package/project-template/.claude/agents/refs/creative-director-guide.md +35 -0
  25. package/project-template/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  26. package/project-template/.claude/agents/refs/creative-discovery-guide.md +76 -0
  27. package/project-template/.claude/agents/refs/creative-north-star-examples.md +82 -0
  28. package/project-template/.claude/agents/refs/developer-dev-handoff.md +1 -1
  29. package/project-template/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  30. package/project-template/.claude/agents/refs/game-designer-gameplay-sufficiency.md +4 -4
  31. package/project-template/.claude/agents/reviewer.md +11 -0
  32. package/project-template/.claude/hooks/README.md +12 -12
  33. package/project-template/.claude/hooks/block-developer-preflight.mjs +49 -13
  34. package/project-template/.claude/hooks/block-ring1-tasklist.mjs +1 -1
  35. package/project-template/.claude/hooks/user-prompt.mjs +3 -13
  36. package/project-template/.claude/hooks/validate-workflow-stop.mjs +209 -12
  37. package/project-template/.claude/settings.json +2 -2
  38. package/project-template/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  39. package/project-template/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -1
  40. package/project-template/.claude/snapshot.mjs +0 -1
  41. package/project-template/.codebuddy/settings.json +1 -1
  42. package/project-template/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  43. package/project-template/CLAUDE.md +9 -9
  44. package/project-template/docs/project-state.md +1 -1
  45. package/project-template/docs/team/agent-conduct.md +18 -18
  46. package/project-template/docs/team/collaboration.md +19 -7
  47. package/project-template/docs/team/workflow-changelog.md +15 -0
  48. package/project-template/fusion-patterns/catalog.json +77 -0
  49. package/project-template/package.json +2 -0
  50. package/project-template/skills/_shared/scripts/create-playable.mjs +31 -11
  51. package/project-template/skills/_shared/scripts/dev-start.mjs +3 -3
  52. package/project-template/skills/_shared/scripts/disclosure-config.mjs +65 -39
  53. package/project-template/skills/_shared/scripts/harness-smoke.mjs +3 -1
  54. package/project-template/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  55. package/project-template/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  56. package/project-template/skills/_shared/scripts/slice-set.mjs +29 -2
  57. package/project-template/skills/_shared/scripts/state-get.mjs +124 -7
  58. package/project-template/skills/_shared/scripts/state-store.mjs +25 -2
  59. package/project-template/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  60. package/project-template/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  61. package/project-template/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  62. package/project-template/skills/playable-record/scripts/record-playable.mjs +2 -1
  63. package/project-template/skills/playable-record/scripts/verify-contract.mjs +4 -4
  64. package/project-template/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  65. package/project-template/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  66. package/project-template/skills/playable-record/scripts/verify-slice.mjs +2 -2
  67. package/project-template/templates/design-brief.template.md +34 -0
  68. package/project-template/templates/design-methodology.md +1 -2
  69. package/project-template/templates/design.template.md +65 -0
  70. package/project-template/templates/developer-log.template.md +12 -1
  71. package/project-template/templates/five-axis-framework.md +12 -0
  72. package/project-template/templates/game-designer-creative-process.template.md +99 -0
  73. package/project-template/templates/level-sketch.template.json +56 -0
  74. package/project-template/templates/north-star.template.md +43 -0
  75. package/project-template/templates/project-state.template.md +1 -1
  76. package/project-template/vite.config.ts +4 -5
  77. package/project-templates/phaser/.claude/agents/creative-director.md +114 -0
  78. package/project-templates/phaser/.claude/agents/game-designer.md +41 -20
  79. package/project-templates/phaser/.claude/agents/refs/creative-director-guide.md +35 -0
  80. package/project-templates/phaser/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  81. package/project-templates/phaser/.claude/agents/refs/creative-discovery-guide.md +76 -0
  82. package/project-templates/phaser/.claude/agents/refs/creative-north-star-examples.md +82 -0
  83. package/project-templates/phaser/.claude/agents/refs/developer-dev-handoff.md +1 -1
  84. package/project-templates/phaser/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  85. package/project-templates/phaser/.claude/agents/reviewer.md +11 -0
  86. package/project-templates/phaser/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  87. package/project-templates/phaser/.claude/hooks/user-prompt.mjs +17 -18
  88. package/project-templates/phaser/.claude/hooks/validate-workflow-stop.mjs +209 -12
  89. package/project-templates/phaser/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  90. package/project-templates/phaser/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  91. package/project-templates/phaser/CLAUDE.md +88 -54
  92. package/project-templates/phaser/docs/DESIGN.md +25 -0
  93. package/project-templates/phaser/docs/project-state.md +1 -1
  94. package/project-templates/phaser/docs/team/collaboration.md +19 -7
  95. package/project-templates/phaser/docs/team/workflow-changelog.md +14 -0
  96. package/project-templates/phaser/fusion-patterns/catalog.json +77 -0
  97. package/project-templates/phaser/package.json +3 -1
  98. package/project-templates/phaser/skills/_shared/scripts/dev-start.mjs +3 -3
  99. package/project-templates/phaser/skills/_shared/scripts/disclosure-config.mjs +65 -39
  100. package/project-templates/phaser/skills/_shared/scripts/harness-smoke.mjs +3 -1
  101. package/project-templates/phaser/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  102. package/project-templates/phaser/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  103. package/project-templates/phaser/skills/_shared/scripts/state-get.mjs +124 -7
  104. package/project-templates/phaser/skills/_shared/scripts/state-store.mjs +25 -2
  105. package/project-templates/phaser/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  106. package/project-templates/phaser/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  107. package/project-templates/phaser/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  108. package/project-templates/phaser/skills/playable-record/scripts/record-playable.mjs +2 -1
  109. package/project-templates/phaser/skills/playable-record/scripts/verify-contract.mjs +4 -4
  110. package/project-templates/phaser/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  111. package/project-templates/phaser/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  112. package/project-templates/phaser/skills/playable-record/scripts/verify-slice.mjs +2 -2
  113. package/project-templates/phaser/templates/design-brief.template.md +42 -11
  114. package/project-templates/phaser/templates/design-methodology.md +1 -2
  115. package/project-templates/phaser/templates/design.template.md +77 -0
  116. package/project-templates/phaser/templates/five-axis-framework.md +50 -1
  117. package/project-templates/phaser/templates/game-designer-creative-process.template.md +99 -0
  118. package/project-templates/phaser/templates/level-sketch.template.json +56 -0
  119. package/project-templates/phaser/templates/north-star.template.md +43 -0
  120. package/project-templates/phaser/templates/project-state.template.md +1 -1
  121. package/project-templates/phaser/vite.config.ts +4 -5
  122. package/project-templates/playable-template-phaser/.playcraftignore +10 -0
  123. package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
  124. package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
  125. package/project-templates/playable-template-phaser/README.md +381 -0
  126. package/project-templates/playable-template-phaser/assets/brain.png +0 -0
  127. package/project-templates/playable-template-phaser/assets/button.png +0 -0
  128. package/project-templates/playable-template-phaser/assets/interact.png +0 -0
  129. package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
  130. package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
  131. package/project-templates/playable-template-phaser/builds.config.js +12 -0
  132. package/project-templates/playable-template-phaser/globals.d.ts +2 -0
  133. package/project-templates/playable-template-phaser/index.html +12 -0
  134. package/project-templates/playable-template-phaser/package.json +45 -0
  135. package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
  136. package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
  137. package/project-templates/playable-template-phaser/src/Game.ts +154 -0
  138. package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
  139. package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
  140. package/project-templates/playable-template-phaser/src/index.css +18 -0
  141. package/project-templates/playable-template-phaser/src/index.html +9 -0
  142. package/project-templates/playable-template-phaser/src/index.ts +30 -0
  143. package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
  144. package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
  145. package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
  146. package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
  147. package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
  148. package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
  149. package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
  150. package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
  151. package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
  152. package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
  153. package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
  154. package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
  155. package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
  156. package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
  157. package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
  158. package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
  159. package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
  160. package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
  161. package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
  162. package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
  163. package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
  164. package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
  165. package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
  166. package/project-templates/playable-template-phaser/tsconfig.json +26 -0
  167. package/project-templates/playable-template-phaser/vite.config.ts +64 -0
  168. package/project-templates/playable-template-phaser//346/226/260/346/211/213/345/274/200/345/217/221/346/214/207/345/215/227.md +209 -0
  169. package/project-templates/playable-template-three/README.md +795 -0
  170. package/project-templates/playable-template-three/assets/Stork.glb +0 -0
  171. package/project-templates/playable-template-three/assets/button.png +0 -0
  172. package/project-templates/playable-template-three/assets/logo.webp +0 -0
  173. package/project-templates/playable-template-three/build.json +7 -0
  174. package/project-templates/playable-template-three/builds.config.example.js +151 -0
  175. package/project-templates/playable-template-three/builds.config.js +158 -0
  176. package/project-templates/playable-template-three/package.json +36 -0
  177. package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
  178. package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
  179. package/project-templates/playable-template-three/src/Game.ts +215 -0
  180. package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
  181. package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
  182. package/project-templates/playable-template-three/src/global.d.ts +108 -0
  183. package/project-templates/playable-template-three/src/index.css +18 -0
  184. package/project-templates/playable-template-three/src/index.html +9 -0
  185. package/project-templates/playable-template-three/src/index.ts +20 -0
  186. package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
  187. package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
  188. package/project-templates/playable-template-three/src/theme/README.md +225 -0
  189. package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
  190. package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
  191. package/project-templates/playable-template-three/src/theme/current.ts +12 -0
  192. package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
  193. package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
  194. package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
  195. package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
  196. package/project-templates/playable-template-three/tsconfig.json +24 -0
  197. package/project-templates/threejs/.claude/agents/creative-director.md +114 -0
  198. package/project-templates/threejs/.claude/agents/game-designer.md +41 -20
  199. package/project-templates/threejs/.claude/agents/refs/creative-director-guide.md +35 -0
  200. package/project-templates/threejs/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  201. package/project-templates/threejs/.claude/agents/refs/creative-discovery-guide.md +76 -0
  202. package/project-templates/threejs/.claude/agents/refs/creative-north-star-examples.md +82 -0
  203. package/project-templates/threejs/.claude/agents/refs/developer-dev-handoff.md +1 -1
  204. package/project-templates/threejs/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  205. package/project-templates/threejs/.claude/agents/reviewer.md +11 -0
  206. package/project-templates/threejs/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  207. package/project-templates/threejs/.claude/hooks/user-prompt.mjs +17 -18
  208. package/project-templates/threejs/.claude/hooks/validate-workflow-stop.mjs +209 -12
  209. package/project-templates/threejs/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  210. package/project-templates/threejs/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  211. package/project-templates/threejs/CLAUDE.md +88 -54
  212. package/project-templates/threejs/docs/DESIGN.md +25 -0
  213. package/project-templates/threejs/docs/project-state.md +1 -1
  214. package/project-templates/threejs/docs/team/collaboration.md +19 -7
  215. package/project-templates/threejs/docs/team/workflow-changelog.md +14 -0
  216. package/project-templates/threejs/fusion-patterns/catalog.json +77 -0
  217. package/project-templates/threejs/package.json +3 -1
  218. package/project-templates/threejs/skills/_shared/scripts/dev-start.mjs +3 -3
  219. package/project-templates/threejs/skills/_shared/scripts/disclosure-config.mjs +65 -39
  220. package/project-templates/threejs/skills/_shared/scripts/harness-smoke.mjs +3 -1
  221. package/project-templates/threejs/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  222. package/project-templates/threejs/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  223. package/project-templates/threejs/skills/_shared/scripts/state-get.mjs +124 -7
  224. package/project-templates/threejs/skills/_shared/scripts/state-store.mjs +25 -2
  225. package/project-templates/threejs/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  226. package/project-templates/threejs/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  227. package/project-templates/threejs/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  228. package/project-templates/threejs/skills/playable-record/scripts/record-playable.mjs +2 -1
  229. package/project-templates/threejs/skills/playable-record/scripts/verify-contract.mjs +4 -4
  230. package/project-templates/threejs/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  231. package/project-templates/threejs/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  232. package/project-templates/threejs/skills/playable-record/scripts/verify-slice.mjs +2 -2
  233. package/project-templates/threejs/templates/design-brief.template.md +42 -11
  234. package/project-templates/threejs/templates/design-methodology.md +1 -2
  235. package/project-templates/threejs/templates/design.template.md +77 -0
  236. package/project-templates/threejs/templates/five-axis-framework.md +50 -1
  237. package/project-templates/threejs/templates/game-designer-creative-process.template.md +99 -0
  238. package/project-templates/threejs/templates/level-sketch.template.json +56 -0
  239. package/project-templates/threejs/templates/north-star.template.md +43 -0
  240. package/project-templates/threejs/templates/project-state.template.md +1 -1
  241. package/project-templates/threejs/vite.config.ts +4 -5
  242. package/project-template/docs/DESIGN.md +0 -65
@@ -0,0 +1,301 @@
1
+ import * as Phaser from 'phaser';
2
+ import { sdk } from '@playcraft/adsdk';
3
+ import { AudioManager } from '../managers/AudioManager';
4
+ import { GameThemeConfig } from '../config/ThemeConfig';
5
+ // 导入 UI 组件
6
+ import { BrainAgeUI } from '../ui/BrainAgeUI';
7
+ import { CountdownUI } from '../ui/CountdownUI';
8
+ import { ScoreUI } from '../ui/ScoreUI';
9
+ import { SuccessUI } from '../ui/SuccessUI';
10
+ import { PlayButton } from '../ui/PlayButton';
11
+ // 导入大脑图片资源
12
+ import brainIcon from 'assets/brain.png';
13
+ import warnBrainIcon from 'assets/warn_brain.png';
14
+
15
+ /**
16
+ * 主游戏场景
17
+ *
18
+ * 负责:
19
+ * - 游戏主要逻辑
20
+ * - UI 交互
21
+ * - 应用主题配置
22
+ *
23
+ * ========================================
24
+ * 以下为 UI 组件使用示例,请根据实际需求裁剪
25
+ * ========================================
26
+ */
27
+ export class MainScene extends Phaser.Scene {
28
+ private installButton!: Phaser.GameObjects.Container;
29
+ public audioManager!: AudioManager;
30
+
31
+ // --- UI 组件实例(按需选用) ---
32
+ private brainAgeUI?: BrainAgeUI;
33
+ private countdownUI?: CountdownUI;
34
+ private scoreUI?: ScoreUI;
35
+ private isGameOver: boolean = false;
36
+ // 匹配成功次数(用于进度追踪)
37
+ private matchCount: number = 0;
38
+ // 目标匹配次数(进度到 100% 所需次数)
39
+ private readonly totalMatchCount: number = 2;
40
+
41
+ // 挑战是否已开始(用于标记首次交互)
42
+ private challengeStarted: boolean = false;
43
+
44
+ // 演示定时器
45
+ private demoTimer?: number;
46
+
47
+ constructor() {
48
+ super({ key: 'MainScene' });
49
+ }
50
+
51
+ create() {
52
+ // 应用主题背景色
53
+ this.cameras.main.setBackgroundColor(GameThemeConfig.BACKGROUND_COLOR);
54
+
55
+ // 初始化 AudioManager
56
+ this.audioManager = new AudioManager({
57
+ bgmVolume: 0.5,
58
+ effectVolume: 0.6,
59
+ enableBackgroundPause: true,
60
+ watchdogInterval: 500
61
+ });
62
+ this.audioManager.init(this);
63
+
64
+ // ========================================
65
+ // 示例 1:ScoreUI — 积分显示
66
+ // 注意:ScoreUI 已改为纯 DOM 实现
67
+ // ========================================
68
+ this.scoreUI = new ScoreUI(40, 15, 0); // x, y, 初始分数
69
+
70
+ // 启动演示定时器:每秒 +10 分
71
+ this.demoTimer = window.setInterval(() => {
72
+ this.scoreUI?.add(10);
73
+ }, 1000);
74
+
75
+ // ========================================
76
+ // 示例 2:CountdownUI — LED 数码管倒计时(自动开始)
77
+ // 注意:CountdownUI 已改为纯 DOM 实现,创建后自动倒计时
78
+ // 参数:x坐标, y坐标, 配置对象
79
+ // 传入 window.innerWidth / 2 实现水平居中
80
+ // ========================================
81
+ this.countdownUI = new CountdownUI(window.innerWidth / 2, 40, {
82
+ initialSeconds: 30,
83
+ warnTime: 10,
84
+ onGameOver: () => this.onGameOver('countdown'),
85
+ });
86
+
87
+ // ========================================
88
+ // 示例 3:BrainAgeUI — 脑龄面板(自动开始)
89
+ // 注意:BrainAgeUI 已改为纯 DOM 实现,创建后自动计时
90
+ // 使用从 assets 导入的图片资源
91
+ // ========================================
92
+ this.brainAgeUI = new BrainAgeUI(30, 80, brainIcon, {
93
+ scale: 0.65,
94
+ }, {
95
+ initial: 60, max: 100, warningAge: 90,
96
+ iconUrl: brainIcon, // 正常状态图片(从 assets 导入)
97
+ warnIconUrl: warnBrainIcon, // 警告状态图片(从 assets 导入)
98
+ onGameOver: () => this.onGameOver('brainAge'),
99
+ });
100
+
101
+ // ========================================
102
+ // Install 按钮(模板自带)
103
+ // ========================================
104
+ this.installButton = this.add.container(this.cameras.main.centerX, this.cameras.main.centerY);
105
+ const animationContainer = this.add.container(0, 0);
106
+ this.installButton.add(animationContainer);
107
+ const buttonBackground = this.add.image(0, 0, 'button');
108
+ buttonBackground.setScale(0.35);
109
+ const installText = this.add
110
+ .text(0, 0, 'Install', {
111
+ fontFamily: 'cursive',
112
+ fontSize: GameThemeConfig.FONT_SIZE_BUTTON,
113
+ color: GameThemeConfig.TEXT_COLOR,
114
+ fontStyle: 'bold'
115
+ })
116
+ .setOrigin(0.5);
117
+ installText.setShadow(4, 4, '#fffc6a', 9, true, true);
118
+ animationContainer.add([buttonBackground, installText]);
119
+ this.tweens.add({
120
+ targets: animationContainer,
121
+ scaleX: 1.1,
122
+ scaleY: 1.1,
123
+ duration: 800,
124
+ yoyo: true,
125
+ repeat: -1,
126
+ ease: 'Sine.easeInOut'
127
+ });
128
+ buttonBackground.setInteractive({ useHandCursor: true });
129
+ buttonBackground.on('pointerdown', () => {
130
+ // 首次点击时标记挑战开始
131
+ if (!this.challengeStarted) {
132
+ this.challengeStarted = true;
133
+ sdk.playcraftChallengeStart();
134
+ }
135
+ sdk.playcraftInstall();
136
+ });
137
+ // 全部加载完成,触发页面可以交互
138
+ sdk.playcraftInteractive();
139
+
140
+ // ========================================
141
+ // 调试按钮:手动触发埋点事件
142
+ // ========================================
143
+ this.createDebugButtons();
144
+ }
145
+
146
+ /**
147
+ * 匹配成功时调用(示例)
148
+ */
149
+ onMatchSuccess(): void {
150
+ // 首次交互时标记挑战开始
151
+ if (!this.challengeStarted) {
152
+ this.challengeStarted = true;
153
+ sdk.playcraftChallengeStart();
154
+ }
155
+
156
+ this.scoreUI?.add(5);
157
+ this.brainAgeUI?.onMatchSuccess();
158
+ this.matchCount++;
159
+ const percent = Math.min(100, Math.floor((this.matchCount / this.totalMatchCount) * 100));
160
+ sdk.recordPlaycraftChallengeProgress(percent);
161
+ }
162
+
163
+ /**
164
+ * 创建调试按钮(开发测试用)
165
+ */
166
+ private createDebugButtons(): void {
167
+ const buttonWidth = 140;
168
+ const buttonHeight = 50;
169
+ const buttonSpacing = 10;
170
+ const startX = 20;
171
+ // 使用 cameras.main.height 获取相机高度
172
+ const startY = this.cameras.main.height - 180;
173
+
174
+ console.log('[调试按钮] 创建调试按钮,屏幕高度:', this.cameras.main.height);
175
+
176
+ // 按钮配置
177
+ const buttons = [
178
+ {
179
+ text: '挑战开始',
180
+ color: 0x9C27B0,
181
+ hoverColor: 0xAB47BC,
182
+ onClick: () => {
183
+ if (!this.challengeStarted) {
184
+ this.challengeStarted = true;
185
+ sdk.playcraftChallengeStart();
186
+ console.log('[调试] 手动触发挑战开始');
187
+ } else {
188
+ console.log('[调试] 挑战已经开始过了');
189
+ }
190
+ }
191
+ },
192
+ {
193
+ text: '匹配成功',
194
+ color: 0x4CAF50,
195
+ hoverColor: 0x66BB6A,
196
+ onClick: () => this.onMatchSuccess()
197
+ },
198
+ {
199
+ text: '发送成功',
200
+ color: 0x2196F3,
201
+ hoverColor: 0x42A5F5,
202
+ onClick: () => sdk.playcraftRecordSuccess()
203
+ },
204
+ {
205
+ text: '发送失败',
206
+ color: 0xF44336,
207
+ hoverColor: 0xEF5350,
208
+ onClick: () => sdk.playcraftRecordFailed()
209
+ }
210
+ ];
211
+
212
+ buttons.forEach((config, index) => {
213
+ const y = startY + index * (buttonHeight + buttonSpacing);
214
+
215
+ // 创建按钮背景(直接创建,不使用 container)
216
+ const background = this.add.rectangle(startX + buttonWidth / 2, y + buttonHeight / 2, buttonWidth, buttonHeight, config.color, 0.9);
217
+ background.setStrokeStyle(2, 0xffffff, 0.8);
218
+ background.setDepth(1000); // 设置高层级,确保在最上层
219
+
220
+ // 创建按钮文字
221
+ const text = this.add.text(startX + buttonWidth / 2, y + buttonHeight / 2, config.text, {
222
+ fontFamily: 'Arial',
223
+ fontSize: '16px',
224
+ color: '#ffffff',
225
+ fontStyle: 'bold'
226
+ }).setOrigin(0.5);
227
+ text.setDepth(1001); // 文字层级比背景高
228
+
229
+ // 添加交互
230
+ background.setInteractive({ useHandCursor: true });
231
+
232
+ background.on('pointerover', () => {
233
+ background.setFillStyle(config.hoverColor, 0.9);
234
+ background.setScale(1.05);
235
+ text.setScale(1.05);
236
+ });
237
+
238
+ background.on('pointerout', () => {
239
+ background.setFillStyle(config.color, 0.9);
240
+ background.setScale(1);
241
+ text.setScale(1);
242
+ });
243
+
244
+ background.on('pointerdown', () => {
245
+ background.setScale(0.95);
246
+ text.setScale(0.95);
247
+ });
248
+
249
+ background.on('pointerup', () => {
250
+ background.setScale(1.05);
251
+ text.setScale(1.05);
252
+ config.onClick();
253
+ console.log(`[调试按钮] ${config.text} 被点击`);
254
+ });
255
+
256
+ console.log(`[调试按钮] 创建按钮 "${config.text}",位置: (${startX + buttonWidth / 2}, ${y + buttonHeight / 2})`);
257
+ });
258
+ }
259
+
260
+ /**
261
+ * 游戏通关时调用(示例)
262
+ */
263
+ onGameWin(): void {
264
+ new SuccessUI(this).show();
265
+ }
266
+
267
+ /**
268
+ * 游戏结束处理(示例)
269
+ */
270
+ onGameOver(reason: 'countdown' | 'brainAge'): void {
271
+ if (this.isGameOver) return;
272
+ this.isGameOver = true;
273
+
274
+ if (reason === 'countdown') {
275
+ this.countdownUI?.showGameOver();
276
+ } else {
277
+ this.brainAgeUI?.showGameOver();
278
+ }
279
+
280
+ new PlayButton(this).show(
281
+ this.scale.width / 2,
282
+ this.scale.height / 2 + 60,
283
+ 0.45,
284
+ 302,
285
+ );
286
+ }
287
+
288
+ update() {
289
+ if (this.installButton) {
290
+ this.installButton.setPosition(this.cameras.main.centerX, this.cameras.main.centerY);
291
+ }
292
+ }
293
+
294
+ shutdown() {
295
+ // 清理演示定时器
296
+ if (this.demoTimer) {
297
+ clearInterval(this.demoTimer);
298
+ this.demoTimer = undefined;
299
+ }
300
+ }
301
+ }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Preload 场景(原 Loading 场景)
3
+ * 显示 App Logo、名称、评分、描述和加载动画
4
+ * 支持横竖屏自适应
5
+ *
6
+ * 负责预加载所有游戏资源,加载完成后启动 MainScene
7
+ */
8
+ import * as Phaser from 'phaser';
9
+ import { sdk } from '@playcraft/adsdk';
10
+ import { LoadingConfig } from '../config/LoadingConfig';
11
+ // 导入所有需要预加载的资源
12
+ import buttonBg from 'assets/button.png';
13
+ // UI 组件依赖的资源
14
+ import interactImage from 'assets/interact.png';
15
+ import brainIcon from 'assets/brain.png';
16
+ import warnBrainIcon from 'assets/warn_brain.png';
17
+
18
+ export class PreloadScene extends Phaser.Scene {
19
+ // UI 元素
20
+ private logoElement!: HTMLImageElement;
21
+ private loadingText!: Phaser.GameObjects.Text;
22
+ private gamepadIcon!: Phaser.GameObjects.Text;
23
+
24
+ // 动画相关
25
+ private dotCount: number = 0;
26
+ private dotTimer!: Phaser.Time.TimerEvent;
27
+
28
+ // 加载状态
29
+ private minTimeReached: boolean = false;
30
+ private resourcesLoaded: boolean = false;
31
+ private interactLoaded: boolean = false;
32
+
33
+ constructor() {
34
+ super({ key: 'PreloadScene' });
35
+ }
36
+
37
+ preload(): void {
38
+ // 在这里预加载所有游戏资源
39
+ this.load.image('button', buttonBg);
40
+ // UI 组件依赖的资源
41
+ this.load.image('brain', brainIcon);
42
+ this.load.image('warn_brain', warnBrainIcon);
43
+
44
+ // 监听加载完成事件
45
+ this.load.on('complete', () => {
46
+ console.log('Resources loaded');
47
+ this.resourcesLoaded = true;
48
+ this.tryStartGame();
49
+ });
50
+
51
+ // 手动加载 interact 合图(通过 Image 对象,包含 playBtn、dangerTip 等帧)
52
+ const interactImg = new Image();
53
+ interactImg.onload = () => {
54
+ this.textures.addImage('interact', interactImg);
55
+ const tex = this.textures.get('interact');
56
+ // 定义合图帧区域(x, y, width, height)
57
+ tex.add('successBg', 0, 20, 13, 650, 440);
58
+ tex.add('playBtn', 0, 710, 278, 685, 200);
59
+ tex.add('failBg', 0, 20, 504, 650, 480);
60
+ tex.add('finger', 0, 1232, 588, 400, 400);
61
+ tex.add('aperture', 0, 1650, 850, 160, 160);
62
+ tex.add('dangerTip', 0, 700, 480, 530, 530);
63
+ console.log('Interact atlas loaded');
64
+ this.interactLoaded = true;
65
+ this.tryStartGame();
66
+ };
67
+ interactImg.src = interactImage;
68
+ }
69
+
70
+ create(): void {
71
+ this.createUI();
72
+
73
+ // 最小显示时间计时器
74
+ this.time.delayedCall(LoadingConfig.SCENE_DURATION, () => {
75
+ console.log('Min time reached');
76
+ this.minTimeReached = true;
77
+ this.tryStartGame();
78
+ });
79
+ }
80
+
81
+ private createUI(): void {
82
+ const width = this.scale.width;
83
+ const height = this.scale.height;
84
+ const isLandscape = width > height;
85
+ const centerX = width / 2;
86
+
87
+ // 根据画布尺寸计算缩放比例(基于设计尺寸)
88
+ const baseWidth = isLandscape ? 667 : 375;
89
+ const baseHeight = isLandscape ? 375 : 667;
90
+ const scaleX = width / baseWidth;
91
+ const scaleY = height / baseHeight;
92
+ const scale = Math.min(scaleX, scaleY, 2); // 限制最大缩放
93
+
94
+ // 根据横竖屏获取基础布局配置,再乘以缩放比例
95
+ const logoSize = Math.round((isLandscape ? 80 : 120) * scale);
96
+ const titleFontSize = Math.round((isLandscape ? 24 : 32) * scale);
97
+ const starsFontSize = Math.round((isLandscape ? 18 : 24) * scale);
98
+ const descFontSize = Math.round((isLandscape ? 12 : 14) * scale);
99
+ const descMaxWidth = Math.round((isLandscape ? 300 : 280) * scale);
100
+ const iconFontSize = Math.round((isLandscape ? 18 : 24) * scale);
101
+ const loadingFontSize = Math.round((isLandscape ? 14 : 18) * scale);
102
+ const gap = Math.round((isLandscape ? 8 : 12) * scale);
103
+
104
+ // 位置计算
105
+ const logoY = height * (isLandscape ? 0.35 : 0.28);
106
+ const titleY = logoY + logoSize / 2 + Math.round((isLandscape ? 15 : 25) * scale);
107
+ const starsY = titleY + Math.round((isLandscape ? 25 : 35) * scale);
108
+ const descY = starsY + Math.round((isLandscape ? 20 : 30) * scale);
109
+ const loadingY = height * (isLandscape ? 0.85 : 0.88);
110
+
111
+ // Logo - 使用 DOM 元素显示,避免 canvas 缩放锯齿
112
+ this.createDomLogo(centerX, logoY, logoSize);
113
+
114
+ // 标题
115
+ this.add.text(centerX, titleY, LoadingConfig.APP_TITLE, {
116
+ fontFamily: 'Arial, sans-serif',
117
+ fontSize: `${titleFontSize}px`,
118
+ color: '#ffffff',
119
+ fontStyle: 'bold'
120
+ }).setOrigin(0.5);
121
+
122
+ // 五星评分
123
+ this.add.text(centerX, starsY, LoadingConfig.APP_STARS, {
124
+ fontFamily: 'Arial, sans-serif',
125
+ fontSize: `${starsFontSize}px`,
126
+ color: '#ffc107'
127
+ }).setOrigin(0.5);
128
+
129
+ // 描述文字
130
+ this.add.text(centerX, descY, LoadingConfig.APP_DESC, {
131
+ fontFamily: 'Arial, sans-serif',
132
+ fontSize: `${descFontSize}px`,
133
+ color: '#888888',
134
+ align: 'center',
135
+ wordWrap: { width: descMaxWidth }
136
+ }).setOrigin(0.5, 0);
137
+
138
+ // 游戏手柄图标 + Loading 文字(组合居中)
139
+ // 先创建文字获取宽度
140
+ this.loadingText = this.add.text(0, 0, `${LoadingConfig.LOADING_TEXT}...`, {
141
+ fontFamily: 'Arial, sans-serif',
142
+ fontSize: `${loadingFontSize}px`,
143
+ color: '#888888'
144
+ });
145
+
146
+ // 计算组合总宽度并居中
147
+ const textWidth = this.loadingText.width;
148
+ const iconWidth = iconFontSize;
149
+ const totalWidth = iconWidth + gap + textWidth;
150
+ const startX = centerX - totalWidth / 2;
151
+
152
+ // 放置图标
153
+ this.gamepadIcon = this.add.text(startX + iconWidth / 2, loadingY, LoadingConfig.GAMEPAD_ICON, {
154
+ fontFamily: 'Arial, sans-serif',
155
+ fontSize: `${iconFontSize}px`
156
+ }).setOrigin(0.5);
157
+
158
+ // 添加跳跃动画
159
+ this.startBounceAnimation(loadingY, isLandscape, scale);
160
+
161
+ // 放置文字(紧跟图标后面)
162
+ this.loadingText.setPosition(startX + iconWidth + gap, loadingY);
163
+ this.loadingText.setOrigin(0, 0.5);
164
+
165
+ // 启动点号动画
166
+ this.startDotAnimation();
167
+ }
168
+
169
+ // 尝试启动游戏(需要满足两个条件:资源加载完成 && 最小显示时间已到)
170
+ private tryStartGame(): void {
171
+ if (this.resourcesLoaded && this.minTimeReached && this.interactLoaded) {
172
+ console.log('All conditions met, starting MainScene');
173
+ // 移除 DOM logo 元素
174
+ this.removeDomLogo();
175
+ // 停止当前场景
176
+ this.scene.stop('PreloadScene');
177
+ // 启动主游戏场景
178
+ this.scene.start('MainScene');
179
+ // 通知 SDK 游戏开始
180
+ sdk.playcraftStart();
181
+ }
182
+ }
183
+
184
+ private startDotAnimation(): void {
185
+ this.dotTimer = this.time.addEvent({
186
+ delay: LoadingConfig.DOT_ANIM_DELAY,
187
+ callback: () => {
188
+ this.dotCount = (this.dotCount + 1) % 4;
189
+ const dots = '.'.repeat(this.dotCount);
190
+ this.loadingText.setText(LoadingConfig.LOADING_TEXT + dots);
191
+ },
192
+ loop: true
193
+ });
194
+ }
195
+
196
+ // 图标跳跃动画
197
+ private startBounceAnimation(baseY: number, isLandscape: boolean, scale: number): void {
198
+ const bounceHeight = Math.round((isLandscape ? LoadingConfig.BOUNCE_HEIGHT_LANDSCAPE : LoadingConfig.BOUNCE_HEIGHT_PORTRAIT) * scale);
199
+
200
+ this.tweens.add({
201
+ targets: this.gamepadIcon,
202
+ y: baseY - bounceHeight,
203
+ duration: LoadingConfig.BOUNCE_DURATION,
204
+ ease: 'Quad.easeOut',
205
+ yoyo: true,
206
+ repeat: -1,
207
+ repeatDelay: LoadingConfig.BOUNCE_REPEAT_DELAY
208
+ });
209
+ }
210
+
211
+ shutdown(): void {
212
+ if (this.dotTimer) {
213
+ this.dotTimer.destroy();
214
+ }
215
+ this.removeDomLogo();
216
+ }
217
+
218
+ // 移除 DOM logo 元素
219
+ private removeDomLogo(): void {
220
+ if (this.logoElement && this.logoElement.parentNode) {
221
+ this.logoElement.parentNode.removeChild(this.logoElement);
222
+ }
223
+ }
224
+
225
+ // 创建 DOM logo 元素
226
+ private createDomLogo(centerX: number, centerY: number, size: number): void {
227
+ // 获取 canvas 元素和其在页面中的位置
228
+ const canvas = this.game.canvas;
229
+ const canvasRect = canvas.getBoundingClientRect();
230
+
231
+ // 计算缩放比例(canvas 实际显示大小 vs 游戏内部尺寸)
232
+ const scaleX = canvasRect.width / this.scale.width;
233
+ const scaleY = canvasRect.height / this.scale.height;
234
+
235
+ // 计算 logo 在页面中的实际位置和大小
236
+ const actualSize = size * scaleX;
237
+ const actualX = canvasRect.left + (centerX * scaleX) - (actualSize / 2);
238
+ const actualY = canvasRect.top + (centerY * scaleY) - (actualSize / 2);
239
+
240
+ // 创建 img 元素
241
+ this.logoElement = document.createElement('img');
242
+ this.logoElement.src = LoadingConfig.LOGO_IMAGE;
243
+ this.logoElement.style.cssText = `
244
+ position: absolute;
245
+ left: ${actualX}px;
246
+ top: ${actualY}px;
247
+ width: ${actualSize}px;
248
+ height: ${actualSize}px;
249
+ border-radius: 15%;
250
+ pointer-events: none;
251
+ z-index: 1000;
252
+ `;
253
+
254
+ document.body.appendChild(this.logoElement);
255
+ }
256
+ }