十万积分的成果又回来了!手机端成功使用视频驱动!!
精华修改于04/04181 浏览开发心得 包含 AI 合成内容
作者:浠涫
一、问题背景:十万积分差点(已经)打水漂
在 TapTap Maker 聚光灯 24 小时 GameJam 中,我做了《方格屋》——一个叙事驱动的舒尔特方格游戏。为了最好的叙事体验,标题界面做了做了循环背景和开门过渡视频,序章做了 11 段 AI 生成视频(A 段 5 段 + B 段 6 段)。
总计约 13 段视频,消耗了近 10 万积分。
然后在上线前一天的手机端测试中,我发现:手机端视频全挂了!
二、问题复现:手机端视频为什么播不了
2.1 最初的情况(v0.9.29)

根因分析:
- 桌面端用 <video> 标签,支持 blob URL
- 手机端用 Android WebView,blob URL 不稳定
- TapTap Maker 的 Video.isSupported 在手机端返回 false
解决方案(v0.9.30):
- 手机端检测后自动 fallback 到静态图片 + 文字 ADV 模式
- 桌面端继续用视频
- 结果:手机端勉强能玩,但花了 10 万积分的视频完全看不到
三、第一轮优化尝试(v0.9.30 - v0.9.34):治标不治本
3.1 视频格式优化(v0.9.34)
即使在 Video.isSupported 返回 true 的情况下,手机端视频加载还是经常超时(>3 秒)。进一步排查发现:

用 ffmpeg 重编码全部 13 个视频:
bash
# faststart:moov 前置,支持渐进式加载
# Main profile + 无 B-frames:最大化移动端兼容性
# 降码率到 1.5 Mbps(AI 生成视频足够清晰)
ffmpeg -i input.mp4 \
-movflags +faststart \
-profile:v main -bf 0 \
-b:v 1500k -maxrate 2000k \
output.mp4
效果:

3.2 非阻塞预加载(v0.9.33)
v0.9.32 的预加载是阻塞式的——用户必须等视频加载完才能继续,在手机端经常卡 10 秒以上。
核心理念转变:
不要让用户等一个注定失败的加载!
修复方案:
- 免责声明不再阻塞用户:1.5s 最短展示后即可点击继续
- 用户点击跳过时:未完成的预加载直接放弃 → videoWorks=false
- 标题页/序幕自动走 fallback:静态图/ADV 文字模式,零等待
- 进度条改为纯信息展示:呼吸动画不依赖预加载状态
3.3 问题:这些优化都没用
虽然做了很多工作,但视频在手机端还是没法正常播放——
核心原因:当时 TapTap Maker 官方没更新,没有对应通道!
所有优化都在绕路走,但核心问题没解决。
四、转折点:TapTap Maker 官方通道打通(v0.9.35)
4.1 关键转折点
直到VideoPlayer 原生支持 HTTPS URL 直接加载!昨天/前天才打通了这条路:
这是整个修复链条中的关键转折点——没有官方支持 HTTPS URL 直连是前提!
4.2 根因定位(跨 3 个会话深入分析)
终于找到了真正的根因:
plain text
WASM VideoPlayer 加载虚拟路径
↓
DWP 下载
↓
blob URL ← 【问题就在这里!
↓
<video> 标签播放
blob URL 在 Android WebView 中不稳定,经常加载失败或超时。
4.3 解决方案:CDN 直连绕过 blob URL
VideoPlayer 原生支持 HTTPS URL 直接加载!绕过 blob URL 整条链路。
新增 video_url_resolver.lua(280 行):
- 运行时异步探测 CDN base URL(6 个候选模式)
- 用 VideoPlayer:AsyncLoad 验证可达性
新增 video_cdn_map.lua:
- 从 manifest 提取 13 个视频虚拟路径 → CDN 文件名映射
修改 main.lua 和 prologue_video.lua:
- 所有视频加载点都通过 resolver 解析为 HTTPS URL
- 探测失败时优雅回退到虚拟路径(桌面端正常)
五、最终修复:分布式预加载架构(v0.9.56)
5.1 问题
CDN 直连解决了加载问题,但在手机端 WebView 中,VideoPlayer.GetVideoWidth/Height 经常返回 0,导致 widget.Render() 中跳过绘制 → 黑屏!
5.2 修复 1:视频尺寸 workaround
lua
--- 手机端 VideoPlayer.GetVideoWidth/Height 返回 0 的 workaround
--- 引擎 JS 层在 HTTPS 直链模式下可能无法获取视频原始尺寸,
--- 导致 widget.Render() 中跳过绘制 = 黑屏。用 textureWidth/Height 兜底。
function PatchVideoSizeForMobile(widget)
if not isMobileWeb_ or not widget or not widget.player_ then return end
local p = widget.player_
local origGetW = p.GetVideoWidth
local origGetH = p.GetVideoHeight
local tw = widget.props.textureWidth or 1920
local th = widget.props.textureHeight or 1080
p.GetVideoWidth = function(self)
local w = origGetW(self)
return (w and w > 0) and w or tw
end
p.GetVideoHeight = function(self)
local h = origGetH(self)
return (h and h > 0) and h or th
end
end
5.3 修复 2:分布式预加载
手机端必须走 AsyncLoad + swap 路径,不能同步加载。
分布式预加载架构:
- 开门视频:在标题页 idle 时预加载
- 序幕 B1 视频:在方格玩法期间预加载
- 5 个视频分别在不同空闲时段 AsyncLoad + swap
lua
-- 二级预加载(标题页/游玩期间异步加载后续视频)
local preloadDoorPlayer_ = nil -- 预加载的开门过渡视频
local preloadDoorDone_ = false
local preloadB1Player_ = nil -- 预加载的序幕B1视频(方格玩法期间加载)
local preloadB1Done_ = false
六、完整修复链路总结
、

