@playcraft/cli 0.0.52 → 0.0.55

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 (79) hide show
  1. package/dist/commands/create.js +164 -98
  2. package/dist/index.js +4 -1
  3. package/dist/scaffold/templates.js +35 -0
  4. package/package.json +3 -3
  5. package/project-templates/playable-template-phaser/.playcraftignore +10 -0
  6. package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
  7. package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
  8. package/project-templates/playable-template-phaser/README.md +381 -0
  9. package/project-templates/playable-template-phaser/assets/brain.png +0 -0
  10. package/project-templates/playable-template-phaser/assets/button.png +0 -0
  11. package/project-templates/playable-template-phaser/assets/interact.png +0 -0
  12. package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
  13. package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
  14. package/project-templates/playable-template-phaser/builds.config.js +12 -0
  15. package/project-templates/playable-template-phaser/globals.d.ts +2 -0
  16. package/project-templates/playable-template-phaser/index.html +12 -0
  17. package/project-templates/playable-template-phaser/package.json +45 -0
  18. package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
  19. package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
  20. package/project-templates/playable-template-phaser/src/Game.ts +154 -0
  21. package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
  22. package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
  23. package/project-templates/playable-template-phaser/src/index.css +18 -0
  24. package/project-templates/playable-template-phaser/src/index.html +9 -0
  25. package/project-templates/playable-template-phaser/src/index.ts +30 -0
  26. package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
  27. package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
  28. package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
  29. package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
  30. package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
  31. package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
  32. package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
  33. package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
  34. package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
  35. package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
  36. package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
  37. package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
  38. package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
  39. package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
  40. package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
  41. package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
  42. package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
  43. package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
  44. package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
  45. package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
  46. package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
  47. package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
  48. package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
  49. package/project-templates/playable-template-phaser/tsconfig.json +26 -0
  50. package/project-templates/playable-template-phaser/vite.config.ts +64 -0
  51. 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
  52. package/project-templates/playable-template-three/README.md +795 -0
  53. package/project-templates/playable-template-three/assets/Stork.glb +0 -0
  54. package/project-templates/playable-template-three/assets/button.png +0 -0
  55. package/project-templates/playable-template-three/assets/logo.webp +0 -0
  56. package/project-templates/playable-template-three/build.json +7 -0
  57. package/project-templates/playable-template-three/builds.config.example.js +151 -0
  58. package/project-templates/playable-template-three/builds.config.js +158 -0
  59. package/project-templates/playable-template-three/package.json +36 -0
  60. package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
  61. package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
  62. package/project-templates/playable-template-three/src/Game.ts +215 -0
  63. package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
  64. package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
  65. package/project-templates/playable-template-three/src/global.d.ts +108 -0
  66. package/project-templates/playable-template-three/src/index.css +18 -0
  67. package/project-templates/playable-template-three/src/index.html +9 -0
  68. package/project-templates/playable-template-three/src/index.ts +20 -0
  69. package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
  70. package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
  71. package/project-templates/playable-template-three/src/theme/README.md +225 -0
  72. package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
  73. package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
  74. package/project-templates/playable-template-three/src/theme/current.ts +12 -0
  75. package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
  76. package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
  77. package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
  78. package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
  79. package/project-templates/playable-template-three/tsconfig.json +24 -0
