# Playable Ads 本项目是一个基于 Vue3 + TypeScript + Vite 的项目,核心是基于webgl的web端填色,用于创建可播放的广告。 ## 背景 我们有一个填色应用app,已经上架到google play 和 app store。 但是投放的广告基本上是静态图片或视频, 没有playable的广告, 本项目立足制作playable的广告, 用于提升广告的互动性和效果。 playable广告的核心是H5页面, 本工程已经实现了基于webgl的web端填色核心玩法,现在需要进一步完善制作成可播放的广告。 ## 竞品分析 以下是几家竞品的playbale 广告, 可以作为分析参考, 尤其是页面的规范方面。 ### Paint by Number: 1. ``` https://creative-ag-global.umcdn.cn/html/14/df/cf/14dfcf467412065a43ec4d2f1ad63971.html ``` 主要展示了一屏十几个填色作品的填列表页,所谓互动的部分其实就是可以滑动该列表页,点击某个列表页进入一个详情页,这属于简单的内容互动展示,没有涉及游戏核心玩法 2. ``` https://creative-ag-global.umcdn.cn/html/df/3a/14/df3a14870fcb67b02292a0eac34c5ab1.html ``` 可以滑动选择几个有吸引力的上色作品,点击进入填色详情页, 但同样不能进行填色操作,没有涉及游戏核心玩法,本质还是简单的内容互动展示 ### Happy Color 1. ``` https://creative-ag-global.umcdn.cn/html/83/d5/81/83d5810d37e36b92ee5d90517e31b0f9.html ``` 页面动效较好, 但也属于内容展示,没有体现核心玩法, 能够互动的部分就是一个手指指向的 PLAY NOW 按钮,点击即进入安装弹框 2. ``` https://creative-ag-global.umcdn.cn/html/85/8a/03/858a031936edfda1009dd6acb5c3ec2f.html ``` 这个有涉及一点核心玩法,有提示手型,可点击上色。 ### Color asis ``` https://creative-ag-global.umcdn.cn/html/09/2d/44/092d441b80639f462fa667bb3f8964bf.html ``` 这个有涉及核心玩法, 可点击上色,结束后撒花动画,展示典型内容,显示“Over 10,000+ pictures”, 可以作为重点对标研究对象 ## playable广告规范 各家广告平台对于playable的广告规范如下, 需要仔细研读理解,我们最终发布的内容要符合所有平台的规范: 1. mintergal ``` https://www.playturbo.com/review/doc ``` 2. applovin ``` https://p.applov.in/playablePreview?create=1 ``` 3. unity ``` https://docs.unity.com/zh-cn/grow/acquire/creatives/playable/specifications ``` 4. google admod ``` https://support.google.com/google-ads/answer/9981650?hl=en#_HTML ``` gemini关于playable广告的讲解: ``` https://gemini.google.com/share/8340c20dd2d1 ``` ## 项目目标 制作符合各广告平台的playable ads, 当前可聚焦核心填色玩法的模版, 未来可以可能开发更多模版。 ## 技术栈 | 层级 | 技术 | | ------------ | -------------------------------------------------------------------- | | 渲染引擎 | WebGL 2(自研,无外部 GL 框架) | | 构建工具 | Vite 5 + TypeScript 5 | | 打包插件 | vite-plugin-singlefile(输出单文件 HTML) | | 广告平台适配 | AdPlatformAdapter(Applovin / Unity / Playturbo-Mintegral / Google) | | 单文件产物 | 默认与各平台 profile 均输出自包含 HTML | ## 项目结构 ``` src/ filler/ # 广告主逻辑 index.ts # 入口,整合加载/CTA/平台初始化 cta.ts # CTA 按钮点击 + 高亮动画 ad-platform/ # 平台 Adapter;构建时通过 Vite alias 选择目标平台 FillerScene.ts# WebGL 填色场景 FingerHint.ts # 指引手指 DOM overlay Loader.ts # 资源加载 base/ # 自研 WebGL 2 渲染层 assets/ res/ # 填色资源包(config.json + 图片) css/ # 样式 dist/ index.html # 默认产物 applovin/index.html # Applovin 产物 unity/index.html # Unity 产物 playturbo/index.html # Playturbo 产物 mintegral/index.html # Mintegral 产物 google/index.html # Google 产物 ``` ## 开发调试 ### 安装依赖 ```bash npm install ``` ### 启动本地开发服务器 ```bash npm run dev ``` ### 映射端口到云服务器 ```bash ssh -R 0.0.0.0:5173:localhost:5173 ecs ``` 然后可以在浏览器打开: ``` http://42.193.231.145:5173 ``` ## 构建 本项目保持一套业务源码,通过 Vite `--mode` 在构建期选择平台 Adapter,并输出平台专用单文件 HTML。默认构建使用 Google-like adapter,输出 `dist/index.html`。 ```bash npm run build # dist/index.html npm run build:applovin # dist/applovin/index.html npm run build:unity # dist/unity/index.html npm run build:playturbo # dist/playturbo/index.html npm run build:mintegral # dist/mintegral/index.html npm run build:google # dist/google/index.html npm run build:all # 依次输出以上全部产物 ``` 所有产物均为单文件自包含 HTML,JS/CSS/图片/字体均内联。构建后会移除 Vite 单文件产物中的 `type="module"` 和 `crossorigin` 属性,以满足 Playturbo 对本地 HTML 文件的扫描要求。 | 指标 | 数值 | | ------------ | -------------------- | | 文件大小 | ~1.02 MB | | Gzip 大小 | ~673 KB | | 平台大小限制 | 5 MB(各平台均满足) | 平台选择由 `vite.config.js` 中的 `platformBuilds` 控制:`playturbo` 与 `mintegral` 都使用 Playturbo adapter,但输出到不同目录和文件名。当前不需要 `.env.applovin` / `.env.unity` 等环境文件。 > **注意**:非 Playturbo/Mintegral 平台的 Store 链接集中在 `src/filler/ad-platform/adapters/storeUrls.ts`: > > - **测试阶段**:使用 PBN 落地页(当前默认),避免产生无效转化 > - **正式上线**:换回自己 app 的 Store 链接(文件内有注释说明) > > Playturbo/Mintegral 产物不会内置 Store URL,CTA 只调用平台要求的 `window.install()`。 ## 多平台测试 ### 测试前准备 1. 根据目标平台执行对应构建命令,或执行 `npm run build:all` 生成全部产物 2. 检查 `src/filler/ad-platform/adapters/storeUrls.ts` 中的落地页 URL 符合当前测试目的 ### Applovin **预览工具**:https://p.applov.in/playablePreview?create=1 1. 执行 `npm run build:applovin`,打开预览工具,上传 `dist/applovin/index.html` 2. 检查项: - [ ] 广告正常加载,WebGL 填色可交互 - [ ] CTA 按钮可点击(Applovin 通过 `ExitApi.exit()` 关闭广告,落地页由平台后台配置) - [ ] 手指引导动画正常显示 - [ ] 完成填色后撒花 + 结算屏正常展示 ### Unity Ads **规范文档**:https://docs.unity.com/zh-cn/grow/acquire/creatives/playable/specifications **验证方式**:Unity 没有 Web 端预览工具,需通过 **Ad Testing App**(iOS / Android,最新版 4.0.0)扫码验证。 1. 执行 `npm run build:unity`,将 `dist/unity/index.html` 托管到可访问的 HTTPS URL 2. 在 Unity Ads Dashboard 上传素材,或在 Ad Testing App 中直接扫码加载 3. 检查项: - [ ] `dapi.gameReady()` 被正确调用(可在控制台确认) - [ ] CTA 跳转正常(Unity 使用 MRAID `mraid.open(url)` 跳转) - [ ] 广告尺寸符合规范(单文件 HTML,无外部请求) - [ ] 文件大小 ≤ 5 MB ### Google AdMob / Google Ads **规范文档**:https://support.google.com/google-ads/answer/9981650?hl=en#_HTML **验证工具**:https://h5validator.appspot.com/adwords/asset Google 的官方验证器要求上传 `.zip` 而非裸 HTML。需要先将产物打包为 zip,上传时勾选 **"Select for App Campaigns"**,然后点击眼睛图标预览。 1. 执行 `npm run build:google`,将 `dist/google/index.html` 打包为 `google-ad.zip` 2. 打开 h5validator,上传 zip,勾选 "Select for App Campaigns" 3. 点击眼睛图标预览,测试交互和 CTA 点击 4. 检查项: - [ ] 无外部网络请求(所有资源均已内联) - [ ] 文件大小满足限制(≤5 MB,zip 内 ≤512 个文件) - [ ] CTA 可正常触发跳转(Google 平台使用 `ExitApi.exit()`) - [ ] 不依赖 `document.write` 或被禁用 API - [ ] 包含方向 meta 标签(``) ### Mintergal / Playturbo **规范文档**:https://www.playturbo.com/review/doc **预览工具**:https://www.playturbo.com/review 1. 执行 `npm run build:playturbo` 或 `npm run build:mintegral`,按平台文档要求上传对应产物 2. 检查项: - [ ] 资源加载完成后调用 `window.gameReady()` - [ ] 结束流程调用 `window.gameEnd()` - [ ] CTA 只调用 `window.install()`,不主动 `window.open` 或跳 Store URL - [ ] 产物不包含 `type="module"`、`crossorigin`、`import`、`export` - [ ] 暴露 `window.gameStart` / `window.gameClose` - [ ] 音频在用户首次交互后解锁(iOS 限制) ### 真机测试 在真实设备上直接用浏览器打开 `dist/index.html`(可通过云服务器地址访问): ``` http://42.193.231.145:5173 # dev 模式 # 或将 dist/index.html 放到静态服务器后访问 ``` | 设备 | 浏览器 | 检查项 | | ------- | ------- | ---------------------------------------------- | | iOS | Safari | 音频首次交互解锁、手势填色、CTA 跳转 App Store | | Android | Chrome | 音频、手势、CTA 跳转 Google Play | | Android | WebView | 广告平台内嵌 WebView 行为是否一致 | ## 常见问题 **Q: 填色区域点击没反应?** A: 检查 WebGL context 是否初始化成功,Console 有无报错。 **Q: 音效不播放(iOS)?** A: iOS 要求用户交互后才能播放音频,代码已有 unlock 逻辑,确认首次点击后音效正常即可。 **Q: CTA 点击没有跳转?** A: 各平台跳转机制不同:Applovin 优先用 `ExitApi.exit()`;Unity/通用 MRAID 环境用 `mraid.open(url)`;Google-like 环境用 `window.open(url)`;Playturbo/Mintegral 只允许调用 `window.install()`,不应主动跳 Store URL。 **Q: 构建产物超过平台大小限制?** A: 检查 `assets/res/` 下的图片资源,压缩大尺寸图片后重新构建。 # 二期规划 在当前一期 playable 广告的基础上,构建一个 **广告制作管理平台**(Playable Ads Platform),供广告运营团队(5-10人)通过浏览器完成"上传素材 → 配置参数 → 选择平台 → 一键生成广告文件"的完整流程,无需接触源代码和命令行。 > 详细技术方案见 [二期细化方案](./docs/phase2-plan.md) ## 核心概念 | 概念 | 说明 | |------|------| | **模板(Template)** | 可复用的广告框架。当前有 `coloring`(填色玩法),未来可扩展更多 | | **创意(Creative)** | 一个模板的实例 = 运营选择的模板 + 上传的素材 + 主题配置 + 构建产物 | | **素材包** | 运营上传的 `.zip` 文件,包含 `config.json`(必)、`page.png`(必)、`map.png`(必)、`special.jpeg`(可选) | | **构建产物** | 各广告平台的单文件 HTML,以及全部产物的 ZIP 包 | ## 技术架构 ``` React SPA (浏览器) │ ▼ Express API (:3001) ├── REST API (/api/v1/*) ├── Build Service (调用 Vite 程序化构建) ├── SQLite (creatives, builds, templates) └── storage/creatives// (素材 + 产物) ``` - **前端**:React + Vite + TypeScript - **后端**:Node.js / Express + TypeScript - **数据库**:SQLite (better-sqlite3) - **构建**:调用现有 Vite 项目,通过 `#ad-config` alias 注入用户素材和主题 - **部署**:单体部署到 ECS,Express 同时服务 API + 静态文件 ## 项目结构 ``` playableads-platform/ ├── templates/ # 模板根目录(扩展点:新模板 = 新子目录) │ └── coloring/ # 填色玩法模板(当前一期代码) │ ├── manifest.json # 模板元信息(驱动前后端 UI 和校验) │ ├── src/filler/ # 业务逻辑 │ │ ├── index.ts # 入口(从 #ad-config 导入素材+主题) │ │ ├── ad-config.ts # 默认配置(dev / CLI 构建) │ │ └── _ad_config_.ts # gitignored,平台构建时动态生成 │ └── assets/user/ # gitignored,平台构建时 symlink → storage │ ├── platform/ # 二期平台 │ ├── server/ # Express API + Build Service │ └── client/ # React SPA │ └── storage/ # gitignored,运行时数据 ├── data.db # SQLite └── creatives// ├── assets/ # 用户上传的 zip 解压产物 └── builds// # 构建输出 ``` ## 运营操作流程 ``` 新建创意 → 选择模板(填色玩法) │ ▼ 上传素材 zip(config.json + page.png + map.png ± special.jpeg) │ ▼ 配置主题(背景渐变、CTA 文案/颜色、进度条颜色…) │ ▼ 勾选目标平台(Google / Applovin / Unity / Playturbo / Mintegral) │ ▼ 点击构建 → 等待完成 → 下载单平台 HTML 或全部产物 ZIP ``` ## 模板参数化 核心思路:**配置集中化 + Vite alias 切换**。 - 模板 `index.ts` 从 `#ad-config` 单入口导入所有素材和主题 - 日常 `npm run dev` → alias 指向 `ad-config.ts`(默认素材) - 平台构建 → 环境变量 `AD_CONFIG_PATH=_ad_config_.ts`,alias 指向动态生成的配置 - 每个模板通过 `manifest.json` 声明需要哪些素材、哪些主题参数,前端据此渲染 UI,后端据此校验 ## 实施阶段 | 阶段 | 内容 | 预计 | |------|------|------| | 1. 基础搭建 | 目录重构、Express/React 脚手架、DB 初始化 | 2-3天 | | 2. 模板参数化 | index.ts 重构、#ad-config alias、dev/CLI 行为不变 | 1.5-2天 | | 3. 后端 API | 素材存取、构建服务、全部 API 端点 | 2-3天 | | 4. 前端 UI | Dashboard、CreativeDetail、构建交互 | 2-3天 | | 5. 部署 | Docker 化、ECS 部署、端到端测试 | 1-2天 |