七、最终效果(v0.9.56)
- 开门视频:标题 idle 预加载 → 播放正常
- 序幕 B1:方格期间预加载 → 播放正常
- 分布式预加载架构:5 个视频分别在不同空闲时段 AsyncLoad + swap
- 用户确认:全流程视频播放正常
✅ 手机端全流程视频修复完成
十万积分终于回来了! 🎉
八、避坑指南(给后来者)
8.1 TapTap Maker 手机端视频播放检查清单

8.2 如果视频还是播不了
- 先确认官方通道有没有打通:TapTap Maker 刷新了吗?问嗒啦啦了吗?有没有 HTTPS URL 直连支持?
- 检查 blob URL:手机端尽量用 HTTPS 直链,绕过 blob
- 查看日志:VideoPlayer 的 AsyncLoad 回调返回什么?
- 简化测试:先放一个最简单的 5 秒视频测试
- 兜底方案:做 fallback 到静态图/文字模式
九、核心总结
一句话总结:在 TapTap Maker 手机端做视频,首先需要 TapTap Maker 官方提供 HTTPS URL 加载通道,这是前提!然后再配合格式、预加载策略、尺寸获取等多维度优化,才能让十万积分的视频真正在手机上播放出来!
关键要点:
- 官方通道是核心前提! HTTPS URL 直连是核心,之前 TapTap Maker 官方没更新,没有对应通道,其他优化都治标不治本!
- 视频格式:faststart + Main profile + 无 B-frames + 1.5 Mbps
- 加载方式:HTTPS URL 直连,绕过 blob URL
- 预加载策略:分布式异步预加载 + swap
- 尺寸获取:textureWidth/Height 兜底,防止 GetVideoWidth 返回 0
- 永远要有 fallback:静态图/文字模式兜底
希望这篇干货能帮大家节省大量调试时间,如果身边有 TapTap Maker 开发者正在头疼手机端视频问题,不妨把这篇文章分享给他~
> (注:文档部分内容可能由 AI 生成)
|(注:文档部分内容可能由 AI 生成)
注:文档核心内容由嗒啦啦生成!
#
#

