
最近接了好几个 Unity 项目目标很统一要上抖音小游戏。群里也一直有人问“Unity 项目到底怎么接入抖音小游戏”。这个问题看着就一句话实际拆开能讲一大筐。Unity 出身的团队习惯的是 App 思维、Scene 思维、真机 Touch 思维而抖音小游戏是 Web 容器、生命周期托管、包体限制、平台 API 约束两者之间的转换远比“换个导出选项”复杂得多。这篇就把我实际踩过的路写出来从选型评估、环境准备、构建导出到平台适配、性能优化、常见坑排查尽量按真实项目的推进顺序讲。无论你是还没立项、正在评估可行性还是已经卡在构建阶段都能找到对应章节直接抄作业。1. 先想清楚你的 Unity 项目适不适合上抖音小游戏1.1 小游戏和 App 的载体差别抖音小游戏本质上跑在宿主 App 提供的小游戏运行时环境里用的是 WebGL JavaScript 技术栈。Unity 项目想进去不能直接导出 Android 或 iOS 包而是要把 Unity 工程构建成 WebGL 产物再通过小游戏运行时加载和驱动。这个载体差别决定了三件事第一你的能力边界变了原生插件、文件系统、后台权限这些 App 里的能力基本不可用第二包体上限变了小游戏对首包体积和总包体积都有严格限制不能指望像 1GB 的 App 那样全塞进去第三生命周期变了宿主 App 随时可能把后台小游戏冻结回收Unity 传统 Game 循环里“靠暂停就万事大吉”的思路要彻底改。所以做技术选型前先别问“怎么接”先问“接过去剩下多少东西能用”。如果是简单的 2D 休闲游戏、模拟经营、放置数值型产品非常适合。如果是重渲染、重模型、强物理的 3D 大型场景要提前准备好大面积删减和专项优化否则硬接入就是给自己挖坑。1.2 选型Unity 转小游戏还是小游戏原生重写这是立项时最该较真的地方。Unity 转抖音小游戏的优势是能复用已有 C# 逻辑、美术资源和游戏设计团队不需要重新学一套引擎迭代节奏能保持。代价是运行时效率不如原生小游戏引擎包体也会凭空多一层引擎运行时开销。反过来如果项目本身已经是纯 UI 玩法、逻辑简单用一个成熟的小游戏引擎原生重写包体可以做到几百 KB加载速度飞快性能上限也更高。但这种方案需要重写核心玩法代码开发周期一般不会短而且原项目的数值配置、技能系统、动画资产很多都要导出后重新处理。我的建议是做个成本和玩法的交叉判断如果游戏逻辑复杂度高、并且团队主要擅长 Unity直接转如果玩法偏界面轻交互、团队里有前端高手可以认真评估重写路线。最怕的是两头摇摆大会开完说转做了两周又说重写最后两边都耽误。1.3 预期管理先定一个“最小跑通目标”很多人都想一步到位把整个游戏塞进小游戏结果第一次构建出来白屏就直接放弃了。正确做法是先定一个“最小跑通目标”找一个核心场景包含 UI、基础玩法循环、一个资源加载流程先在这个小目标上完成构建、加载、适配、真机预览。这个环境跑通了再慢慢往里搬内容搬一层测一层。后面很多“玄学问题”其实都出在目标太大、变量太多。小目标跑通后你能明显感知到哪一类问题是构建配置导致的哪一类是场景资源导致的哪一类是平台 API 限制导致的。2. 环境准备与 Unity 工程基础设置2.1 开发工具清单接入抖音小游戏不是只装个 Unity 就行需要的工具链大致有这几块Unity 编辑器建议 2020 LTS 或 2021 LTS太老的版本对 WebGL 的 IL2CPP 支持和新硬件兼容都有问题、抖音开发者工具用于创建小游戏项目、预览、调试、打包上传、一个可以测试的抖音 App 真机最好准备低配和中配两台性能差距能试出很多问题、以及代码和资源管理工具。另外还需要一个能查询抖音小游戏开放平台文档的账号很多能力申请、版本发布、广告组件开通都在后台操作。如果项目里要接虚拟支付还得提前走主体资质审核流程这些线下流程比写代码耗时长得多别等开发完才去申请。2.2 Unity Build Settings 的基础切法打开 Unity 项目后第一件事是在 Build Settings 里把目标平台切到 WebGL。这一步看着简单但工程里很多配置会在切换瞬间失效比如特定平台的宏定义、SDK 引用、插件目录下的原生库全都要重新梳理。切到 WebGL 后有几个关键选项直接决定后面能不能跑Code Optimization 选代码大小优先还是运行速度优先需要根据包体压力动态调Data Caching 建议开启能减少部分重复读取开销配合小游戏远程资源加载会更友好压缩格式尽量优先考虑 Brotli 或 Gzip前提是服务器和小游戏容器都支持对应 Content-Encoding。还有一个很容易漏的地方是 Player Settings 里的其他分辨率。WebGL 平台没有原生分辨率一说像素尺寸在小游戏里等同于 Canvas 尺寸要结合抖音小游戏的设计宽度去做基准。很多团队带着移动端 Portrait 逻辑直接搬结果在折叠屏和不同宽高比机器上布局乱成一锅粥。2.3 Unity 工程里的小游戏适配层工程层面要做的最重要的改造是在代码里增加一条平台分支用#if UNITY_WEBGL把所有和平台强相关的内容隔离出来。比如登录逻辑App 里拿到的是设备 ID、账号体系小游戏里拿到的是宿主提供的用户凭证必须走宿主登录能力再比如持久化App 可以读写本地文件小游戏里只能用类似 localStorage 的接口PlayerPrefs 虽然能用但底层实现和真机行为都可能有一点差异再比如 SDK 入口广告、支付、分享这些能力在小游戏里都必须通过全局对象调用不能在 Unity 的 C# 层直接 new 一个 App 端 SDK。建议从项目初始化开始就统一封装一个PlatformBridge类凡是平台差异能力都走这一个接口后面无论是接抖音还是以后接其他小游戏平台改动都会集中在少数几个文件里。我之前见过一个项目把微信登录逻辑散落在十几个脚本里换抖音平台时改到怀疑人生。3. 核心流程Unity 构建转抖音小游戏的完整步骤3.1 整体方案思路现在 Unity 接抖音小游戏的主流做法是构建出 WebGL 产物后利用小游戏运行时的适配层把启动入口接到一个 JavaScript 壳子中。抖音小游戏运行时的全局对象是tt相关 API 的写法和微信小游戏的wx高度相似所以不少团队会先参考微信小游戏的适配链路再移植到抖音环境。但“相似”不等于“相同”每个平台的启动参数、分包方式、能力开放范围、审核规范都不一样。实际项目推进时一定要以抖音开发者工具里的运行表现为准不能用“微信能跑所以抖音肯定能跑”这种偷懒逻辑。3.2 实操步骤从 Unity 导出到小游戏运行整个链路我会拆成五个大步骤每一步都有明确的验收点。第一步检查 Unity 版本和 WebGL 构建配置。建议用 LTS 版本确保 WebGL 相关 Module 已经安装。打开 Build Settings选择 WebGL确认安装模块然后进入 Player Settings 把 Editing Scripting Backend 切到 IL2CPP。第二步处理工程里的非兼容代码。删除或条件编译所有依赖原生插件的逻辑包括 Native DLL、Java 层调用、部分高级别图形 API 的硬编码。还有一些第三方 SDK 如果只提供 Android/iOS 版必须替换为 WebGL 或小游戏适用的版本。第三步构建 WebGL 产物。把目标场景加入 Build Settings 的 Scenes 列表点击 Build。构建完成后你会得到一整个 WebGL 目录里面有index.html、Build子目录和TemplateData等文件。先不要直接上传这个产物还属于浏览器形态需要经过小游戏运行时处理。第四步搭建小游戏壳工程。在抖音开发者工具里新建一个小游戏项目填好 AppID没有就去抖音开放平台注册申请把你构建出来的 WebGL 目录资源拷贝到对应目录下把默认的小游戏启动逻辑替换成加载该 WebGL 产物的逻辑。这一步说到底就是让宿主环境知道先加载引擎 wasm再加载场景数据然后启动 Unity 渲染循环。第五步在开发者工具里预览调试。先用开发者工具的模拟器确认 UI 能显示、按钮有响应再用真机预览去做性能和功能验证。整个过程中 JavaScript 控制台和网络面板就是你的眼睛任何资源加载失败都会在这里报出来。3.3 几个值得写进配置的参数实际工程配置中有几个参数特别值得关注。包名和版本号要保持一致抖音开放平台后台记录的小游戏版本号跟本地项目版本号对不上上传阶段会直接报错。屏幕方向要去 Player Settings 里设置好抖音小游戏支持横屏、竖屏和全屏一旦发布后再改UI 和安全区适配都要重做。路径设置方面需要用相对路径来加载资源避免写死绝对地址。小游戏环境里不存在“本地文件”和“服务端文件”的传统概念所有远程资源都必须放到配置好的合法域名下否则真机预览时会因为域名拦截而加载失败。3.4 构建过程的常见附加检查项构建时后台跑一堆任务容易让人忽略细节我一般在点下 Build 按钮前会过一遍检查清单是否关闭了所有不必要的引擎模块比如没有物理需求却把 PhysX 打进去包体会白白变重是否确认了Player Settings - Publishing Settings里的压缩格式是否检查场景里有没有引用不存在的脚本或材质这类引用错误在编辑器里不会立刻爆炸但构建后经常直接白屏是否确认了项目里没有大量 Debug 日志每条Debug.Log在 WebGL 下都是真实性能开销。检查清单走完再构建构建后先做一次冒烟测试也就是打开页面看 Unity Logo 能不能正常启动、主场景能不能进、最小交互能不能闭环。冒烟测试没过不要往下做任何优化。4. 平台兼容性适配细节点4.1 生命周期和平台 API 的差异Unity 的OnApplicationPause和OnApplicationFocus在小游戏环境里会被宿主事件驱动。用户切走聊天、接电话、锁屏宿主可能直接暂停游戏甚至过一会儿就直接销毁进程。所以不能依赖 Unity 自己的自动暂停逻辑要在小游戏壳层监听宿主生命周期事件再转发给 Unity。数据保存也是重点。Unity 的PlayerPrefs在小游戏运行时底层通常映射到tt.setStorageSync和tt.getStorageSync这类接口但存储机制、同步时机和 App 本地文件完全不同。保存进度这种关键数据我会封装一层带版本号的序列化存储每次写入时把异常捕获起来避免静默失败导致玩家进度丢失。宿主环境还提供了大量原生能力包括剪切板、震动、设备信息、网络状态变更、截屏等。这些能力能通过tt对象暴露给 JavaScript再通过 C# SDK 调用。不要自己在 C# 层硬模拟这些能力稳定性差且审核可能出问题。4.2 触控、音频和 UI 输入适配触控方面小游戏里的触摸事件是通过 DOM 事件或tt.onTouchStart这类接口映射到 Unity 内部的。大多数情况下 Unity 的Input.touches能拿到数据但注意硬件设备上同时触摸点数上限、坐标比例和 App 端不同UI 点击区域如果设计得太小在真机上很难点中。音频方面现在很多小游戏运行时要求用户产生一次交互后才能播放声音这跟浏览器自动播放限制的思路类似。我在接入时会在玩家第一次点击屏幕时做一个音频上下文初始化把所有背景音乐和音效的播放都放到这个初始化之后。否则会出现游戏跑得很顺但完全没声音折腾半天其实是触发时机的锅。UI 方面小游戏的安全区概念要学会用尤其竖屏游戏顶部传感器区域和底部手势条会遮挡 UI。Unity 的 Canvas 需要按实际可用高度适配不能一直用满屏高度做锚点否则发布后一大堆真机 UI 被顶出屏幕。4.3 资源加载、分包和远程资源小游戏对启动加载速度非常敏感。玩家点击图标到能玩等待时间越短留存越高。所以资源加载策略的第一原则是“启动时只加载主场景必要资源其他内容后续动态加载”。Unity WebGL 里的场景和 AssetBundle 都能打包我一般会把核心玩法场景放进首包把美术资源、音频、未解锁玩法模块做成 AssetBundle 走远程加载。远程加载需要有 CDN 地址并且要在抖音开放平台配置下载白名单。加载过程要做进度条和加载失败重试不要在后台静默加载玩家会以为游戏已经卡死。分包方面抖音小游戏目前对主包大小管得比较严超限会无法过审或发布时直接被拒。实际做法通常是保证主包控制在较小的体积引擎代码已经占了很大部分资源和脚本尽量往后放。这个体积上限要看最新开放平台规范但不管上限多少“启动资源轻量化”都是不变的铁律。4.4 性能优化要从构建期开始小游戏环境的性能天花板比 App 低不少Unity 项目的性能优化不能拖到最后一刻。我能给出的最直接建议是经常在低端安卓真机上预览不要只在中高端机型和开发者工具模拟器上跑。渲染层面控制 Draw Call合批能用尽量用动态合批和静态合批场景里都要打开Shader 尽量用移动端 friendly 的变体复杂的后处理特效在小游戏上很容易掉帧粒子数量限制一下一个屏幕里几十个粒子系统叠加能让低端机直接发热。内存层面WebGL 环境总内存有限纹理用压缩格式不需要的 Mipmap 就不要生成。场景卸载时一定要把 AssetBundle 真正 Unload否则内存缓慢上涨到某个临界点小游戏会被宿主强制杀进程表现就是玩家打着打着突然闪退。CPU 层面避免在 Update 里做高频 Find、GetComponent 和字符串拼接这些在 App 端还能忍在 WebAssembly 环境下的开销感知会更强。能 Cache 的全 Cache能 Job 化思考的模块优先考虑物理和动画模块是不是真的每一帧都在工作。4.5 广告、支付和分享能力接入抖音小游戏的商业化目前大部分产品靠广告变现也有部分做虚拟支付。广告接入要用抖音开放平台开通的广告组件通过相应的接口去拉激励视频广告、插屏广告和格子广告。广告测试阶段开发者工具里一般有测试位不要直接上线上广告位。接入广告有个核心体验问题要注意Unity 小游戏里弹出广告本质上是从 Unity 渲染切到宿主原生 UI再切回来。切换过程中游戏状态可能会暂停如果广告回调触发时机和 Unity 生命周期对不上很容易出现“看完广告游戏卡死”的鬼故事。我的做法是广告播放前强制保存当前状态广告结束后统一恢复而不是依赖回调里立刻执行场景操作。分享裂变也是小游戏流量来源的重要部分分享入口要做得自然开发者可以通过分享参数识别新用户来源。分享图片和文案的配置建议在客户端代码里动态生成避免每次都要发版本改文案。5. 常见问题与排查技巧实录5.1 白屏和黑屏第一嫌疑不是渲染而是加载我见过太多人一白屏就怀疑 Shader、渲染和 GPU实际上 90% 的启动黑屏都出在资源加载层。打开开发者工具 Console先看有没有报错wasm 文件加载失败、脚本解析失败、JSON 资源找不到、域名不在白名单等。其中“域名不在白名单”在真机预览里最常见开发者工具模拟器因为安全策略不同经常不会暴露这个问题。排查时我会把网络请求全部打开按时间线看 Unity 引擎内嵌的加载逻辑走到哪一步断的。如果请求对应文件返回 404先看路径大小写和相对路径对不对如果返回正常但后续脚本抛异常再往引擎初始化流程排查。5.2 构建成功了但场景一片空白这种情况往往是在编辑器和模拟器正常真机上什么都没有。先确认场景里的 Camera 是否被正确保留WebGL 导出时会剔除部分不用的脚本和依赖如果脚本用了反射创建组件很可能会在 Strip Engine Code 时被误删。解决这类问题的手段是把需要保留的类型用[Preserve]标记或加到 link.xml 里。我一般在工程根目录维护一份 link.xml把热更用的、反射用的、序列化用的类型全部显式列出来这个小文件能省掉大量“真机灵异问题”。5.3 音频不响或只有部分机型不响音频问题的第一查法是看用户交互时机。小游戏限制自动播放并不罕见代码里把AudioListener.pause和音频实例的 Play 放在首次点击之后通常能解决一大批不响的情况。如果是特定机型不响要去查音频格式兼容性。抖音小游戏跑的底层是一个微型浏览器内核不同安卓机 WebGL 音频解码路径差异很大在项目里尽量用兼容性更稳的压缩格式避免使用特别冷门的编码参数。5.4 真机性能差距极大经常有开发者说“我手机很流畅测试机卡成幻灯片”。我建议准备一台入门级安卓机做基准性能机把帧率统计、内存占用统计做成一个调试面板。前期就把目标定在低端机流畅跑而不是高端机完美跑。资源一多所有优化都要重新量化所以性能测试也要跟着内容迭代持续跑不能只在最后一次集中测。5.5 包体超了怎么瘦身包体超限后第一刀砍未使用的引擎模块第二刀查纹理资源第三刀关 Strip Engine Code 优化级别第四刀把非核心资源全部改成远程加载。建议按这个顺序来因为引擎模块开关对包体影响大但后果难以一眼看到纹理却可以直接用工具分析 AssetBundle。还要注意 Unity 自带的一些 Demo 素材和示例资源特别是从商店项目里直接导入的有时会藏在隐藏目录里不带回提示。构建前用资源管理器把整个工程的关键资源过一遍能瘦下来一大截。6. 一些经验说在最后接入抖音小游戏这件事技术上没有特别玄学的壁垒大多数问题都是因为团队不了解小游戏运行时的边界条件。我个人的看法是一定不要让主程一个人扛所有适配工作最好让前端同事参与小游戏壳层的维护因为平台能力调用、分包策略、域名配置、启动性能这些事本质上带前端工程色彩。另外一个小经验日志和异常上报一定要最早接入。小游戏用户量大、机型杂线上问题靠用户截图和描述很难定位。把 Unity 日志和 JS 日志统一收集到一个后台每次发版后先观察一天异常量出现集中报错马上回滚比在开发者工具里复现线上问题要高效得多。最后建议所有准备接抖音小游戏的团队把“先跑通一个小 Demo”放在第一优先级。一个小 Demo 能过滤掉绝大多数方案层面的大坑也能让团队对性能上限有直观认知。Demo 阶段积累的适配代码和文档之后每一项都能继续用不是白干。祝大家接入顺利少踩几个我已经踩过的坑。