常见问题
以下是开发者在使用 Tiny.js 做游戏开发过程遇到的一些常见问题,如何解决及规避的总结。 首先,请确保使用最新版本的 Tiny.js。
一、渲染内核
1-1 关于渲染模式选择
Tiny.js 支持 WebGL 和 Canvas 两种模式来渲染。但 WebGL 渲染会导致以下问题:
- Android 4.x、5.x 设备有一定概率渲染异常,Android 6.0.x 系统会有一定概率出现闪屏及黑屏,部分华为低端设备(黑名单统计中)闪退数量增加,因此可以:
- Android 6.1 系统版本以下的设备使用 Canvas 渲染模式
- Android 其他系统版本及设备使用自动识别渲染模式(即不传
renderType
或传入Tiny.RENDERER_TYPE.UNKNOWN
)
1-2 iOS 设备请使用 WKWebview
iOS 设备使用 UIWebview 进行渲染时,Canvas 区域会有小概率出现蓝屏问题,需要在可以使用 WKWebview 的设备上尽量开启这一特性。
二、绘制性能
2-1. Canvas 渲染模式下避免使用 Tiny.Graphics
Tiny.Graphics
底层使用的是 Canvas API 来绘制图形,每一帧进行贴图都会进行计算并绘制。如非必要,请尽量使用图片材质
2-2. 帧绘制时无需清理 canvas
如果绘制区域的背景非透明,则可以设置 renderOptions.transparent: false
这一启动参数,这样在每一帧绘制时渲染引擎无需清理 canvas,可以节省一定的 CPU 开销。具体配置请参考启动参数。
2-3. 合理选择帧率来平衡性能与体验
Tiny.js 引擎在 1.1.7 版本增加了帧率设置启动参数。在不明显影响体验的基础上,可以通过降低帧率,即设置 fps
启动参数,来有效减少 CPU 性能开销。具体配置请参考启动参数。
2-4. 使用 ParticleContainer 来优化绘制性能
对于一些重复精灵、大量重复运动的场景,可以使用 ParticleContaier 来优化渲染性能。具体可参考:ParticleContainer
2-5. 帧动画可能会导致渲染帧率恢复到 60FPS
在 1.1.7、1.1.8、1.2.0、1.2.1 版本的 Tiny.js 下,如果游戏设置 60 以下的 FPS 帧率,当帧动画(AnimatedSprite
并且 loop: false
)播放完成时,游戏主场景帧率会自动恢复到 60 FPS,增加 CPU 性能开销
三、内存优化
3-1. 清理 actions
如果 Tiny 对象重复或循环执行一些特定的 action,那么在执行动画动作前,需要调用 Tiny.Action.cleanup
,来清理 Tiny 对象上残留的 Tween
实例引用,避免造成内存泄漏。
// noteLeft 是一个 Tiny.Sprite 实例
const flyLeft = Tiny.MoveTo(3000, Tiny.point(-10, -10));
const fadeOutLeft = Tiny.FadeOut(3000);
Tiny.Action.cleanup(noteLeft);
noteLeft.runAction(flyLeft, fadeOutLeft);
3-2. 清理骨骼显示对象
如果使用了骨骼动画,那么在移除骨骼动画时,请调用骨骼显示对象的 dispose
方法来销毁骨骼。
armatureDisplay.dispose();
3-3. 开发粒子特效注意内存回收
在手动开发粒子特性动画时,注意循环利用或者在不用时销毁动画对象,来避免造成内存无限增长。
particle () {
// 每个例子都会执行该方法来绘制运动轨迹
const snowflake = new Tiny.Sprite(Tiny.TextureCache['snow']);
snowflake.setScale(random(0.8, 1.2));
snowflake.setOpacity(random(0.8, 1));
const fallAction = Tiny.MoveTo(1e3, Tiny.point(0, 100));
fallAction.onComplete = () => {
container.removeChild(snowflake); // 运动结束后移除 snowflake 对象
};
container.addChild(snowflake);
snowflake.runAction(fallAction);
}
3-4. 减少加载 JSON 文件的 xhr 请求
可以通过将材质数据以 JSON 对象的形式内联进 js,而不是构建成单个 JSON 文件。这样可以直接使用 Tiny.loaders.Loader
加载 JSON 对象(需传入 metadata.JSONObject
),来有效减少加载 JSON 文件产生的 xhr 请求。
四、图片拼叠
4-1. 需注意 TilingSprite 平铺图片的像素
使用 TilingSprite
绘制纯色或者渐变背景时,如果重复的图片宽度(或者高度)大于 1,在某些特定的像素值上,在部分 iOS 设备上,会导致 webview 卡死。
4-2. 不要使用 TilingSprite 平铺雪碧图的图片
使用 TilingSprite
平铺图片时,请确保使用的是单张图片,不要将图片合成到雪碧图中。否则会造成平铺的颜色产出半透明的渐变。
4-3. NinePatch 九宫格渲染间隙规避
使用 NinePatch
绘制九宫格时,通过 canvasPadding: 1
来规避 canvas 渲染间隙问题。(tinyjs-plugin-ui 默认已经规避了这一问题,请确保不要手动改动这一参数)。
4-4. Mesh 网格的渲染间隙规避
使用 canvas 渲染模式,需要设置网格(tinyjs-plugin-mesh
、tinyjs-plugin-drangonbones
)的 canvasDrawTimes
为一个合理值(1到10),来来规避 canvas 渲染间隙
五、文字相关
5-1. 避免重复创建 Tiny.Text 实例
每次使用 Tiny.Text
创建文字实例时,Tiny.js 引擎都会创建一个离屏 canvas 的 DOM 节点,来进行一些文字测量的相关计算。因此要注意不要循环重复创建实例,要尽量重复利用已创建 Tiny.Text
实例。否则会引发内存泄漏。
5-2. canvas 无法支持特定字号的 emoji 字符
Tiny.Text
实例中的文字如果含有 emoji 表情,特定 fontSize
下,可能会无法展示 emoji 字符,这是由于 canvas 的底层绘制引起,无法解决,但可以通过尝试调整到特定字号(一般是减少字号)来规避。
5-3. 正确截断含有 emoji 字符的文字
如果在 emoji 字符的中间位置进行截断,可能会导致文字中的截断位置以前的其他字符被截掉,影响展现。所以需要在 emoji 字符开始或者结束的位置进行截断。可以通过 encodeURIComponent
方法来判断是否在正确的位置进行了截断:
try {
encodeURIComponent(text); // 截断不完整的 emoji 会报错
} catch (e) {}
六、其他
6-1. 避免使用客户端时间
涉及时间获取的逻辑请尽量使用服务端时间。避免使用 new Date()
或 Date.now()
来获取时间。也可以 Tiny.getTime()
方法来获取时间,但在 iOS 8.X 系统上底层还是依赖于 Date
对象实现,需要特殊处理。