文章目录
- 1 Windows
- 2 Android
- 2.1 横版 / 竖版游戏
- 2.2 API 最低版本
- 2.3 目标帧率
- 2.3.1 targetFrameRate
- 2.3.2 vSyncCount
- 2.3.3 Unity 默认设置以及推荐设置
- 2.3.4 Unity 帧率托管
- 3 WebGL
- 3.1 平台限制
- 3.2 打包报错记录 1
- 3.3 打包报错记录 2
最近尝试将写的小游戏打包,主要平台包括 Windows、Android 和 WebGL,以下是一些打包过程记录。
- Unity 版本:6000.0.26f1c1
1 Windows
不得不说,Windows 平台是真好,打包一切顺利,没有任何卡壳,太感动了〒▽〒。
点击 Build And Run,直接无脑生成 exe。

小游戏的话不需要全屏,所以我设置了 Windowed。

2 Android
需要做一些处理,没有那么顺利。
2.1 横版 / 竖版游戏
需要设置游戏是横版还是竖版,我将 Default Orientation 设置了 Auto Rotation,即自动翻转,并将 Allowed Orientations for Auto Rotation 仅设置 Landscape Right/Landscape Left,即可以横版左右翻转。

以下是相关设置说明:
-
Default Orientation:指定应用程序窗口在设备屏幕内的方向。
可选项包括:
- Portrait:竖屏显示。
- Portrait Upside Down:竖屏倒立显示。
- Landscape Right:横屏显示(手机右侧朝下)。
- Landscape Left:横屏显示(手机左侧向下)。
- Auto Rotation:自动旋转。
-
Auto Rotation Behavior:自动旋转方式。
可选项包括:
- User:用户可以锁定设备的自动旋转方向设置以关闭自动旋转。
- Sensor:依据设备传感器数据进行旋转,用户无法进行干预。
2.2 API 最低版本
Minimum API Level 必须手动设置,一般设置为 API Level 33,或者依据 Build 提示设为其指定值。我这里依据 Build 提示,设置为 API Level 34。

有意思的是,Unity 默认会选很古老的 API,例如 Android 8.0(API Level 26)。
但奇怪的是,在 Unity Hub 下载 Android 模块时,往往只下载了 API Level 33 ~ 35,这就非常搞鬼了,不手动设置的话,基本第一次 Build 都会报错,因为压根没装那么低版本的 API。

2.3 目标帧率
通过 targetFrameRate(软件计时、手动设置)和 vSyncCount(硬件支持、自动设置)两种方式进行控制,只会同时生效其中一种(不考虑 VR 设备):
vSyncCount = 0
时,targetFrameRate
生效。vSyncCount != 0
时,vSyncCount
生效。
2.3.1 targetFrameRate
targetFrameRate
是指定 Unity 尝试渲染游戏的目标帧速率,需要在程序运行之初使用代码进行设置。参考官方文档:https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Application-targetFrameRate.html。
不做任何手动设置时,Android 固定 30 fps 渲染(即 targetFrameRate = 30
),以节省电池电量,与显示器的本机刷新率无关。
说明:
targetFrameRate
只是帧率上限,实际帧率只会比这个值低或等于。不是说设置为 120 fps 后,老年机也能跑 120 帧。
targetFrameRate = -1
,表示帧率不封顶,帧数能跑多高就会跑多高。受到显示器最大刷新率的限制,实际帧率不会超过显示器的本机刷新率。
如果
targetFrameRate = 120
,但是手机屏幕最高仅支持 90 帧,那么实际帧率最大也只能是 90。在 Android 和 iOS 上,
targetFrameRate
将向下舍入到能够整除显示器当前刷新率的最大值。例如,在 60Hz Android 显示器上运行时,且设置
targetFrameRate = 25
,则设备帧率最高为 20fps,因为 20 是小于 25 且可以整除 60 的最高数字。在 Windows 和 Web 上,不建议使用
targetFrameRate
,因为targetFrameRate
基于软件计时,容易出现轻微卡顿现象。
最初不知道如何设置的时候,导出 APK 在手机上运行时只有 30 帧超级卡,我还以为是自己性能优化没做好 hh。
2.3.2 vSyncCount
vSyncCount
表示每帧之间应传递的垂直同步数,即游戏允许在帧之间传递的屏幕刷新次数。范围是 0~ 4 内的整数,默认为 1。参考官方文档:https://docs.unity3d.com/6000.0/Documentation/ScriptReference/QualitySettings-vSyncCount.html。
假设目标设备显示器的刷新率为 90 fps,当 vSyncCount > 0
时,游戏目标帧为 90 / vSyncCount
。
vSyncCount
能够在 “Project Settings” -> “Quality”-> “VSync Count” 中进行设置:
- Don’t Sync:即
vSyncCount = 0
。 - Every V Blank:即
vSyncCount = 1
。 - Every Second V Blank:即
vSyncCount = 2
。

