@playcraft/cli 0.0.53 → 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.
- package/dist/commands/create.js +164 -98
- package/dist/index.js +4 -1
- package/dist/scaffold/templates.js +35 -0
- package/package.json +3 -3
- package/project-templates/playable-template-phaser/.playcraftignore +10 -0
- package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
- package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
- package/project-templates/playable-template-phaser/README.md +381 -0
- package/project-templates/playable-template-phaser/assets/brain.png +0 -0
- package/project-templates/playable-template-phaser/assets/button.png +0 -0
- package/project-templates/playable-template-phaser/assets/interact.png +0 -0
- package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
- package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
- package/project-templates/playable-template-phaser/builds.config.js +12 -0
- package/project-templates/playable-template-phaser/globals.d.ts +2 -0
- package/project-templates/playable-template-phaser/index.html +12 -0
- package/project-templates/playable-template-phaser/package.json +45 -0
- package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
- package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
- package/project-templates/playable-template-phaser/src/Game.ts +154 -0
- package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
- package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
- package/project-templates/playable-template-phaser/src/index.css +18 -0
- package/project-templates/playable-template-phaser/src/index.html +9 -0
- package/project-templates/playable-template-phaser/src/index.ts +30 -0
- package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
- package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
- package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
- package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
- package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
- package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
- package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
- package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
- package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
- package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
- package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
- package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
- package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
- package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
- package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
- package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
- package/project-templates/playable-template-phaser/tsconfig.json +26 -0
- package/project-templates/playable-template-phaser/vite.config.ts +64 -0
- 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
- package/project-templates/playable-template-three/README.md +795 -0
- package/project-templates/playable-template-three/assets/Stork.glb +0 -0
- package/project-templates/playable-template-three/assets/button.png +0 -0
- package/project-templates/playable-template-three/assets/logo.webp +0 -0
- package/project-templates/playable-template-three/build.json +7 -0
- package/project-templates/playable-template-three/builds.config.example.js +151 -0
- package/project-templates/playable-template-three/builds.config.js +158 -0
- package/project-templates/playable-template-three/package.json +36 -0
- package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
- package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
- package/project-templates/playable-template-three/src/Game.ts +215 -0
- package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
- package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
- package/project-templates/playable-template-three/src/global.d.ts +108 -0
- package/project-templates/playable-template-three/src/index.css +18 -0
- package/project-templates/playable-template-three/src/index.html +9 -0
- package/project-templates/playable-template-three/src/index.ts +20 -0
- package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
- package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
- package/project-templates/playable-template-three/src/theme/README.md +225 -0
- package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
- package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
- package/project-templates/playable-template-three/src/theme/current.ts +12 -0
- package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
- package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
- package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
- 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
|