@@ -0,0 +1,795 @@
1
+ # Playable Template - Three.js
2
+
3
+ 基于 Three.js 的可玩广告开发模板,支持 TypeScript,适合 3D 游戏开发。
4
+
5
+ ## 技术栈
6
+
7
+ - [Three.js](https://threejs.org/) - 强大的 3D 图形库
8
+ - [@playcraft/adsdk](https://git.woa.com/ua_playable_game/playable-template/playable-sdk) - 可玩广告 SDK,提供标准化事件和方法
9
+ - [@playcraft/devkit](https://git.woa.com/ua_playable_game/playable-template/playable-scripts) - 构建和开发工具
10
+
11
+ ## 功能特性
12
+
13
+ - ✅ Three.js 集成,支持高性能 3D 渲染
14
+ - ✅ TypeScript 支持
15
+ - ✅ 开发环境热更新 (HMR)
16
+ - ✅ SDK 事件处理(resize、pause、resume、volume 等)
17
+ - ✅ 安装按钮实现
18
+ - ✅ 交互追踪
19
+ - ✅ **响应式适配** - 自动适配横竖屏和不同设备
20
+ - ✅ **主题配置系统** - 支持多主题切换,配置与代码分离
21
+ - ✅ **PreloadScene** - 标准的资源预加载流程
22
+ - ✅ **批量构建** - 支持多主题、多渠道批量打包
23
+ - ✅ 图片 WebP 转换工具
24
+
25
+ ## 快速开始
26
+
27
+ ### 1. 安装依赖
28
+
29
+ ```bash
30
+ npm install
31
+ ```
32
+
33
+ ### 2. 启动开发服务器
34
+
35
+ ```bash
36
+ # 默认预览模式
37
+ npm run dev
38
+
39
+ # 指定渠道调试(调试特定渠道的 SDK 行为)
40
+ npm run dev bigoads # 调试 BigoAds
41
+ npm run dev ironsource # 调试 IronSource (MRAID)
42
+ npm run dev google # 调试 Google Ads
43
+ ```
44
+
45
+ 开发服务器默认运行在 `http://localhost:8080`
46
+
47
+ ### 3. 构建生产版本
48
+
49
+ #### 单渠道构建
50
+
51
+ ```bash
52
+ npm run build -- <network>
53
+
54
+ # 示例
55
+ npm run build -- google # Google Ads
56
+ npm run build -- facebook # Facebook
57
+ npm run build -- unity # Unity Ads
58
+ npm run build -- applovin # AppLovin
59
+ npm run build -- ironsource # IronSource
60
+ npm run build -- tiktok # TikTok
61
+ npm run build -- moloco # Moloco
62
+ npm run build -- liftoff # Liftoff
63
+ npm run build -- bigoads # BigoAds
64
+ ```
65
+
66
+ #### 批量构建(多渠道)
67
+
68
+ **方式1: 使用默认配置(无主题切换)**
69
+
70
+ ```bash
71
+ npm run build:all
72
+ # 或
73
+ npm run builds
74
+ ```
75
+
76
+ 自动构建所有渠道,输出到 `dist-all/` 目录。
77
+
78
+ **方式2: 使用配置文件(支持主题切换)**
79
+
80
+ ```bash
81
+ # 1. 创建配置文件
82
+ cp builds.config.example.js builds.config.js
83
+
84
+ # 2. 修改配置(根据项目需求)
85
+ vi builds.config.js
86
+
87
+ # 3. 运行批量构建
88
+ npm run builds
89
+
90
+ # 4. 高级用法:指定主题和渠道
91
+ npm run builds -- -t "theme1,theme2" # 构建指定主题
92
+ npm run builds -- -c "google,facebook" # 构建指定渠道
93
+ npm run builds -- -t "theme1" -c "google" # 组合使用
94
+ ```
95
+
96
+ **配置示例**(`builds.config.js`):
97
+
98
+ ```javascript
99
+ module.exports = {
100
+ // 项目基本信息
101
+ projectName: 'My Three Game',
102
+ projectCode: '0001',
103
+
104
+ // 渠道列表
105
+ channels: [
106
+ 'google',
107
+ 'facebook',
108
+ 'tiktok',
109
+ 'applovin',
110
+ 'ironSource',
111
+ 'unity',
112
+ 'moloco',
113
+ 'liftoff',
114
+ 'bigoads',
115
+ ],
116
+
117
+ // 主题配置(可选)
118
+ themes: {
119
+ enabled: true, // 是否启用主题切换
120
+ sourceDir: 'src/theme', // 主题目录
121
+ entryFile: 'src/index.ts', // 入口文件
122
+ whitelist: (name) => true, // 主题白名单(可选)
123
+ },
124
+
125
+ // 文件命名配置
126
+ naming: {
127
+ language: 'EN',
128
+ format: '无',
129
+ supplier: 'AIX',
130
+ follower: 'ZQL',
131
+ },
132
+ };
133
+ ```
134
+
135
+ **输出示例**:
136
+
137
+ ```
138
+ dist-all/
139
+ ├── My-Three-Game/ # 项目名称目录
140
+ │ ├── facebook/
141
+ │ │ └── My-Three-Game-EN-无-2月-20260224-AIX-ZQL-facebook.html
142
+ │ ├── tiktok/
143
+ │ │ └── tiktok.zip
144
+ │ └── google/
145
+ │ └── google.zip
146
+
147
+ # 或启用主题切换后:
148
+ ├── theme1/
149
+ │ ├── facebook/
150
+ │ │ └── theme1-EN-无-2月-20260224-AIX-ZQL-facebook.html
151
+ │ └── google/
152
+ │ └── google.zip
153
+ └── theme2/
154
+ └── ...
155
+ ```
156
+
157
+ ## 脚本命令
158
+
159
+ | 命令 | 说明 |
160
+ | ---------------------------- | -------------------------------------- |
161
+ | `npm run dev` | 启动开发服务器 (端口 8080) |
162
+ | `npm run dev <network>` | 启动开发服务器并指定渠道调试 |
163
+ | `npm run build -- <network>` | 构建指定渠道 |
164
+ | `npm run build:all` | 批量构建(使用默认配置) |
165
+ | `npm run builds` | 批量构建(使用配置文件) |
166
+ | `npm run webp` | 将 assets 目录下的图片转换为 WebP 格式 |
167
+ | `npm run webp:80` | 以 80% 质量转换为 WebP 格式 |
168
+
169
+ ## 项目结构
170
+
171
+ ### 基础结构
172
+
173
+ ```
174
+ playable-template-three/
175
+ ├── assets/ # 游戏资源目录(模型、纹理等)
176
+ │ ├── textures/ # 纹理贴图
177
+ │ ├── models/ # 3D 模型
178
+ │ └── ...
179
+ ├── scripts/
180
+ │ ├── theme-builds.js # 批量构建脚本(支持主题切换)
181
+ │ └── convert-to-webp.js # WebP 转换脚本
182
+ ├── src/
183
+ │ ├── config/ # 配置文件
184
+ │ │ ├── LoadingConfig.ts # Loading 页面配置
185
+ │ │ └── ThemeConfig.ts # 主题配置接口 ★
186
+ │ ├── scenes/ # 场景类 ★
187
+ │ │ └── PreloadScene.ts # 预加载场景
188
+ │ ├── theme/ # 主题配置 ★
189
+ │ │ ├── current.ts # 当前主题入口
190
+ │ │ ├── blue-theme.ts # 蓝色主题
191
+ │ │ ├── green-theme.ts # 绿色主题
192
+ │ │ └── red-theme.ts # 红色主题
193
+ │ ├── managers/
194
+ │ │ └── AudioManager.ts # 音频管理器
195
+ │ ├── index.ts # 入口文件,SDK 初始化
196
+ │ ├── Game.ts # 游戏主逻辑
197
+ │ ├── index.css # 样式文件
198
+ │ └── index.html # HTML 模板
199
+ ├── build.json # 构建配置
200
+ ├── builds.config.example.js # 批量构建配置模板
201
+ ├── package.json
202
+ └── tsconfig.json
203
+ ```
204
+
205
+ ### 主题系统
206
+
207
+ 模板提供了完整的主题配置系统,支持:
208
+
209
+ - **配置与代码分离**:主题是纯数据配置文件
210
+ - **类型安全**:TypeScript 接口确保配置正确
211
+ - **易于扩展**:添加新主题只需创建配置文件
212
+ - **批量构建**:自动为每个主题构建所有渠道
213
+
214
+ **主题配置示例**:
215
+
216
+ ```typescript
217
+ // src/theme/blue-theme.ts
218
+ import { ThemeConfig } from '../config/ThemeConfig';
219
+
220
+ const config: ThemeConfig = {
221
+ name: '蓝色主题',
222
+ id: 'blue',
223
+
224
+ // 场景配置
225
+ sceneBackground: 0x001a2d, // 深蓝色背景
226
+
227
+ // 光照配置
228
+ ambientLightColor: 0xffffff,
229
+ ambientLightIntensity: 2,
230
+ directionalLightColor: 0xffffff,
231
+ directionalLightIntensity: 2,
232
+
233
+ // UI 配置
234
+ uiPrimaryColor: '#4a90e2',
235
+ uiTextColor: '#ffffff',
236
+ buttonStyle: {
237
+ fontSize: '35px',
238
+ fontWeight: 'bold',
239
+ textShadow: 'rgb(255 252 106 / 63%) 4px 3px 9px',
240
+ },
241
+
242
+ // 游戏配置
243
+ modelScale: 0.5,
244
+ rotationSpeed: 0.01,
245
+ };
246
+
247
+ export default config;
248
+ ```
249
+
250
+ **使用主题配置**:
251
+
252
+ ```typescript
253
+ import themeConfig from './theme/current';
254
+
255
+ // 设置场景背景色
256
+ this.scene.background = new THREE.Color(themeConfig.sceneBackground);
257
+
258
+ // 使用光照配置
259
+ const ambientLight = new THREE.AmbientLight(
260
+ themeConfig.ambientLightColor,
261
+ themeConfig.ambientLightIntensity,
262
+ );
263
+
264
+ // 使用 UI 配置
265
+ button.style.color = themeConfig.uiTextColor;
266
+ button.style.fontSize = themeConfig.buttonStyle.fontSize;
267
+ ```
268
+
269
+ 详细说明请查看 [src/theme/README.md](./src/theme/README.md)
270
+
271
+ ## 资源加载流程
272
+
273
+ 模板使用标准的 **PreloadScene** 模式进行资源加载:
274
+
275
+ ### 加载流程
276
+
277
+ 1. **PreloadScene 启动** - 显示 Loading UI
278
+ 2. **预加载资源** - 在 `preloadResources()` 中加载所有资源
279
+ 3. **等待完成** - 资源加载完成 + 最小显示时间到达
280
+ 4. **启动游戏** - 隐藏 Loading UI,初始化游戏场景
281
+ 5. **通知 SDK** - 调用 `sdk.start()` 通知游戏开始
282
+
283
+ ### 添加新资源
284
+
285
+ 在 `src/scenes/PreloadScene.ts` 的 `preloadResources()` 方法中添加:
286
+
287
+ ```typescript
288
+ // 加载图片
289
+ const image = new Image();
290
+ image.src = imageAsset;
291
+ image.onload = () => {
292
+ this.resources.myImage = image;
293
+ checkComplete();
294
+ };
295
+
296
+ // 加载 3D 模型
297
+ const loader = new GLTFLoader();
298
+ loader.load(modelAsset, (gltf) => {
299
+ this.resources.myModel = gltf;
300
+ checkComplete();
301
+ });
302
+
303
+ // 加载音频
304
+ const audio = new Audio(audioAsset);
305
+ audio.onloadeddata = () => {
306
+ this.resources.myAudio = audio;
307
+ checkComplete();
308
+ };
309
+ ```
310
+
311
+ ### Loading 配置
312
+
313
+ 可以在 `src/config/LoadingConfig.ts` 中修改 Loading 页面的显示内容:
314
+
315
+ ```typescript
316
+ export const LoadingConfig = {
317
+ LOGO_IMAGE: logoImage, // Logo 图片
318
+ APP_TITLE: 'Vita Mahjong', // 应用标题
319
+ APP_STARS: '★★★★★', // 评分星星
320
+ APP_DESC: 'Relaxing Vita Mahjong...', // 应用描述
321
+ LOADING_TEXT: 'Loading Playable Ads', // 加载文字
322
+ SCENE_DURATION: 100, // 最小显示时长(毫秒)
323
+ // ...
324
+ };
325
+ ```
326
+
327
+ ## Three.js 基础示例
328
+
329
+ ### 创建简单 3D 场景
330
+
331
+ ```typescript
332
+ import * as THREE from 'three';
333
+ import { sdk } from '@playcraft/adsdk';
334
+
335
+ export class Game {
336
+ private renderer: THREE.WebGLRenderer;
337
+ private scene: THREE.Scene;
338
+ private camera: THREE.PerspectiveCamera;
339
+ private cube: THREE.Mesh;
340
+
341
+ constructor(width: number, height: number) {
342
+ // 创建渲染器
343
+ this.renderer = new THREE.WebGLRenderer({ antialias: true });
344
+ this.renderer.setSize(width, height);
345
+ document.body.appendChild(this.renderer.domElement);
346
+
347
+ // 创建场景
348
+ this.scene = new THREE.Scene();
349
+
350
+ // 创建相机
351
+ this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
352
+ this.camera.position.z = 5;
353
+
354
+ // 创建立方体
355
+ const geometry = new THREE.BoxGeometry(1, 1, 1);
356
+ const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
357
+ this.cube = new THREE.Mesh(geometry, material);
358
+ this.scene.add(this.cube);
359
+
360
+ // 开始渲染循环
361
+ this.animate();
362
+
363
+ // 通知 SDK 游戏已准备好
364
+ sdk.start();
365
+ }
366
+
367
+ private animate = () => {
368
+ requestAnimationFrame(this.animate);
369
+
370
+ // 旋转立方体
371
+ this.cube.rotation.x += 0.01;
372
+ this.cube.rotation.y += 0.01;
373
+
374
+ this.renderer.render(this.scene, this.camera);
375
+ };
376
+
377
+ public resize(width: number, height: number): void {
378
+ this.camera.aspect = width / height;
379
+ this.camera.updateProjectionMatrix();
380
+ this.renderer.setSize(width, height);
381
+ }
382
+ }
383
+ ```
384
+
385
+ ## 响应式适配
386
+
387
+ ### 处理窗口尺寸变化
388
+
389
+ ```typescript
390
+ // index.ts
391
+ sdk.on('resize', (size) => {
392
+ game.resize(size.width, size.height);
393
+ });
394
+
395
+ // Game.ts
396
+ public resize(width: number, height: number): void {
397
+ // 更新相机宽高比
398
+ this.camera.aspect = width / height;
399
+ this.camera.updateProjectionMatrix();
400
+
401
+ // 更新渲染器尺寸
402
+ this.renderer.setSize(width, height);
403
+ }
404
+ ```
405
+
406
+ ### 横竖屏适配建议
407
+
408
+ 对于 Three.js 项目:
409
+
410
+ 1. **使用 PerspectiveCamera**:自动根据宽高比调整视野
411
+ 2. **动态调整相机距离**:根据屏幕尺寸调整相机位置
412
+ 3. **UI 元素使用 CSS 定位**:将 UI 层与 3D 场景分离
413
+
414
+ ```typescript
415
+ public resize(width: number, height: number): void {
416
+ const isLandscape = width > height;
417
+
418
+ // 横屏:相机距离较近
419
+ // 竖屏:相机距离较远(以看到更多内容)
420
+ this.camera.position.z = isLandscape ? 5 : 8;
421
+
422
+ this.camera.aspect = width / height;
423
+ this.camera.updateProjectionMatrix();
424
+ this.renderer.setSize(width, height);
425
+ }
426
+ ```
427
+
428
+ ## 主题化批量构建
429
+
430
+ ### 什么是主题化构建?
431
+
432
+ 当你的 3D 游戏需要创建多个变体(如不同风格、不同配色)时,可以使用主题化构建功能。主题系统通过**配置文件**的方式抽离颜色、光照、样式等数据,而不需要重写整个游戏逻辑。
433
+
434
+ ### 主题配置 vs 完整代码
435
+
436
+ **✅ 推荐:主题配置方式**(当前模板使用)
437
+
438
+ ```
439
+ src/theme/
440
+ ├── current.ts # 主题入口(批量构建时自动切换)
441
+ ├── blue-theme.ts # 蓝色主题配置(纯数据)
442
+ ├── green-theme.ts # 绿色主题配置(纯数据)
443
+ └── red-theme.ts # 红色主题配置(纯数据)
444
+ ```
445
+
446
+ **优点**:
447
+
448
+ - 配置简单,只需修改颜色、参数等数据
449
+ - 游戏逻辑共享,代码量少
450
+ - 易于维护和扩展
451
+
452
+ **❌ 不推荐:完整代码方式**
453
+
454
+ ```
455
+ src/theme/
456
+ ├── level1/
457
+ │ └── index.ts # 完整游戏代码
458
+ ├── level2/
459
+ │ └── index.ts # 完整游戏代码(重复)
460
+ └── level3/
461
+ └── index.ts # 完整游戏代码(重复)
462
+ ```
463
+
464
+ **缺点**:
465
+
466
+ - 代码重复,维护困难
467
+ - 修改游戏逻辑需要同步所有主题
468
+ - 文件体积大
469
+
470
+ ### 使用场景
471
+
472
+ #### 场景1: 单主题项目(无需配置)
473
+
474
+ 只需要为多个渠道打包:
475
+
476
+ ```bash
477
+ npm run build:all
478
+ ```
479
+
480
+ #### 场景2: 多主题项目(使用主题配置)
481
+
482
+ 项目有多个主题,需要为每个主题 × 每个渠道打包:
483
+
484
+ ```bash
485
+ # 1. 添加新主题配置
486
+ # 在 src/theme/ 下创建 purple-theme.ts
487
+
488
+ # 2. 运行批量构建(会自动识别所有主题)
489
+ npm run builds
490
+
491
+ # 3. 高级用法:指定主题和渠道
492
+ npm run builds -- -t "blue-theme,green-theme" # 只构建指定主题
493
+ npm run builds -- -c "google,facebook" # 只构建指定渠道
494
+ ```
495
+
496
+ ### 添加新主题
497
+
498
+ **步骤1: 创建主题配置文件**
499
+
500
+ ```bash
501
+ # 在 src/theme/ 下创建新文件
502
+ touch src/theme/purple-theme.ts
503
+ ```
504
+
505
+ **步骤2: 编写主题配置**
506
+
507
+ ```typescript
508
+ // src/theme/purple-theme.ts
509
+ import { ThemeConfig } from '../config/ThemeConfig';
510
+
511
+ const config: ThemeConfig = {
512
+ name: '紫色主题',
513
+ id: 'purple',
514
+
515
+ sceneBackground: 0x2d1a3d, // 深紫色背景
516
+ ambientLightColor: 0xffccff,
517
+ ambientLightIntensity: 1.8,
518
+ directionalLightColor: 0xffaaff,
519
+ directionalLightIntensity: 2.1,
520
+
521
+ uiPrimaryColor: '#9b59b6',
522
+ uiTextColor: '#ffffff',
523
+ buttonStyle: {
524
+ fontSize: '35px',
525
+ fontWeight: 'bold',
526
+ textShadow: 'rgb(200 150 255 / 70%) 4px 3px 9px',
527
+ },
528
+
529
+ modelScale: 0.5,
530
+ rotationSpeed: 0.01,
531
+ };
532
+
533
+ export default config;
534
+ ```
535
+
536
+ **步骤3: 测试主题**
537
+
538
+ ```typescript
539
+ // 修改 src/theme/current.ts
540
+ import config from './purple-theme'; // 切换到新主题
541
+ export default config;
542
+ ```
543
+
544
+ 运行 `npm run dev` 预览效果
545
+
546
+ **步骤4: 批量构建**
547
+
548
+ ```bash
549
+ npm run builds # 会自动识别并构建 purple-theme
550
+ ```
551
+
552
+ ### 工作原理
553
+
554
+ 批量构建脚本会:
555
+
556
+ 1. **扫描主题目录**:`src/theme/` 下所有 `*-theme.ts` 文件
557
+ 2. **遍历构建**:为每个主题 × 每个渠道进行构建
558
+ 3. **自动切换**:修改 `src/theme/current.ts` 的 import 语句
559
+ 4. **恢复原状**:构建完成后恢复原始文件
560
+
561
+ 完整文档请参考 [builds.config.example.js](./builds.config.example.js) 和 [src/theme/README.md](./src/theme/README.md)
562
+
563
+ ## SDK 事件
564
+
565
+ 模板已集成以下 SDK 事件:
566
+
567
+ - `resize` - 屏幕尺寸变化(已实现响应式适配)
568
+ - `pause` - 游戏暂停
569
+ - `resume` - 游戏恢复
570
+ - `volume` - 音量变化
571
+ - `finish` - 游戏结束
572
+ - `interaction` - 用户交互计数
573
+
574
+ ## SDK 生命周期集成
575
+
576
+ - `sdk.init()` - 初始化,获取容器尺寸
577
+ - `sdk.start()` - 游戏资源加载完成后调用
578
+ - `sdk.finish()` - 游戏结束时调用
579
+ - `sdk.install()` - 跳转应用商店
580
+
581
+ ## 配置说明
582
+
583
+ `build.json` 配置项:
584
+
585
+ | 字段 | 说明 |
586
+ | ------------- | ------------------------------ |
587
+ | app | 应用名称 |
588
+ | name | 概念名称 |
589
+ | version | 版本号 |
590
+ | language | 语言 |
591
+ | googlePlayUrl | Google Play 链接 |
592
+ | appStoreUrl | App Store 链接 |
593
+ | filename | 输出文件名(单渠道构建时使用) |
594
+
595
+ ## 性能优化建议
596
+
597
+ ### 1. 几何体优化
598
+
599
+ ```typescript
600
+ // ✅ 推荐:复用几何体
601
+ const geometry = new THREE.BoxGeometry(1, 1, 1);
602
+ for (let i = 0; i < 100; i++) {
603
+ const mesh = new THREE.Mesh(geometry, material);
604
+ scene.add(mesh);
605
+ }
606
+
607
+ // ❌ 避免:每次创建新几何体
608
+ for (let i = 0; i < 100; i++) {
609
+ const geometry = new THREE.BoxGeometry(1, 1, 1); // 重复创建
610
+ const mesh = new THREE.Mesh(geometry, material);
611
+ scene.add(mesh);
612
+ }
613
+ ```
614
+
615
+ ### 2. 材质优化
616
+
617
+ ```typescript
618
+ // ✅ 使用简单材质
619
+ const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
620
+
621
+ // ⚠️ 避免过多光照材质
622
+ const material = new THREE.MeshPhongMaterial({
623
+ color: 0x00ff00,
624
+ specular: 0x111111,
625
+ shininess: 30,
626
+ });
627
+ ```
628
+
629
+ ### 3. 纹理优化
630
+
631
+ ```typescript
632
+ // 使用 WebP 纹理
633
+ const textureLoader = new THREE.TextureLoader();
634
+ const texture = textureLoader.load('assets/texture.webp');
635
+
636
+ // 设置纹理过滤
637
+ texture.minFilter = THREE.LinearFilter;
638
+ texture.magFilter = THREE.LinearFilter;
639
+ ```
640
+
641
+ ### 4. 渲染优化
642
+
643
+ ```typescript
644
+ // 开启抗锯齿
645
+ this.renderer = new THREE.WebGLRenderer({ antialias: true });
646
+
647
+ // 设置像素比(避免模糊)
648
+ this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
649
+
650
+ // 限制帧率(移动设备)
651
+ const fps = 30;
652
+ const fpsInterval = 1000 / fps;
653
+ let lastTime = Date.now();
654
+
655
+ function animate() {
656
+ requestAnimationFrame(animate);
657
+
658
+ const now = Date.now();
659
+ const elapsed = now - lastTime;
660
+
661
+ if (elapsed > fpsInterval) {
662
+ lastTime = now - (elapsed % fpsInterval);
663
+ renderer.render(scene, camera);
664
+ }
665
+ }
666
+ ```
667
+
668
+ ## 工具脚本
669
+
670
+ ### WebP 图片转换
671
+
672
+ ```bash
673
+ # 使用默认质量(100%)
674
+ npm run webp
675
+
676
+ # 指定质量(80%,推荐)
677
+ npm run webp:80
678
+ ```
679
+
680
+ **优化效果**:
681
+
682
+ - PNG → WebP: 减少 30-50% 文件大小
683
+ - JPG → WebP: 减少 20-40% 文件大小
684
+
685
+ ## 常见问题
686
+
687
+ ### Q1: Three.js 场景为什么是黑色的?
688
+
689
+ **A**: 可能原因:
690
+
691
+ 1. 没有添加光源(使用 MeshPhongMaterial 等需要光照的材质时)
692
+ 2. 相机位置或方向不对
693
+ 3. 对象在相机视野外
694
+
695
+ **解决方案**:
696
+
697
+ ```typescript
698
+ // 添加环境光
699
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
700
+ scene.add(ambientLight);
701
+
702
+ // 添加方向光
703
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
704
+ directionalLight.position.set(1, 1, 1);
705
+ scene.add(directionalLight);
706
+ ```
707
+
708
+ ### Q2: 如何加载 3D 模型?
709
+
710
+ **A**: 使用 GLTFLoader:
711
+
712
+ ```typescript
713
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
714
+
715
+ const loader = new GLTFLoader();
716
+ loader.load('assets/models/model.glb', (gltf) => {
717
+ scene.add(gltf.scene);
718
+ });
719
+ ```
720
+
721
+ ### Q3: 移动端性能差怎么办?
722
+
723
+ **A**:
724
+
725
+ 1. 降低多边形数量
726
+ 2. 使用简单材质(MeshBasicMaterial)
727
+ 3. 减少光源数量
728
+ 4. 限制帧率到 30fps
729
+ 5. 使用 LOD (Level of Detail)
730
+
731
+ ### Q4: 如何实现点击 3D 对象?
732
+
733
+ **A**: 使用 Raycaster:
734
+
735
+ ```typescript
736
+ const raycaster = new THREE.Raycaster();
737
+ const mouse = new THREE.Vector2();
738
+
739
+ renderer.domElement.addEventListener('click', (event) => {
740
+ // 计算鼠标位置
741
+ mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
742
+ mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
743
+
744
+ // 更新射线
745
+ raycaster.setFromCamera(mouse, camera);
746
+
747
+ // 检测相交对象
748
+ const intersects = raycaster.intersectObjects(scene.children);
749
+
750
+ if (intersects.length > 0) {
751
+ console.log('Clicked:', intersects[0].object);
752
+ }
753
+ });
754
+ ```
755
+
756
+ ### Q5: 支持哪些渠道?
757
+
758
+ **A**:
759
+
760
+ - google (Google Ads)
761
+ - facebook (Facebook)
762
+ - tiktok (TikTok)
763
+ - applovin (AppLovin)
764
+ - ironSource (IronSource)
765
+ - unity (Unity Ads)
766
+ - moloco (Moloco)
767
+ - liftoff (Liftoff)
768
+ - bigoads (BigoAds)
769
+
770
+ ## 环境要求
771
+
772
+ - Node.js >= 16.0.0
773
+ - npm >= 7.0.0
774
+
775
+ ## 参考资料
776
+
777
+ ### 官方文档
778
+
779
+ - [Three.js 官方文档](https://threejs.org/docs/)
780
+ - [Three.js 示例](https://threejs.org/examples/)
781
+ - [@playcraft/adsdk 文档](https://git.woa.com/ua_playable_game/playable-sdk)
782
+
783
+ ### 学习资源
784
+
785
+ - [Three.js Fundamentals](https://threejs.org/manual/)
786
+ - [Three.js Journey](https://threejs-journey.com/) (推荐)
787
+
788
+ ### 内部资源
789
+
790
+ - [项目命名规范](https://iwiki.woa.com/p/4017321768)
791
+ - [可玩广告最佳实践](https://iwiki.woa.com/ua_playable)
792
+
793
+ ## 许可证
794
+
795
+ MIT