说明:
- 在 Android 和 iOS 上,
vSyncCount
始终被忽略,因为移动设备不允许不同步渲染(使用targetFrameRate
控制帧速率)。- 在 Windows 和 Web 上,建议使用
vSyncCount
,因为其基于硬件同步机制,能够产生完全无卡顿的输出。
2.3.3 Unity 默认设置以及推荐设置
平台 | Unity 默认设置 | 推荐设置 |
---|---|---|
Windows | vSyncCount = 0, targetFrameRate = -1 (帧率不封顶,不同步渲染) | 默认即可 |
Android / IOS | targetFrameRate = 30 (固定 30 帧渲染) | targetFrameRate = 60 |
WebGL | vSyncCount = 1 (垂直同步,以本机显示刷新率渲染) | 默认即可 |
实现代码:
public class GameStart : MonoBehaviour
{
private void Awake()
{
#if PLATFORM_ANDROID
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 60;
#endif
}
}
2.3.4 Unity 帧率托管
在 “Project Settings” -> “Player”-> “Resolution and Presentation” -> “Resolution” 中,可勾选 “Optimized Frame Pacing”,从而让 Unity 均匀分布帧,以减少帧速率的差异并创造更流畅的体验,提供更平滑的帧率表现。

但参考该文:https://blog.csdn.net/m0_63261863/article/details/143192236,勾选可能导致游戏降频,建议结合自己情况勾选。
3 WebGL
WebGL 平台限制是真多,导出过程真的快吐了。
但优点是链接点开即玩,所以没有办法。
3.1 平台限制
-
不支持访问本地文件。
对资产数据和 AssetBundle 的网络请求会缓存在浏览器缓存中。
-
不支持多线程。
不支持 C#
System.Threading
命名空间中的任何内容。 -
不支持
System.Net
命名空间内的 .NET 网络类。 -
不支持 Unity 内部 AudioSource 与 AudioClip 的部分 API。
这将导致,在 Unity 编辑器中使用 Addressables 模拟真机 AB 包加载音频时,无法正确播放。只能使用 Use Asset Database,或者将音频文件放在 Resources 文件夹下(不建议这样做)。

-
不允许使用
System.Reflection.Emit
动态生成代码。 -
不支持同步加载资源,只能异步加载。
3.2 打包报错记录 1
一次偶然的机会,看到 Low 选项,不自觉设置为了 Medium。

于是 WebGL 下打包报错,获取不到单例的非公共构造函数。
报错代码:FrameworkException: Non-Public Constructor() not found! in Framework.Toolkits.PoolKit.SingletonObjectPool`1[Framework.Toolkits.AudioKit.AudioPlayer] at Framework.Toolkits.SingletonKit.SingletonCreator.CreateNonPublicConstructorObject[T] () [0x00000] in <00000000000000000000000000000000>:0

查原因后,发现 Strip Engine Code 等级为 Medium 及以上时,会对反射的代码产生影响。而恰好,我的单例是通过反射获取私有无参构造函数创建的(官方链接:https://docs.unity3d.com/6000.0/Documentation/Manual/class-PlayerSettingsWebGL.html)。解决方案就是将 Strip Engine Code 等级调回 Low 即可。

-
Strip Engine Code:仅适用于 IL2CPP 脚本后端。
勾选会剔除应用程序不使用的 DLL,以减少内置播放器的大小,建议勾选。
-
Managed Stripping Level:控制剔除程度。
-
Minimal
使用此选项以剥离类库、UnityEngine、Windows 运行时程序集,并复制所有其他程序集。
-
Low
删除无法访问的托管代码,减少构建大小和 Mono/IL2CPP 构建时间。
-
Medium
运行 UnityLinker 以减少代码大小,剔除范围大于 Low 选项,且某些反射代码路径的行为可能不同(支持自定义 link.xml 文件)。
-
High
运行 UnityLinker 进一步减少代码大小,剔除范围大于 Mediumm 选项,某些反射代码路径的行为可能不同,同时某些方法的托管代码调试可能不再起作用(支持自定义 link.xml 文件)。
-
3.3 打包报错记录 2
有次无意间不满意新写的代码,直接将老版本代码(unitypackage)覆盖导入 Unity,之后再进行 WebGL 打包时发生如下报错:
Build completed with a result of 'Failed' in 129 seconds (129224 ms)
Building Library\Bee\artifacts\WebGL\build\debug_WebGL_wasm\build.js failed with output:
P:\Unity Project\Mine\Survivor Game 2.0>set MYDIR=S:\Unity Editor\6000.0.26f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\
P:\Unity Project\Mine\Survivor Game 2.0>goto FOUND_MYDIR
wasm-ld: warning: function signature mismatch: UxmlFactory__ctor_m87E7DA1E842A9B4B4A45132392EDA2868D62B8A2
>>> defined as () -> void in P:/Unity Project/Mine/Survivor Game 2.0/Library/Bee/artifacts/WebGL/il2cppOutput/build/GameAssembly.a(3yoe1qmioyer.o)
>>> defined as (i32, i32) -> void in lto.tmp
wasm-ld: warning: function signature mismatch: VisualChangesProcessor_get_elementBuilder_m759F6B67F45FB60E9B1E4C646F26689258317B1E
>>> defined as () -> void in P:/Unity Project/Mine/Survivor Game 2.0/Library/Bee/artifacts/WebGL/il2cppOutput/build/GameAssembly.a(lsyvkdbleliz.o)
>>> defined as (i32, i32) -> i32 in lto.tmp
wasm-ld: warning: function signature mismatch: Area__ctor_m6128DC659857264FD4FBF06C01E0C2F04EAA4105
>>> defined as () -> void in P:/Unity Project/Mine/Survivor Game 2.0/Library/Bee/artifacts/WebGL/il2cppOutput/build/GameAssembly.a(kkef0xumaj1j.o)
>>> defined as (i32, i32, i32) -> void in lto.tmp
wasm-ld: warning: function signature mismatch: SingleQueryMatcher_IsInUse_mF17B2C22806E732E50055D4F6AC8A0E3ABEB4B99
>>> defined as () -> void in P:/Unity Project/Mine/Survivor Game 2.0/Library/Bee/artifacts/WebGL/il2cppOutput/build/GameAssembly.a(z4arkogp7up0.o)
>>> defined as (i32, i32) -> i32 in lto.tmp
...
最初认为是项目中部分插件不支持 WebGL 平台,倒腾一番后,又重装了 WebGL 打包模块,依然是同样的报错。
解决方案:删除该项目下的 Library 文件夹,Unity 重新打开项目,没有报错。

分析原因:Library 中缓存的以前打包的结果,导致代码链接失败。