创建 v2.4.0 文档
BIN
docs/versioned_docs/version-2.4.0/assets/plugin-uninstall.png
Normal file
After Width: | Height: | Size: 21 KiB |
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"label": "最佳实践",
|
||||||
|
"position": 3,
|
||||||
|
"collapsed": false
|
||||||
|
}
|
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 194 KiB |
After Width: | Height: | Size: 114 KiB |
@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
description: "了解并上手社区版提供的新特性。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 新引擎特性
|
||||||
|
|
||||||
|
社区版添加了一些实用的新特性,帮助你更好地开发游戏。
|
||||||
|
|
||||||
|
## 社区版设置面板
|
||||||
|
|
||||||
|
由于 v2.x 没有提供官方 API 往引擎原有的设置面板上添加设置项,所以我们另外增加了一个设置面板。
|
||||||
|
|
||||||
|
依次点击编辑器菜单项 **项目 - 社区版设置** 打开设置面板:
|
||||||
|
|
||||||
|
![settings-panel](./assets/settings-panel.png)
|
||||||
|
|
||||||
|
## 支持 SpriteFrame 给 Spine 换装
|
||||||
|
|
||||||
|
官方文档中介绍了替换 attachment 对象进行换装的方法,但如果动画中有切换 attachment 的关键帧,这种方法就失效了。
|
||||||
|
|
||||||
|
还有一种方法是修改 attachment 的 region 对象来进行换装,但这种方法引擎没有直接支持,所以社区版对其进行了支持。
|
||||||
|
|
||||||
|
只需要一句代码即可使用 cc.SpriteFrame 的数据修改 attachment 的 region 对象数据:
|
||||||
|
|
||||||
|
```js
|
||||||
|
skeletonComponent.setRegionData('Head', 'Head', new sp.RegionData(spriteFrame));
|
||||||
|
```
|
||||||
|
|
||||||
|
![changespine](./assets/changespine.png)
|
||||||
|
|
||||||
|
> 图片中是被换头的小男孩。
|
||||||
|
|
||||||
|
这样做是直接修改了 SkeletonData 的数据,所有使用该数据的 Spine 组件都会受到影响(被换头),但我们提供了克隆 SkeletonData 数据的接口,可前往 [Spine](../user-guide/spine/spine-intro.mdx) 文档了解更多详情。
|
||||||
|
|
||||||
|
## 支持 RichText 自定义材质
|
||||||
|
|
||||||
|
可前往 [RichText 自定义材质](../user-guide/text-render/text-richtext.md) 文档了解更多详情。
|
@ -0,0 +1,119 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
description: "在游戏开发中享受不用关注 Draw Call 的快乐。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 提升游戏性能
|
||||||
|
|
||||||
|
建议先阅读官方文档的 [UI 渲染批次合并指南](https://docs.cocos.com/creator/2.4/manual/zh/advanced-topics/ui-auto-batch.html) 了解一些基础知识。
|
||||||
|
|
||||||
|
社区版对动态合图、文本渲染等功能进行了大量的改进,现在,你只需要确保以下几点就可以大幅提升游戏的性能。
|
||||||
|
|
||||||
|
## 使用动态合图
|
||||||
|
|
||||||
|
在之前,动态合图不支持复用图集的废弃区域,所以我们通常会关闭该特性,倾向于靠静态图集或者自动图集达到降低 Draw Call 的目的。
|
||||||
|
|
||||||
|
现在,社区版几乎重构了整个动态合图系统,实用性得到大幅提升:
|
||||||
|
|
||||||
|
- 支持复用废弃区域
|
||||||
|
- 自动参与多纹理渲染
|
||||||
|
- 支持 Spine 参与动态合图
|
||||||
|
|
||||||
|
你只需要重新启用动态合图,并尽量让更多的纹理都参与合图,即可大幅降低 DrawCall 的数量。
|
||||||
|
|
||||||
|
我们还建议做以下调整:
|
||||||
|
|
||||||
|
**放宽能参与合图的纹理尺寸限制**
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.maxFrameSize = 1024; // 推荐 512、1024 甚至 2048
|
||||||
|
```
|
||||||
|
|
||||||
|
你可以根据游戏运行过程中所使用的动态图集数量,来逐步放宽限制,取得一个平衡点。
|
||||||
|
|
||||||
|
**优化动态图集的使用率**
|
||||||
|
|
||||||
|
当游戏使用的动态图集数量太多,你可以控制某个组件或纹理不参与动态合图:
|
||||||
|
|
||||||
|
- 调整纹理的 `packable` 属性
|
||||||
|
- 调整组件的 `allowDynamicAtlas` 属性
|
||||||
|
|
||||||
|
通过让某些优化性价比较低的纹理不参与合图,例如:
|
||||||
|
|
||||||
|
- 大尺寸、单独出现的纹理:比如背景图,尺寸较大使得占用图集的空间大,并且一个场景内可能只有单个节点会显示,不存在需要大量合批的情况。
|
||||||
|
|
||||||
|
来优化图集的使用。
|
||||||
|
|
||||||
|
**及时释放无用资源**
|
||||||
|
|
||||||
|
由于社区版支持了图集空间的复用,所以释放资源的同时也会将其占用的图集空间释放,以提供给其它纹理使用。
|
||||||
|
|
||||||
|
建议在不用的时候及时释放一些冷门资源,常用的资源则不进行释放,避免频繁释放后加载导致的性能消耗。
|
||||||
|
|
||||||
|
## 使用 Label 缓存模式
|
||||||
|
|
||||||
|
在之前,引擎提供的缓存模式都各有问题,无法在实际项目中使用:
|
||||||
|
|
||||||
|
- Bitmap 模式:虽然能够参与动态合图,但无法复用废弃空间,随着游戏的运行,废弃字符的纹理占满图集后就失去了优化效果。
|
||||||
|
|
||||||
|
- Char 模式:依然是无法复用的问题,仅有一张字符纹理,当纹理被填满后甚至无法渲染出文本。
|
||||||
|
|
||||||
|
现在,由于社区版使动态合图支持了复用,并且还重构了 Char 模式的实现,使得 你只需要合理运用这两种缓存模式即可完成对 Label 的优化工作。
|
||||||
|
|
||||||
|
**首选 Char 缓存模式**
|
||||||
|
|
||||||
|
我们建议 Label 默认使用 Char 缓存模式,相比 Bitmap 模式,Char 模式是按字符进行复用的,有着更高的性能优势。
|
||||||
|
|
||||||
|
**备选 Bitmap 缓存模式**
|
||||||
|
|
||||||
|
如果遇到以下场景,则不适合使用 Char 模式进行渲染:
|
||||||
|
|
||||||
|
- 显示像 Emoji 这种字素簇的字符串,由于内部实现无法分割为单个字符,所以不能正常显示。
|
||||||
|
- 超大字体,可能瞬间占满字符纹理。
|
||||||
|
- 显示聊天消息、输入框、玩家名称这类不可控的内容,由于第一条限制,所以为了保证 Label 能正常显示,则不建议使用。
|
||||||
|
|
||||||
|
这时候就可以改用 Bitmap 模式进行渲染,依然能参与动态合图进行合批。
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
|
||||||
|
- **勿对 `fontSize` 属性进行动画或者缓动**
|
||||||
|
|
||||||
|
无论是否使用缓存模式,也不建议频繁修改 `fontSize` 属性,这会导致每帧都需要重新生成文字纹理,造成大量的性能消耗,可以转为使用节点的 `scale` 来代替。
|
||||||
|
|
||||||
|
- **Char 缓存模式依旧不能在内部字符纹理填满时正常渲染**
|
||||||
|
|
||||||
|
这是引擎原本的限制,我们未对其进行修复,原因是我们认为 8 张数量已经够多了,8 张都用完的情况大部分是没有合理搭配使用两种缓存模式。
|
||||||
|
|
||||||
|
## 启用 Spine 合批
|
||||||
|
|
||||||
|
Spine 组件现在不仅可以参与动态合图,还能与其他渲染组件进行合批。
|
||||||
|
|
||||||
|
只需启用组件上的 `Enable Batch` 属性即可。
|
||||||
|
|
||||||
|
## 提升 TiledMap 性能
|
||||||
|
|
||||||
|
一个 TiledMap 可能会有很多个 TiledLayer,如果开启了 TiledMap 的 Culling 特性,则需要每个 Layer 都单独计算 Culling 数据。
|
||||||
|
|
||||||
|
社区版新增了在满足一定条件的情况下可以复用 Culling 数据的特性,以减少 CPU 的性能消耗。
|
||||||
|
|
||||||
|
可前往 [复用 Culling 数据](../user-guide/tiledmap/tiledmap-culling.md) 文档了解更多详情。
|
||||||
|
|
||||||
|
## 启用多线程支持
|
||||||
|
|
||||||
|
**现在,以下引擎的部分增加了多线程支持:**
|
||||||
|
|
||||||
|
- **资源管线(下载与缓存部分)**
|
||||||
|
- **音频系统**
|
||||||
|
- **XMLHttpRequest**
|
||||||
|
- **WebSocket**
|
||||||
|
|
||||||
|
启用后可以释放其对主线程的占用,减少卡顿现象。
|
||||||
|
|
||||||
|
除此之外,还**支持自定义多线程扩展**,大幅简化了将项目逻辑多线程化所需的步骤!
|
||||||
|
|
||||||
|
并且在微信小游戏平台下还有以下改进:
|
||||||
|
|
||||||
|
- **默认启用网络接口和音频接口的高性能模式**
|
||||||
|
- **网络接口支持 HTTP/2、HTTP/3(QUIC) 协议**
|
||||||
|
|
||||||
|
详情请阅读文档:[多线程支持](../user-guide/multithread/thread-intro)。
|
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "提升游戏的质量。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 提升游戏质量
|
||||||
|
|
||||||
|
社区版加入了一些新特性,使得你能提升游戏各方面的质量。
|
||||||
|
|
||||||
|
## 高 DPI 文本渲染
|
||||||
|
|
||||||
|
以前我们会使用将 Label 字号放大一倍,Label 节点缩小一倍的方式去解决字体模糊的问题。
|
||||||
|
|
||||||
|
而现在不需要了,你可以通过一句代码调整渲染比例:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.sp.labelRetinaScale = 2; // 渲染文本时纹理的缩放倍数,默认值为 1.
|
||||||
|
```
|
||||||
|
|
||||||
|
![labelscaledemo](./assets/labelscaledemo.png)
|
||||||
|
|
||||||
|
> 图片中,上方的是开启后效果,下面是未开启效果。
|
||||||
|
|
||||||
|
**推荐你根据设备像素比(devicePixelRatio)来动态设置该值,并且该值不要大于 `2`,这不会带来更好的效果,但却将字体纹理放大了数倍,会影响到游戏整体性能。**
|
||||||
|
|
||||||
|
可前往 [高 DPI 支持](../user-guide/text-render/text-high-dpi.md) 文档了解更多详情。
|
92
docs/versioned_docs/version-2.4.0/breaking-change.md
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 5
|
||||||
|
description: "一般情况下都不需要了解。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 破坏性变更
|
||||||
|
|
||||||
|
在添加新特性的过程中,有些变化在所难免,你可以浏览本文档以进行评估是否会对项目造成影响。
|
||||||
|
|
||||||
|
## 默认禁用原生 TTF 渲染器
|
||||||
|
|
||||||
|
引擎的 Label 在使用 Char 缓存模式并且使用 TTF 字体时,会使用一个原生 TTF 渲染器。
|
||||||
|
|
||||||
|
这个渲染器理论上能提升原生平台的 Label 性能,但仅在 Char 缓存模式并且还要使用 TTF 字体时才生效,这也导致了在原生平台上字体样式可能与其它平台不一致的问题。
|
||||||
|
|
||||||
|
在重构 CHAR 缓存模式时考虑到这些因素和人力有限的原因,我们暂时不打算适配这个原生 TTF 渲染器,所以直接默认禁用了原生 TTF 渲染器,这个禁用是官方提供的接口,不会造成其它问题。
|
||||||
|
|
||||||
|
**大部分项目可以不用关心**。
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.macro.ENABLE_NATIVE_TTF_RENDERER = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 动态图集的一些变化
|
||||||
|
|
||||||
|
对动态图集的重构虽然保留了所有原有的公开接口,但实现细节与以前不同了。
|
||||||
|
|
||||||
|
如果你的项目有在细致地管理动态图集,请注意以下几点:
|
||||||
|
|
||||||
|
- **如果你有手动修改 `cc.dynamicAtlasManager.maxAtlasCount` 属性,请考虑删除**
|
||||||
|
|
||||||
|
社区版会根据设备最大纹理数和 Char 缓存模式字符图集的相关设置自动调整动态合图的最大图集数量。
|
||||||
|
|
||||||
|
这不意味着你不能手动调整了,而是建议你阅读新动态图集和新 Char 缓存模式相关的文档后再考虑是否有必要进行调整。
|
||||||
|
|
||||||
|
关于这个你可以查看 [提升项目性能](./best-practices/performance-guide.md) 了解。
|
||||||
|
|
||||||
|
- **动态图集重复纹理的判断从 `texture._id` 改为使用 `texture._uuid`**
|
||||||
|
|
||||||
|
具体的设计原因和原理可查看动态合图的原理文档。
|
||||||
|
|
||||||
|
- **`cc.dynamicAtlasManager.insertSpriteFrame(spriteFrame)` 不再检查纹理的 `packable` 属性**
|
||||||
|
|
||||||
|
并不是说 `packable` 属性无效了,而是 `insertSpriteFrame` 现在作为将纹理强制加入动态图集的接口使用。
|
||||||
|
|
||||||
|
- **如果你的项目依赖动态图集的内部实现细节,请重新检查相关代码**
|
||||||
|
|
||||||
|
比如你的项目依赖一些动态图集未公开的类或者接口(引擎在 `creator.d.ts` 声明了的则是已经公开的),则可能需要重新编写。
|
||||||
|
|
||||||
|
## Char 缓存模式的一些变化
|
||||||
|
|
||||||
|
对 Char 缓存模式也进行了重构,内部变化比较大。
|
||||||
|
|
||||||
|
- **暂不支持自定义材质**
|
||||||
|
|
||||||
|
如果项目中有组件在使用 Char 缓存模式并且设置了自定义材质则可能会失效,具体原因可前往 [新的 Char 缓存模式](./user-guide/text-render/text-char-mode.md) 文档进行了解。
|
||||||
|
|
||||||
|
- **如果你的项目依赖 Char 缓存模式的内部实现细节,请重新检查相关代码**
|
||||||
|
|
||||||
|
比如你的项目依赖一些 Char 缓存模式未公开的类或者接口(引擎在 `creator.d.ts` 声明了的则是已经公开的),则可能需要重新编写。
|
||||||
|
|
||||||
|
## 材质 Hash 计算的变化
|
||||||
|
|
||||||
|
在之前,材质的 Hash 发生变化时不会同时更新现有的材质变体 Hash,这导致在修改前创建的变体不能与修改后创建的变体合批(因为 Hash 不同)。
|
||||||
|
|
||||||
|
为了兼顾原生平台与 Web 平台,我们通过使材质本身的 Hash 值不变,并且改变变体的 Hash 计算方式来解决这个问题。
|
||||||
|
|
||||||
|
但这引入了新的问题:完全相同的两个材质不能合批。
|
||||||
|
|
||||||
|
比如你复制了一个 `multi-2d-sprite` 材质,分别使用这两个材质的组件不再会进行合批。
|
||||||
|
|
||||||
|
这种情况比较少见,如果你的项目发生了这种情况,可以手动将新材质的 Hash 值设置为旧材质的 Hash 值,即可让这两个材质合批。
|
||||||
|
|
||||||
|
## 启用多线程时的破坏性改动
|
||||||
|
|
||||||
|
### 资源管线
|
||||||
|
|
||||||
|
当你启用多线程驱动资源管线时,`cacheManager` 的接口有以下不同:
|
||||||
|
|
||||||
|
- `getTemp` 方法被 `getTempAsync` 方法代替,请检查你项目中相关的用法。
|
||||||
|
- 启用多线程之后,会使用多线程版的缓存管理器,如果你的项目访问了未暴露的内部属性,请检查相关的用法。
|
||||||
|
|
||||||
|
### 音频系统
|
||||||
|
|
||||||
|
当你启用多线程驱动音频系统时,以下音频属性是定时更新而不是即时更新的:
|
||||||
|
|
||||||
|
- `duration`
|
||||||
|
- `currentTime`
|
||||||
|
|
||||||
|
这一般不会引起什么问题,无需特别关注。
|
||||||
|
|
||||||
|
如果你正在操作引擎内部维护的平台音频实例的话,需要注意现在的平台音频实例每个事件只支持有一个监听者(普通用户无需关注,AudioSource、CCAudio 等面向用户的 API 依然支持多个监听者)。
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"label": "安装",
|
||||||
|
"position": 2,
|
||||||
|
"collapsed": false
|
||||||
|
}
|
After Width: | Height: | Size: 76 KiB |
BIN
docs/versioned_docs/version-2.4.0/installation/assets/dts.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 23 KiB |
@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
description: "推荐使用此方式进行一键安装。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 一键安装
|
||||||
|
|
||||||
|
我们提供了一个**付费扩展**,该扩展**唯一的作用是帮助你一键安装和管理社区版的版本**。
|
||||||
|
|
||||||
|
它也作为对我们给予肯定和支持的**赞助通道**。
|
||||||
|
|
||||||
|
:::tip 声明
|
||||||
|
|
||||||
|
- **Enhance Kit 不是免费的开源项目吗?**
|
||||||
|
|
||||||
|
再次重申 Enhance Kit 本身是**完全免费开源**的项目,该扩展**永远**只提供一键安装和版本管理的功能。
|
||||||
|
|
||||||
|
也就是说,你可以选择 [手动安装](./installation-manual.md) 来使用,两者没有任何本质区别,也只需要几步即可完成。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 安装扩展
|
||||||
|
|
||||||
|
[点此跳转到商店页面](https://store.cocos.com/app/detail/3824) 下载并安装版本管理扩展。
|
||||||
|
|
||||||
|
## 一键安装
|
||||||
|
|
||||||
|
依次点击 Cocos Creator 的菜单栏 **扩展 - 社区版管理 - 安装... - 安装最新版本**,会自动开始安装社区版。
|
||||||
|
|
||||||
|
![plugin-install](./assets/plugin-install.png)
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
- 社区版会增加一个名称为 `sp` 的 AssetBundle 以存放内置资源,**请确保你的项目未设置同名的 AssetBundle 引起冲突**。
|
||||||
|
- 没有必要将这个 Asset Bundle 设为远程包或者 Zip 压缩,里面只有几个着色器资源。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::note 扩展原理
|
||||||
|
|
||||||
|
扩展使用脚本帮您自动完成手动安装时需要做的步骤,比如设置自定义引擎和放置 TypeScript 类型提示文件。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### 安装其它版本
|
||||||
|
|
||||||
|
依次点击 Cocos Creator 的菜单栏 **扩展 - 社区版管理 - 安装... - 安装其它版本** 可以安装当前引擎支持安装的所有版本。
|
||||||
|
|
||||||
|
可以重复执行安装操作,扩展会自动进行覆盖。
|
||||||
|
|
||||||
|
## 检查安装状态
|
||||||
|
|
||||||
|
你可以运行项目并检查 Console 是否有打印类似 `Cocos Creator SP v2.4.x` 的日志,有的话则已经成功安装。
|
||||||
|
|
||||||
|
![installedconsole](./assets/installed-console.png)
|
||||||
|
|
||||||
|
接下来推荐你继续阅读文档中剩下的内容以了解你能够使用的新特性!
|
||||||
|
|
||||||
|
## 查看信息
|
||||||
|
|
||||||
|
依次点击 Cocos Creator 的菜单栏 **扩展 - 社区版管理 - 查看信息**,会打印当前环境信息。
|
||||||
|
|
||||||
|
![plugin-info](./assets/plugin-info.png)
|
||||||
|
|
||||||
|
你可以看到当前引擎版本、支持一键安装的社区版版本和已安装的社区版版本等信息。
|
@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "需掌握一定的自定义引擎知识。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 手动安装
|
||||||
|
|
||||||
|
## 下载并解压
|
||||||
|
|
||||||
|
前往 [Github Release](https://github.com/smallmain/cocos-enhance-kit/releases) 下载对应版本的压缩包。
|
||||||
|
|
||||||
|
**请确保您的项目的引擎版本与下载的社区版的引擎版本一致。**
|
||||||
|
|
||||||
|
:::tip 提示
|
||||||
|
|
||||||
|
建议升级到最新的引擎版本,官方只会对 2.x 版本只会进行维护性更新,所以不用担心其稳定性问题。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
解压压缩包后可以看到以下目录和文件:
|
||||||
|
|
||||||
|
- `engine`(JavaScript engine)
|
||||||
|
- `cocos2d-x`(Cocos2d-x engine)
|
||||||
|
- `jsb-adapter`(JSB adpater)
|
||||||
|
- `adapters`(Minigame adpaters)
|
||||||
|
- `extension`(支持性引擎扩展)
|
||||||
|
- `creator-sp.d.ts`(社区版类型文件)
|
||||||
|
|
||||||
|
## 替换自定义引擎
|
||||||
|
|
||||||
|
依次进入 Cocos Creator 菜单的 **项目 - 项目设置 - 自定义引擎**。
|
||||||
|
|
||||||
|
分别填写 **JavaScript 引擎路径**(对应 `engine` 目录)和 **Cocos2d-x 引擎路径**(对应 `cocos2d-x` 目录)。
|
||||||
|
|
||||||
|
![custom-engine](./assets/custom-engine.png)
|
||||||
|
|
||||||
|
然后点击 Cocos Creator 主界面右上角的 **编辑器** 按钮,进入到编辑器的资源目录。
|
||||||
|
|
||||||
|
![ide-cocos-path](./assets//ide-cocos-path.png)
|
||||||
|
|
||||||
|
接着使用压缩包内的 `jsb-adapter` 替换掉编辑器的 `Resources/builtin/jsb-adapter` 目录。
|
||||||
|
|
||||||
|
最后使用压缩包内的 `adapters` 替换掉编辑器的 `Resources/builtin/adapters` 目录。
|
||||||
|
|
||||||
|
## 安装支持性引擎扩展
|
||||||
|
|
||||||
|
按照官方的 [扩展安装](https://docs.cocos.com/creator/2.4/manual/zh/extension/install-and-share.html) 文档将压缩包内的 `extension` 目录放到项目的 `packages` 目录即可。
|
||||||
|
|
||||||
|
建议将 `extension` 目录重命名成一个更有意义的名字,比如 `enhance-kit`。
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
- 请勿将此引擎扩展与 [一键安装](./installation-auto) 中的付费引擎扩展混淆!
|
||||||
|
- 该扩展提供社区版运行所需的内置资源,是开源的一部分,**请勿同时安装两个引擎扩展,这会引起冲突**!
|
||||||
|
- 社区版会增加一个名称为 `sp` 的 AssetBundle 以存放内置资源,**请确保你的项目未设置同名的 AssetBundle 引起冲突**。
|
||||||
|
- 没有必要将这个 Asset Bundle 设为远程包或者 Zip 压缩,里面只有几个着色器资源。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## TypeScript 类型提示(可选)
|
||||||
|
|
||||||
|
如果你的项目使用 TypeScript,可将压缩包内 `creator-sp.d.ts` 文件拷贝到项目根目录中以获取新的代码提示。
|
||||||
|
|
||||||
|
![dts](assets/dts.png)
|
||||||
|
|
||||||
|
部分代码编辑器可能需要重启之后类型提示才会生效。
|
||||||
|
|
||||||
|
## 更新多线程扩展的 TypeScript 类型提示(可选)
|
||||||
|
|
||||||
|
如果你正在将项目升级到社区版的新版本,并且之前还创建了多线程扩展的话,可以通过依次点击菜单项 **扩展 - 创建新扩展插件... - 项目多线程扩展** 来更新 `creator-worker.d.ts` 文件。
|
||||||
|
|
||||||
|
关于多线程扩展请阅读文档:[多线程支持](../user-guide/multithread/thread-intro)。
|
||||||
|
|
||||||
|
## 重启 Cocos Creator
|
||||||
|
|
||||||
|
完成以上的步骤后重启 Cocos Creator。
|
||||||
|
|
||||||
|
你可以运行项目并检查 Console 是否有打印类似 `Cocos Creator SP v2.4.x` 的日志,有的话则已经成功安装。
|
||||||
|
|
||||||
|
![installedconsole](./assets/installed-console.png)
|
||||||
|
|
||||||
|
接下来推荐你继续阅读文档中剩下的内容以了解你能够使用的新特性!
|
||||||
|
|
||||||
|
如果编辑器输出了一条有关包体大小的警告,请阅读 [多线程支持](../user-guide/multithread/thread-intro#注意事项) 了解详情。
|
167
docs/versioned_docs/version-2.4.0/intro.md
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
title: 介绍
|
||||||
|
hide_title: true
|
||||||
|
---
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
![logo](/img/logo2.png)
|
||||||
|
|
||||||
|
这是 [Cocos Creator](https://www.cocos.com/creator) v2.x 社区分叉版本,**提供了大量的新特性、性能改进与修复**。
|
||||||
|
|
||||||
|
未来将继续秉持着**完全开源、完全兼容**的理念提供新特性、优化与修复。
|
||||||
|
|
||||||
|
非常欢迎你能和我们一起来完善这个项目!
|
||||||
|
|
||||||
|
## 主要特性
|
||||||
|
|
||||||
|
### 完全兼容
|
||||||
|
|
||||||
|
社区版的现在乃至未来都会做到以下几点:
|
||||||
|
|
||||||
|
- 兼容旧项目,无需改动项目代码即可切换到社区版。
|
||||||
|
- 不会对项目本身进行任何改动,随时可以切换回官方版。
|
||||||
|
- 任何特性都可以选择性开启,保证项目的可控性。
|
||||||
|
|
||||||
|
并且全平台兼容!
|
||||||
|
|
||||||
|
### 支持多纹理渲染
|
||||||
|
|
||||||
|
在之前,每个相邻的节点使用不同的纹理渲染时会打断合批,我们一般通过调节节点顺序、合并图集的方式来避免这个问题。
|
||||||
|
|
||||||
|
但是实际应用起来情况复杂,且操作麻烦,无法优化到极致,而较高的 DrawCall 数量会导致设备性能下降、发热的问题。
|
||||||
|
|
||||||
|
现在,由于支持了多纹理渲染,可允许多达 8 张不同纹理进行渲染时也不会打断合批,**在最坏的情况下,也能将项目的 DrawCall 数量减少至 1/8**!
|
||||||
|
|
||||||
|
并且,该特性在使用动态合图时会自动启用,无需任何改动即可得到优化效果。
|
||||||
|
|
||||||
|
![demo1](/demo-imgs/demo1.png)
|
||||||
|
|
||||||
|
### 支持高清文本渲染
|
||||||
|
|
||||||
|
在之前,Cocos Creator 引擎默认以 1x 的分辨率渲染字体,所以有时候在某些 `Retina` 设备上会感觉到字体很模糊。
|
||||||
|
|
||||||
|
由于引擎本身不支持对其进行调节,我们会通过将 Label 的 `fontSize` 放大一倍,然后将 `scale` 缩小一倍的方式解决字体渲染模糊的问题。
|
||||||
|
|
||||||
|
现在,增加了高清文本渲染的支持,你只需要通过一句代码:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
cc.sp.labelRetinaScale = 2;
|
||||||
|
```
|
||||||
|
|
||||||
|
即可调整引擎文本渲染的分辨率,获得高清的文本渲染效果。
|
||||||
|
|
||||||
|
![demo4](/demo-imgs/demo4.png)
|
||||||
|
|
||||||
|
### 动态合图增强
|
||||||
|
|
||||||
|
除了上面提到将动态合图支持了自动多纹理渲染之外,我们其实几乎重构了整个动态合图模块。
|
||||||
|
|
||||||
|
现在的动态合图即优化了算法,还**支持废弃空间复用**,这对于项目的性能优化空间的提升是巨大的。
|
||||||
|
|
||||||
|
并且,**Spine 现在也可以参与动态合图**了!
|
||||||
|
|
||||||
|
![demo2](/demo-imgs/demo2.png)
|
||||||
|
|
||||||
|
### Label 组件增强
|
||||||
|
|
||||||
|
Label 一直是项目优化的最难点,因为它完全不能和其它的渲染组件进行合批,甚至大部分情况下也不能和其它 Label 组件合批。
|
||||||
|
|
||||||
|
在之前,使用 `Bitmap` 模式可以加入动态合图,以达到合批的目的,但受引擎动态合图不能复用废弃空间的限制,导致项目越运行,可以动态合图的空间就越小,很容易就会将动态图集塞满,所以基本不会在实际的项目中使用。
|
||||||
|
|
||||||
|
`Char` 模式也有类似的问题,内部的字符纹理即不支持复用,还最高仅支持 1 张纹理,纹理很容易就被填满导致无法渲染文本,不会在实际项目中使用。
|
||||||
|
|
||||||
|
但是现在,Label 得到了以下重大的改进:
|
||||||
|
|
||||||
|
- 重构后的动态合图支持复用,`Bitmap` 模式的使用不再受限!
|
||||||
|
- `Char` 模式得到重构,**支持废弃空间复用,支持最多 8 张字符纹理,支持自动多纹理合批**!
|
||||||
|
- RichText 也得到同样的所有改进,并且开放了自定义材质的设置。
|
||||||
|
|
||||||
|
![demo3](/demo-imgs/demo3.png)
|
||||||
|
|
||||||
|
### Spine 组件增强
|
||||||
|
|
||||||
|
除了 Label 之外,如果你的项目用到了 Spine 组件,那么它大概率会成为项目第二个优化难点。
|
||||||
|
|
||||||
|
现在,Spine 也得到了重大改进,**支持与其它组件合批,支持参与动态合图,还能无缝使用 SpriteFrame 进行换装**!
|
||||||
|
|
||||||
|
![demo5](/demo-imgs/demo5.png)
|
||||||
|
|
||||||
|
### TiledMap 组件优化
|
||||||
|
|
||||||
|
单个 TiledMap 组件可能存在多个 TiledLayer,启用 Culling 特性后,每个 Layer 都需要单独计算 Culling 数据。
|
||||||
|
|
||||||
|
现在在满足条件的情况下可以复用 Culling 数据,以减少项目 CPU 的性能消耗。
|
||||||
|
|
||||||
|
### 多线程支持
|
||||||
|
|
||||||
|
**现在,以下引擎的部分增加了多线程支持:**
|
||||||
|
|
||||||
|
- **资源管线(下载与缓存部分)**
|
||||||
|
- **音频系统**
|
||||||
|
- **XMLHttpRequest**
|
||||||
|
- **WebSocket**
|
||||||
|
|
||||||
|
启用后可以释放其对主线程的占用,减少卡顿现象。
|
||||||
|
|
||||||
|
除此之外,还**支持自定义多线程扩展**,大幅简化了将项目逻辑多线程化所需的步骤!
|
||||||
|
|
||||||
|
并且在微信小游戏平台下还有以下改进:
|
||||||
|
|
||||||
|
- **默认启用网络接口和音频接口的高性能模式**
|
||||||
|
- **网络接口支持 HTTP/2、HTTP/3(QUIC) 协议**
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
请阅读文档的 [一键安装](./installation/installation-auto)。
|
||||||
|
|
||||||
|
## 贡献指南
|
||||||
|
|
||||||
|
- 如果你有任何问题或者想法请建立新的 `Issues`,我们会尽自己所能帮助你!
|
||||||
|
- 如果你有新的代码提交,请建立新的 `Pull requests` 以完成合并。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
**为什么要直接修改引擎?**
|
||||||
|
|
||||||
|
官方对 2.x 版本已经停止了更新(仅做一些维护工作),修改引擎不会遇到在官方新版本发布后需要用大量时间去适配,或者特性冲突的问题。
|
||||||
|
|
||||||
|
如果不自定义引擎,则无法兼容原生平台,即使不兼容,使用 Hack 的方式,某些特性的实现需要大量拷贝代码,包体会增大,可维护性会降低。
|
||||||
|
|
||||||
|
最后,我们希望它接近 “原生” 的使用体验,就像只是一次引擎的无痛升级。
|
||||||
|
|
||||||
|
**启动 Cocos Creator 报 Error: Can not parse this input:undefined 错误**
|
||||||
|
|
||||||
|
你可能忘记安装配套的引擎扩展,所以没有找到社区版的内置资源,导致报错,请仔细阅读文档。
|
||||||
|
|
||||||
|
**原生平台或模拟器报错:Assertion failed: (_type == Type::String), function toString, file Value.cpp, line 496.**
|
||||||
|
|
||||||
|
你可能没有替换引擎的 `jsb-adapter` 部分,或者说没有重启以使替换生效。
|
||||||
|
|
||||||
|
并且原生平台需额外注意,升级后的首次构建可能需先删除原有的构建工程才会生效。
|
||||||
|
|
||||||
|
**使用模拟器预览时出现各种问题**
|
||||||
|
|
||||||
|
可能是因为模拟器没有重新编译导致的,请按照官方的 [引擎定制文档](https://docs.cocos.com/creator/2.4/manual/zh/advanced-topics/engine-customization.html#25-%E7%BC%96%E8%AF%91%E6%A8%A1%E6%8B%9F%E5%99%A8) 重新编译原生模拟器即可。
|
||||||
|
|
||||||
|
**Spine 组件出现 Uncaught TypeError: Cannot read property ‘load’ of null, location: src/cocos2d-jsb.b5158.js 等问题**
|
||||||
|
|
||||||
|
这是引擎自身的问题,由于释放 Spine 资源,然后再次加载相同 Spine 资源时导致,具体请看:
|
||||||
|
|
||||||
|
[问题讨论](https://forum.cocos.org/t/topic/137649/148)
|
||||||
|
|
||||||
|
截止 v2.4.12,引擎仍未修复此问题,暂时的解决方案有三种:
|
||||||
|
|
||||||
|
1. 引擎已经有一个修复此问题的 PR,但还未合入,可自行合入:[PR #4307](https://github.com/cocos/engine-native/pull/4307)
|
||||||
|
2. 需要进行释放的 spine 资源,不让其参与动态合图(通过控制 Spine 组件的合图开关)。
|
||||||
|
3. 不释放需要参与动态合图的 spine 资源。
|
||||||
|
|
||||||
|
该问题已在 v2.4.13 版本修复,请升级社区版至 v2.0.0 以上。
|
||||||
|
|
||||||
|
## 支持我们
|
||||||
|
|
||||||
|
社区版的开发需要大量的工作来进行,你的支持是我们最大的动力:
|
||||||
|
|
||||||
|
- 请给我们的项目仓库一个 [Star](https://github.com/smallmain/cocos-enhance-kit)!
|
||||||
|
- 购买可有可无的 [一键安装扩展](https://store.cocos.com/app/detail/3824) 赞助我们!
|
41
docs/versioned_docs/version-2.4.0/uninstall-guide.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
# 卸载
|
||||||
|
|
||||||
|
:::tip 提示
|
||||||
|
|
||||||
|
卸载的原理相同,手动安装后不一定必须手动卸载,也可以使用付费扩展进行卸载,反之亦然。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 卸载前检查
|
||||||
|
|
||||||
|
- 检查项目代码不再依赖社区版提供的特性。
|
||||||
|
- 检查项目没有对社区版扩展内提供的内置资源的引用。
|
||||||
|
|
||||||
|
## 一键卸载
|
||||||
|
|
||||||
|
以下内容仅针对付费引擎扩展编写,可查看 [一键安装](./installation/installation-auto.md) 了解详情。
|
||||||
|
|
||||||
|
依次点击编辑器菜单栏 **扩展 - 社区版管理 - 卸载** 即可。
|
||||||
|
|
||||||
|
![plugin-uninstall](./assets/plugin-uninstall.png)
|
||||||
|
|
||||||
|
之后重启即可生效。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 手动卸载
|
||||||
|
|
||||||
|
依次点击编辑器菜单 **项目 - 项目设置 - 自定义引擎**,勾选使用内置引擎。
|
||||||
|
|
||||||
|
点击 Cocos Creator 主界面右上角的 **编辑器** 按钮,进入到编辑器的资源目录。
|
||||||
|
|
||||||
|
使用引擎原版的 `jsb-adapter` 替换 `Resources/builtin/jsb-adapter`。
|
||||||
|
|
||||||
|
使用引擎原版的 `adapters` 替换 `Resources/builtin/adapters`。
|
||||||
|
|
||||||
|
删除项目内安装的社区版扩展和 `creator-sp.d.ts` 文件。
|
||||||
|
|
||||||
|
重启编辑器生效。
|
7
docs/versioned_docs/version-2.4.0/update-log.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 7
|
||||||
|
---
|
||||||
|
|
||||||
|
# 更新日志
|
||||||
|
|
||||||
|
请前往 [Github Release](https://github.com/smallmain/cocos-enhance-kit/releases) 查看。
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"label": "使用指南",
|
||||||
|
"position": 4,
|
||||||
|
"collapsed": false
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"label": "动态合图",
|
||||||
|
"position": 3,
|
||||||
|
"collapsed": true,
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "dynamic-batcher-intro"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 28 KiB |
@ -0,0 +1,95 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
description: "了解调校动态合图的方式。"
|
||||||
|
---
|
||||||
|
# 调整合图设置
|
||||||
|
|
||||||
|
---
|
||||||
|
## 动态图集最大数量为什么是 7
|
||||||
|
|
||||||
|
在前面的文档中有提到动态图集的最大数量默认为 **设备能同时采样纹理数 - Char 缓存模式自动合批图集数**。
|
||||||
|
|
||||||
|
因为设备能同时采样纹理数固定为 `8`,而 Char 缓存模式自动合批图集数默认为 `1`,所以动态合图的最大数量默认值为 `7`。
|
||||||
|
|
||||||
|
这样就只需要使用 1 个材质,也就是能在 1 Draw Call 里完成所有参与动态合图的纹理(包括 Bitmap 缓存模式 Label)与 Char 缓存模式 Label 的渲染。
|
||||||
|
|
||||||
|
一般情况下不推荐直接修改 `maxAtlasCount`,请参考 [Char 缓存模式](../text-render/text-char-mode.md#与动态图集合批的注意事项) 文档。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 控制纹理是否参与动态合图
|
||||||
|
|
||||||
|
可以在编辑器上调整纹理的 `packable` 属性,或者用代码控制:
|
||||||
|
|
||||||
|
```js
|
||||||
|
texture.packable = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## 控制组件是否参与动态合图
|
||||||
|
|
||||||
|
使用下面的代码控制组件是否默认激活动态合图机制,默认为开启:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.sp.allowDynamicAtlas = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
也可以控制单组件是否激活动态合图机制:
|
||||||
|
|
||||||
|
![dynamic-batch-settings](./assets/dynamic-batch-settings.png)
|
||||||
|
|
||||||
|
除了在编辑器调整,也可以通过代码控制:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// cc.RenderComponent.EnableType
|
||||||
|
// GLOBAL: 全局默认值
|
||||||
|
// ENABLE: 开启
|
||||||
|
// DISABLE: 关闭
|
||||||
|
label.allowDynamicAtlas = cc.RenderComponent.EnableType.ENABLE;
|
||||||
|
```
|
||||||
|
|
||||||
|
如果一个纹理参与动态合图但是组件不参与,那么使用该组件进行渲染时就不会参与,但如果同时在其它参与的组件上渲染,那么依然会被打入动态图集。
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
组件有脏检查标记,修改后可能需要对渲染组件调用 `comp.setVertsDirty()` 才会生效。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
## 是否自动多纹理合批
|
||||||
|
|
||||||
|
控制图集纹理是否会自动添加到多纹理合批管理器,默认为开启状态,如果关闭也就意味着**失去了自动进行多个图集纹理合批的特性**。
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.autoMultiBatch = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## 在场景切换时清空所有图集
|
||||||
|
|
||||||
|
控制在场景切换时是否会清空所有的动态图集,默认为开启状态。
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.autoResetBeforeSceneLoad = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip 提示
|
||||||
|
|
||||||
|
在引擎原来的设计中,该机制不可被关闭,由于旧动态合图不支持复用废弃的空间,图集终究会被用完,所以引擎加入了这个治标不治本的功能。
|
||||||
|
|
||||||
|
但现在,我们认为该机制可以关闭,你只需要管理好纹理资源的释放即可,因为纹理资源释放的同时会释放使用的动态图集空间。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
## 不进行复用的区域空间大小
|
||||||
|
|
||||||
|
在实际的测试中,我们发现废弃空间出现碎片化的现象,比如尺寸 5 * 2 这样的非常小的废弃空间,当碎图尝试加入图集的时候会在这些废弃空间中寻找,这些数量多的小废弃空间无法被复用,却要在每次加入时遍历判断一次。
|
||||||
|
|
||||||
|
所以我们加入了 `ignoreRectSize` 设置,当废弃空间尺寸小于这个值就不会被遍历到(但是能合并为大的废弃空间时还是会合并),这能提升大约 50% 的理论性能。
|
||||||
|
|
||||||
|
这个值默认为 `10`,如果你的项目有很多小于 10 * 10 的纹理,可以考虑进行调整:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.Atlas.ignoreRectSize = 2;
|
||||||
|
```
|
@ -0,0 +1,10 @@
|
|||||||
|
import DocCardList from '@theme/DocCardList';
|
||||||
|
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
# 动态合图
|
||||||
|
|
||||||
|
动态合图是一个非常好的合批手段,但是在之前引擎实现的比较简陋,所以社区版重构了动态合图系统,在原有功能基础上增加了许多重要特性。
|
||||||
|
|
||||||
|
比如支持复用废弃碎图空间,优化了图集装箱算法,所有图集作为一个整体进行管理等等,你可以阅读下面的文档了解详情:
|
||||||
|
|
||||||
|
<DocCardList items={useCurrentSidebarCategory().items}/>
|
@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "随心所欲地控制动态合图的使用。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 手动管理合图
|
||||||
|
|
||||||
|
有时候你可能需要更细致地去控制哪些纹理加入动态图集,考虑到这一点,社区版在保留原来所有接口的基础上完全开放了动态图集相关的所有接口。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 访问图集数组与已用空间集合
|
||||||
|
|
||||||
|
你可以通过
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.atlases
|
||||||
|
```
|
||||||
|
|
||||||
|
和
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.rects
|
||||||
|
```
|
||||||
|
|
||||||
|
分别访问到图集数组与所有图集的已用空间集合。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 添加 SpriteFrame 到动态图集
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.insertSpriteFrame(spriteFrame);
|
||||||
|
```
|
||||||
|
|
||||||
|
可以将 SpriteFrame 所使用的纹理添加到动态图集,这是引擎原有接口,但社区版对其做了一点修改,这个接口不再会检查纹理的 `packable` 属性,也就是变成了一个强制添加的接口。
|
||||||
|
|
||||||
|
这样设计的原因是你可以将所有纹理的 `packable` 都设为 `false`,或者直接将 `maxFrameSize` 设为 `0`,然后完全手动地进行动态合图。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 从动态图集删除 SpriteFrame
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.deleteSpriteFrame(spriteFrame);
|
||||||
|
```
|
||||||
|
|
||||||
|
可以使 SpriteFrame 取消使用动态图集纹理,这不一定会将 SpriteFrame 的纹理从动态图集删除,因为可能还会有其它 SpriteFrame 在使用,只有没有 SpriteFrame 在使用时才会删除纹理。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 从动态图集删除 Texture
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.deleteTexture(texture);
|
||||||
|
```
|
||||||
|
|
||||||
|
这个接口与 `deleteSpriteFrame` 相似,但是它会直接删除纹理,并且会使使用该纹理的 SpriteFrame 全部恢复。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 更多接口
|
||||||
|
|
||||||
|
虽然还暴露了其它接口出来,但因为太过于底层所以不推荐使用,如果你想了解可以阅读原理文档。
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"label": "多纹理渲染",
|
||||||
|
"position": 1,
|
||||||
|
"collapsed": true,
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "multi-render-intro"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 27 KiB |
@ -0,0 +1,112 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "了解如何手动进行多纹理合批。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 多纹理合批
|
||||||
|
|
||||||
|
使用 [多纹理材质](./multi-material.md) 文档中提到的 `MultiHandler` 的接口来动态设置材质的纹理插槽可能会比较麻烦,比如你需要使用一个纹理时,还得找到该纹理所在的材质并设置到渲染组件上。
|
||||||
|
|
||||||
|
为了能更方便地进行多纹理合批,社区版为组件增加了自动切换多纹理材质的机制,并且封装了一个多纹理合批管理类 `cc.sp.MultiBatcher`。
|
||||||
|
|
||||||
|
动态图集与字符图集使用的是全局多纹理合批管理器实例,可以通过 `cc.sp.multiBatcher` 访问。
|
||||||
|
|
||||||
|
## 开关自动切换多纹理材质
|
||||||
|
|
||||||
|
可以通过全局开关来控制所有组件的默认值:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.sp.autoSwitchMaterial = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
默认情况下组件会使用全局值,你可以控制单个组件是否强制启用/禁用该机制:
|
||||||
|
|
||||||
|
![autoswitchsettings](./assets/autoswitch-settings.png)
|
||||||
|
|
||||||
|
除了在编辑器调整,也可以通过代码控制:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// cc.RenderComponent.EnableType
|
||||||
|
// GLOBAL: 全局默认值
|
||||||
|
// ENABLE: 开启
|
||||||
|
// DISABLE: 关闭
|
||||||
|
sprite.autoSwitchMaterial = cc.RenderComponent.EnableType.ENABLE;
|
||||||
|
```
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
组件有脏检查标记,如果修改全局开关或者修改纹理关联的材质,需要对所有使用该纹理的渲染组件调用 `comp.setVertsDirty()` 重新检查。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::caution 特别注意
|
||||||
|
|
||||||
|
如果 Spine 组件所使用的 `SkeletonData` 同时使用了多个纹理,那么只会遍历数据以找到的第一个纹理为主执行自动切换机制。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 设置纹理的关联材质
|
||||||
|
|
||||||
|
我们为纹理增加了一个关联材质的概念,在支持的组件内对纹理进行渲染时,会提前切换为该纹理关联的材质。
|
||||||
|
|
||||||
|
:::info
|
||||||
|
|
||||||
|
每个纹理只能关联一个材质,如果同一个纹理,不同的渲染组件需要使用不同材质就需要手动设置。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
关联材质的接口有两种用法:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const bool = texture.linkMaterial(material);
|
||||||
|
const bool = texture.linkMaterial(material, index);
|
||||||
|
```
|
||||||
|
|
||||||
|
第一句代码会自动将纹理设置到材质的空插槽中,然后将该材质设置为该纹理的关联材质,如果没有空插槽会返回 `false`。
|
||||||
|
|
||||||
|
第二句则是强制将纹理设置到指定的插槽中,并将该材质设置为该纹理的关联材质。
|
||||||
|
|
||||||
|
想要解除两者的关联可以使用:
|
||||||
|
|
||||||
|
```js
|
||||||
|
texture.unlinkMaterial();
|
||||||
|
```
|
||||||
|
|
||||||
|
获取关联的材质可以使用:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const material = texture.getLinkedMaterial();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 多纹理合批管理器
|
||||||
|
|
||||||
|
手动关联材质就意味着你需要手动创建并管理创建的所有材质,如果是大量纹理需要关联材质就会比较麻烦。
|
||||||
|
|
||||||
|
所以我们封装了一个小巧的多纹理合批管理器 `cc.sp.MultiBatcher`。
|
||||||
|
|
||||||
|
这个管理器有点像动态合图管理器,它会持有一个材质数组,初始化后会使用内置的多纹理 Effect 着色器创建一个材质并放在数组中。
|
||||||
|
|
||||||
|
你可以传给管理器一个纹理,它会查找所有材质的空插槽,如果没有材质有空插槽则会创建一个新材质,然后把纹理与材质关联。
|
||||||
|
|
||||||
|
### 如何使用
|
||||||
|
|
||||||
|
创建管理器并初始化可以使用:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const batcher = new cc.sp.MultiBatcher();
|
||||||
|
batcher.init();
|
||||||
|
```
|
||||||
|
|
||||||
|
传入纹理可以使用:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const material = batcher.requsetMaterial(texture);
|
||||||
|
```
|
||||||
|
|
||||||
|
会返回关联的材质,如果纹理本来就已经有关联的材质,则会直接返回已关联的材质。
|
||||||
|
|
||||||
|
清空内部数组可以使用(这不会取消纹理的关联):
|
||||||
|
|
||||||
|
```js
|
||||||
|
batcher.reset();
|
||||||
|
```
|
@ -0,0 +1,137 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
description: "了解如何手动管理多纹理材质。"
|
||||||
|
toc_max_heading_level: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
# 多纹理材质
|
||||||
|
|
||||||
|
社区版新增了一个用于多纹理渲染的内置着色器资源,你可以直接使用该材质。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 创建多纹理材质
|
||||||
|
|
||||||
|
创建一个材质,Effect 选择内置的多纹理 Effect 着色器 `multi-2d-universal`。
|
||||||
|
|
||||||
|
![material-settings](./assets/material-settings.png)
|
||||||
|
|
||||||
|
勾选 `USE_MULTI_TEXTURE` 后可以看到上面有 `texture` - `texture8` 一共 8 个纹理插槽,将需要使用的纹理拖到上面的插槽即可完成多纹理材质的配置。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 在组件中使用多纹理材质
|
||||||
|
|
||||||
|
直接拖到组件的 `Materials` 属性上即可。
|
||||||
|
|
||||||
|
![material-comp](./assets/material-comp.png)
|
||||||
|
|
||||||
|
注意上图中已经在 `cc.Sprite` 组件拖入了刚刚创建的多纹理材质,并且其 `SpriteFrame` 属性设置的是材质中 `texture3` 插槽中的纹理。
|
||||||
|
|
||||||
|
![material-final](./assets/material-final.png)
|
||||||
|
|
||||||
|
你不需要指定组件要使用的纹理插槽 id,组件内部在渲染前会自动查找纹理在材质中的 id。
|
||||||
|
|
||||||
|
:::caution 警告
|
||||||
|
|
||||||
|
如果组件使用的纹理在材质中找不到,为了保证正常渲染,会在组件的材质变体中将纹理设置到 `texture` 插槽中。
|
||||||
|
|
||||||
|
这会导致这个组件的多纹理材质变体之后都不能按预期进行合批,就像 “退化” 成了普通材质。
|
||||||
|
|
||||||
|
所以使用时必须要确保纹理在材质中,假如材质变体已经 “退化” 了,那你可以通过重新设置材质的方式使组件持有一个新的材质变体。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::caution 特别注意
|
||||||
|
|
||||||
|
Spine 组件使用多纹理材质时会强制勾选 `enableBatch`,因为不开启就不能合批,那也就没必要使用多纹理材质。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
## 自定义多纹理材质
|
||||||
|
|
||||||
|
上面介绍的多纹理材质都是使用的内置的多纹理 Effect 着色器,你可以直接在内置多纹理 Effect 着色器的基础上修改。
|
||||||
|
|
||||||
|
除了直接在内置着色器的基础上修改之外,任何着色器中如果存在一个宏 `USE_MULTI_TEXTURE = true`,则会被认为是多纹理材质。
|
||||||
|
|
||||||
|
演示项目中有自定义材质的示范代码可以参考。
|
||||||
|
|
||||||
|
:::tip 提示
|
||||||
|
|
||||||
|
是否为多纹理材质的判断逻辑流程:
|
||||||
|
|
||||||
|
1. 获取材质当前使用的 Technique 中的第一个 Pass
|
||||||
|
2. 判断这个 Pass 是否 `USE_MULTI_TEXTURE = true`
|
||||||
|
3. 是的话,这个材质即为多纹理材质
|
||||||
|
|
||||||
|
在某些情况下通过代码修改材质可能需要调用 `material.updateMultiSupport()` 来触发这个流程。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
## 通过代码设置纹理插槽
|
||||||
|
|
||||||
|
每个多纹理材质都对应着一个多纹理材质管理器,这是社区版新增的一个工具类,其主要用处是便捷、高性能地管理多纹理材质上面的纹理插槽。
|
||||||
|
|
||||||
|
通过 `material.getMultiHandler()` 可以获取到管理器实例,请使用这个实例来操作多纹理材质的纹理插槽。
|
||||||
|
|
||||||
|
比如:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 获取管理器实例
|
||||||
|
const handler = material.getMultiHandler();
|
||||||
|
|
||||||
|
// 设置 `texture` 纹理插槽
|
||||||
|
handler.setTexture(0, texture);
|
||||||
|
|
||||||
|
// 置空 `texture2` 纹理插槽
|
||||||
|
handler.setTexture(1, null);
|
||||||
|
|
||||||
|
// 直接移除指定纹理
|
||||||
|
handler.removeTexture(texture.getImpl());
|
||||||
|
|
||||||
|
// 将纹理自动设置到材质的空插槽
|
||||||
|
handler.autoSetTexture(texture);
|
||||||
|
```
|
||||||
|
|
||||||
|
从上面的代码中可以看出操作纹理插槽的时候并不是传入插槽的名称,而是需要提供下标。
|
||||||
|
|
||||||
|
下标 `0` - `7` 分别对应着名称为 `texture` - `texture8` 的插槽。
|
||||||
|
|
||||||
|
可以使用这两个函数进行转换:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// index to name
|
||||||
|
cc.sp.propertyIndex2Name(0); // return: "texture"
|
||||||
|
|
||||||
|
// name to index
|
||||||
|
cc.sp.propertyName2Index("texture"); // return: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
注意区分材质 `cc.Material` 与材质变体 `cc.MaterialVariant`。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::caution 警告
|
||||||
|
|
||||||
|
请勿直接通过材质原始的 `setProperty` 接口修改多纹理材质的纹理插槽。
|
||||||
|
|
||||||
|
如果你必须这么做,需要调用 `material.getMultiHandler().syncTextures()` 来同步插槽数据到 `MultiHandler` 上。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
## 强制设置材质的类型
|
||||||
|
|
||||||
|
如果你想将某个材质强制视为多纹理材质或非多纹理材质,可以:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 视为多纹理材质
|
||||||
|
material.setMultiSupport(true);
|
||||||
|
|
||||||
|
// 视为非多纹理材质
|
||||||
|
material.setMultiSupport(false);
|
||||||
|
```
|
||||||
|
|
||||||
|
没有特殊情况不需要这么做。
|
@ -0,0 +1,50 @@
|
|||||||
|
import DocCardList from '@theme/DocCardList';
|
||||||
|
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
# 多纹理渲染
|
||||||
|
|
||||||
|
:::tip 提示
|
||||||
|
|
||||||
|
多纹理渲染属于底层设施,若你不准备手动使用多纹理材质或者多纹理合批管理器的话,请跳过本特性文档。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 什么是多纹理渲染?
|
||||||
|
|
||||||
|
在以前的认识里,我们知道相邻的节点使用不同的纹理(Texture)会导致不能合并批次。
|
||||||
|
|
||||||
|
其根本原因是纹理是使用 uniform 变量传给着色器的,而需要合并批次的话不允许每次渲染都拥有不同的 uniform 变量值。
|
||||||
|
|
||||||
|
社区版现在的实现是先设置多个 uniform 变量,比如将 8 张纹理写入到 "texture1" "texture2" "texture3"... 的 8 个 uniform 变量中,然后在着色器里再判断应该在渲染时使用哪个 uniform 变量。
|
||||||
|
|
||||||
|
这样的话如果所有渲染都只用这 8 张纹理,就都能合并为 1 个批次。
|
||||||
|
|
||||||
|
这要求设备支持采样多个纹理,而在现代绝大多数设备中都至少支持采样 8 张纹理,所以这不是问题。
|
||||||
|
|
||||||
|
当然除了这种方法,还有另外几种进行多纹理合批的方法,例如 "Texture Array" 和 "Bindless",但都有实用性与兼容性的问题。
|
||||||
|
|
||||||
|
:::info 那么,代价是什么?
|
||||||
|
|
||||||
|
因为会多传递一个顶点属性,并且需要在着色器中去判断该使用哪个纹理,导致**合并批次并不一定会提升性能**。
|
||||||
|
|
||||||
|
所以我们建议在多个档次设备中实际测试项目是否使用多纹理渲染的性能差距。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
为了让多纹理渲染能以最简单的方式在引擎中使用,社区版已经在内部对其做好了一些封装:
|
||||||
|
|
||||||
|
<DocCardList items={useCurrentSidebarCategory().items}/>
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
- **支持的渲染组件**
|
||||||
|
|
||||||
|
cc.Sprite、cc.Label、cc.RichText、cc.MotionSteak、Spine 组件。
|
||||||
|
|
||||||
|
- **不支持的渲染组件**
|
||||||
|
|
||||||
|
cc.ParticleSystem、TiledMap 组件:这两个组件当前的引擎实现会强制打断合批,暂时不支持。
|
||||||
|
|
||||||
|
DragonBones 组件:因人力有限,并且这个组件与 Spine 组件可以相互代替,所以暂时不支持该组件。
|
||||||
|
|
||||||
|
:::
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"label": "多线程支持",
|
||||||
|
"position": 6,
|
||||||
|
"collapsed": true,
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "thread-intro"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 220 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 265 KiB |
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
description: "在多线程中执行资源管线。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 资源管线
|
||||||
|
|
||||||
|
依次点击编辑器的菜单项 **项目 - 社区版设置**,然后勾选 **多线程驱动资源管线**,即可启用这一特性。
|
||||||
|
|
||||||
|
启用后,资源的下载和缓存部分都会在 Worker 线程中执行,完全释放对主线程的占用。
|
||||||
|
|
||||||
|
![analysis](./assets/tap-a.png)
|
||||||
|
|
||||||
|
![analysis-2](./assets/tap-a2.png)
|
||||||
|
|
||||||
|
这是在 Android 设备上,对游戏帧耗时的分析图,可以看到红框部分的消耗消失了,降低了每帧的耗时。
|
||||||
|
|
||||||
|
在启用资源管线的多线程支持后,会有一些接口差异,请前往 [破坏性变更](../../breaking-change#资源管线) 查看详情。
|
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "在多线程中操作音频。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 音频系统
|
||||||
|
|
||||||
|
依次点击编辑器的菜单项 **项目 - 社区版设置**,然后勾选 **多线程驱动音频系统**,即可启用这一特性。
|
||||||
|
|
||||||
|
启用后,针对音频的所有操作都会在 Worker 线程中执行,完全释放对主线程的占用。
|
||||||
|
|
||||||
|
下面是在 Android 设备上,在开启前对游戏帧耗时的分析图:
|
||||||
|
|
||||||
|
![analysis](./assets/tas-a.png)
|
||||||
|
|
||||||
|
下面是开启多线程支持后:
|
||||||
|
|
||||||
|
![analysis-2](./assets/tas-a2.png)
|
||||||
|
|
||||||
|
可以看到每次播放音频的耗时从 7.5ms 降低至 0.6ms。
|
||||||
|
|
||||||
|
## 调整属性同步间隔
|
||||||
|
|
||||||
|
启用多线程支持后,音频实例运行在 Worker 线程中,所以音频属性是定时同步更新到主线程的。
|
||||||
|
|
||||||
|
默认情况下,间隔时间为 `500` 毫秒,其实大部分项目都不会读取音频属性,而是直接监听播放开始、播放结束等音频事件(无论如何,事件是立即发出的)。
|
||||||
|
|
||||||
|
所以我们可以适当地降低同步频率,优化项目的性能。
|
||||||
|
|
||||||
|
依次点击编辑器的菜单项 **项目 - 社区版设置**,然后修改 **属性同步间隔** 的值即可。
|
||||||
|
|
||||||
|
在启用音频系统的多线程支持后,会有一些接口差异,请前往 [破坏性变更](../../breaking-change#音频系统) 查看详情。
|
@ -0,0 +1,254 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 5
|
||||||
|
description: "轻松地将项目逻辑多线程化。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 自定义多线程扩展
|
||||||
|
|
||||||
|
在之前,如果想为项目编写任何的多线程代码,因为 Worker 本身的易用性差,加上平台之间实现的差异,都会非常麻烦。
|
||||||
|
|
||||||
|
为此,社区版新增了自定义多线程扩展的支持,以简化多线程代码的编写,下面以计算斐波那契数列函数为例演示如何轻松编写多线程代码。
|
||||||
|
|
||||||
|
## 启用多线程扩展
|
||||||
|
|
||||||
|
依次点击编辑器的菜单项 **项目 - 社区版设置**,然后勾选 **项目多线程扩展** 即可。
|
||||||
|
|
||||||
|
## 创建多线程扩展
|
||||||
|
|
||||||
|
依次点击编辑器的菜单项 **扩展 - 创建新扩展插件... - 项目多线程扩展**。
|
||||||
|
|
||||||
|
这会在项目内的 worker 目录中以默认模板创建一个多线程扩展。
|
||||||
|
|
||||||
|
![custom worker struct](./assets/custom_worker_struct.png)
|
||||||
|
|
||||||
|
- `src` 多线程源码目录
|
||||||
|
- `index.js` 入口脚本
|
||||||
|
- `math.js` 含有 `add` 加法函数的示范脚本
|
||||||
|
- `creator-worker.d.ts` 提供代码类型提示
|
||||||
|
- `jsconfig.json` JavaScript 语言服务器配置
|
||||||
|
|
||||||
|
## 初识多线程架构
|
||||||
|
|
||||||
|
在编写多线程代码时,你需要时刻清楚**多线程扩展内的脚本会在 Worker 线程中执行**,所以不能直接导入项目内的脚本文件。
|
||||||
|
|
||||||
|
以下为 `math.js` 的内容,虽然只有几句代码,但这就已经是一个多线程函数的完整实现!
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { registerHandler } = require("ipc-worker.js");
|
||||||
|
|
||||||
|
export function add(x, y, callback) {
|
||||||
|
callback(x + y);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerHandler("math", {
|
||||||
|
add,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
你已经可以在项目中直接调用这个函数:
|
||||||
|
|
||||||
|
```js
|
||||||
|
worker.math.add([1, 2], ([v]) => {
|
||||||
|
console.log('Worker math add result:', v);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
这个函数将在 Worker 线程内执行,而不会阻塞主线程。
|
||||||
|
|
||||||
|
让我们来编写一个在 Worker 线程中计算斐波那契数列的函数,来深入了解多线程代码的编写!
|
||||||
|
|
||||||
|
## 编写多线程脚本
|
||||||
|
|
||||||
|
### 创建脚本
|
||||||
|
|
||||||
|
我们先在 `src` 目录创建一个 `fibonacci.js` 脚本。
|
||||||
|
|
||||||
|
然后在 `index.js` 添加新的一行来导入它:
|
||||||
|
|
||||||
|
```js
|
||||||
|
require("fibonacci.js");
|
||||||
|
```
|
||||||
|
|
||||||
|
这样引擎在创建 Worker 时才会执行这个新脚本。
|
||||||
|
|
||||||
|
### 编写函数
|
||||||
|
|
||||||
|
在 `fibonacci.js` 脚本中实现计算斐波那契的函数:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function _fibonacci(n) {
|
||||||
|
if (n <= 0) return 0;
|
||||||
|
if (n === 1) return 1;
|
||||||
|
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 导出函数到主线程
|
||||||
|
|
||||||
|
现在,虽然在 Worker 线程中我们有了这个函数,但是我们无法在主线程调用它。
|
||||||
|
|
||||||
|
与 Worker 线程的通信通常使用 `postMessage` 和 `onMessage` 进行,但是需要处理很多边缘情况,而且这样的开发体验也较差,所以社区版提供了一个封装。
|
||||||
|
|
||||||
|
我们需要导入 `registerHandler` 函数:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { registerHandler } = require("ipc-worker.js");
|
||||||
|
```
|
||||||
|
|
||||||
|
该函数的签名是:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export function registerHandler(name: string, handler: object): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
调用函数时,函数会执行以下操作:
|
||||||
|
|
||||||
|
- 在全局变量 `worker` 的对象上增加一个与传入 `name` 一样的对象属性。
|
||||||
|
- 遍历传入 `handler` 对象上的所有属性,按规则在 `worker.<name>` 对象上创建对应的函数。
|
||||||
|
|
||||||
|
也就是说,我们只需要将 `fibonacci` 函数传入到 `registerHandler` 函数并调用,函数就可以在主线程中调用了!
|
||||||
|
|
||||||
|
以下是完整的 `fibonacci.js` 内容:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { registerHandler } = require("ipc-worker.js");
|
||||||
|
|
||||||
|
function _fibonacci(n) {
|
||||||
|
if (n <= 0) return 0;
|
||||||
|
if (n === 1) return 1;
|
||||||
|
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fibonacci(n, callback) {
|
||||||
|
callback(_fibonacci(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
registerHandler("utils", {
|
||||||
|
fibonacci,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 导出的内部原理
|
||||||
|
|
||||||
|
你可能注意到了我们导出的是另一个函数,而不是直接导出 `_fibonacci` 函数。
|
||||||
|
|
||||||
|
因为这是实现一个跨线程调用函数时需要遵循的规范:
|
||||||
|
|
||||||
|
- 与 `postMessage` 的要求一样,函数的所有参数必须是可序列化的。
|
||||||
|
- 当函数被调用时,会在函数最后一个参数传入一个回调函数,当需要返回到主线程时,请调用该函数。
|
||||||
|
|
||||||
|
像上面 `fibonacci` 函数的实现,在调用 `_fibonacci` 拿到计算结果后,通过调用 `callback(v)` 将值返回到主线程。
|
||||||
|
|
||||||
|
而在主线程中,我们需要像这样从主线程调用 Worker 线程中的这个函数:
|
||||||
|
|
||||||
|
```js
|
||||||
|
worker.utils.fibonacci([10], ([v]) => {
|
||||||
|
console.log('Worker fibonacci result:', v);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
你可能注意到了,第一个参数是数组,而第二个参数的回调的第一个参数也是数组,这也是规范。
|
||||||
|
|
||||||
|
为了提高跨线程通信的性能,减少垃圾回收的频率,所以选择了这种调用的方式。
|
||||||
|
|
||||||
|
你可以这样理解 `worker.xxx.xxx()` 的调用签名:
|
||||||
|
|
||||||
|
```
|
||||||
|
worker.utils.fibonacci(args, (values) => ...);
|
||||||
|
|
||||||
|
// utils: 要调用的 handler 名称
|
||||||
|
// fibonacci: 要调用 handler 中的 Worker 函数名称
|
||||||
|
// args: 传入到 Worker 函数的所有参数
|
||||||
|
// values: Worker 函数返回时的回调,参数是返回值数组
|
||||||
|
```
|
||||||
|
|
||||||
|
这很好理解,我们再举个多参数调用的例子:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Worker 线程的函数
|
||||||
|
function handle(a, b, c, callback) {
|
||||||
|
// a = "ye.", b = {}, c = 1000
|
||||||
|
callback(1, "text", { prop: 2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主线程的调用方式
|
||||||
|
worker.utils.handle(["ye.", {}, 1000], ([v1, v2, v3]) => {
|
||||||
|
// v1 = 1, v2 = "text", v3 = { prop: 2 }
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 更多导出场景
|
||||||
|
|
||||||
|
无参数函数的实现与调用:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Worker 线程的函数
|
||||||
|
function setValue(callback) {
|
||||||
|
// ...
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主线程的调用方式
|
||||||
|
worker.utils.setValue(() => {
|
||||||
|
// ok.
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
无需返回的函数的实现与调用(这同时能节省 Worker 的通信开销,因为只需要单向通信!):
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Worker 线程的函数
|
||||||
|
function setValue(v) {
|
||||||
|
// ...
|
||||||
|
// 执行完成之后不调用 callback,甚至不用声明
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主线程的调用方式
|
||||||
|
worker.utils.setValue(["ye."]);
|
||||||
|
```
|
||||||
|
|
||||||
|
无参数也无返回的函数的实现与调用(这同时能节省 Worker 的通信开销,因为只需要单向通信!):
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Worker 线程的函数
|
||||||
|
function setValue() {
|
||||||
|
// ...
|
||||||
|
// 执行完成之后不调用 callback,甚至不用声明
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主线程的调用方式
|
||||||
|
worker.utils.setValue();
|
||||||
|
```
|
||||||
|
|
||||||
|
除了函数之外,你还可以导出值、getter/setter 属性,但需要注意需通过 `get_xxx`、`set_xxx` 和 `write_xxx` 三个代理函数进行访问与修改:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Worker 线程中:
|
||||||
|
|
||||||
|
registerHandler("Date", {
|
||||||
|
time: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 主线程中:
|
||||||
|
|
||||||
|
// 获取值
|
||||||
|
worker.Date.get_time(([v]) => {
|
||||||
|
// v is 1.
|
||||||
|
});
|
||||||
|
|
||||||
|
// 修改值,不会回调,性能比 write 更高
|
||||||
|
worker.Date.set_time([100]);
|
||||||
|
|
||||||
|
// 修改值,会回调以通知操作已执行完毕
|
||||||
|
worker.Date.write_time([100], () => {
|
||||||
|
// finish.
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 编译多线程扩展
|
||||||
|
|
||||||
|
每次修改扩展代码之后,需要手动点击 **项目 - 重新编译多线程扩展** 以生效。
|
||||||
|
|
||||||
|
特别注意:**就像修改多线程的设置会影响到所有项目一样,多线程扩展的编译结果也是所有项目共用的!**
|
||||||
|
|
||||||
|
所以当你**构建某个项目之前,必须确保最后一次编译是当前项目的多线程扩展**!
|
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
description: "在多线程中使用 XMLHttpRequest。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# XMLHttpRequest
|
||||||
|
|
||||||
|
依次点击编辑器的菜单项 **项目 - 社区版设置**,然后勾选 **多线程驱动 XMLHttpRequest**,即可启用这一特性。
|
||||||
|
|
||||||
|
启用后,有关于 XMLHttpRequest 的操作将会在 Worker 线程中执行,完全释放对主线程的占用。
|
||||||
|
|
||||||
|
下面是在 Android 设备上,在开启前对游戏帧耗时的分析图:
|
||||||
|
|
||||||
|
![alt text](./assets/th-a.png)
|
||||||
|
|
||||||
|
下面是开启多线程支持后:
|
||||||
|
|
||||||
|
![alt text](./assets/th-b.png)
|
||||||
|
|
||||||
|
可以看到每次发起网络请求的耗时从 15.2ms 降低至 0.5ms。
|
@ -0,0 +1,84 @@
|
|||||||
|
import DocCardList from '@theme/DocCardList';
|
||||||
|
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
# 多线程支持
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
本章节所有多线程特性暂时仅适用于微信小游戏平台。
|
||||||
|
|
||||||
|
并且在微信小游戏平台下还有以下改进:
|
||||||
|
|
||||||
|
- 默认启用网络接口和音频接口的高性能模式
|
||||||
|
- 网络接口支持 HTTP/2、HTTP/3(QUIC) 协议
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
社区版为引擎的部分系统增加了多线程支持,启用后可以释放其对主线程的占用,减少卡顿现象。
|
||||||
|
|
||||||
|
你可以在社区版的设置面板启用多线程支持:
|
||||||
|
|
||||||
|
![thread-settings](./assets/thread-settings.png)
|
||||||
|
|
||||||
|
## 使用代码调整设置
|
||||||
|
|
||||||
|
你可以使用 [构建模板](https://docs.cocos.com/creator/2.4/manual/zh/publish/custom-project-build-template.html) 在 `game.js` 的 `__globalAdapter.init();` 语句执行之前声明宏来调整设置:
|
||||||
|
|
||||||
|
- CC_WORKER_ASSET_PIPELINE(是否启用 Worker 驱动资源管线)
|
||||||
|
- CC_WORKER_AUDIO_SYSTEM(是否启用 Worker 驱动音频系统)
|
||||||
|
- CC_WORKER_DEBUG(是否启用 Worker 调试模式)
|
||||||
|
- CC_CUSTOM_WORKER(是否启用自定义 Worker)
|
||||||
|
- CC_WORKER_AUDIO_SYSTEM_SYNC_INTERVAL(Worker 音频系统同步音频属性的间隔时间(单位:毫秒))
|
||||||
|
- CC_WORKER_WEBSOCKET(是否启用 Worker 驱动 WebSocket)
|
||||||
|
- CC_WORKER_HTTP_REQUEST(是否启用 Worker 驱动 HTTP 请求)
|
||||||
|
|
||||||
|
例如这样:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// game.js
|
||||||
|
require('adapter-js-path');
|
||||||
|
// --- 在 init 执行之前设置 ---
|
||||||
|
globalThis.CC_WORKER_ASSET_PIPELINE = isAndroid;
|
||||||
|
// --------------------------
|
||||||
|
__globalAdapter.init();
|
||||||
|
```
|
||||||
|
|
||||||
|
当 `init` 执行之后,由于引擎已经初始化完毕,就不能再对设置进行修改了。
|
||||||
|
|
||||||
|
## 将 workers 代码目录设为子包
|
||||||
|
|
||||||
|
依次点击编辑器的菜单项 **项目 - 社区版设置**,然后勾选 **设为小游戏子包**,即可启用这一特性。
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
当你重启编辑器,或者启用/禁用多线程支持时,可能出现以下几种情况:
|
||||||
|
|
||||||
|
**禁用多线程支持时输出的包体大小警告:**
|
||||||
|
|
||||||
|
由于要实现多线程支持,社区版在包体内增加了一个 `workers` 目录,用于存放 Worker 线程的代码。
|
||||||
|
|
||||||
|
大小有 `30-50KB` 左右,当你禁用多线程时,这个目录可以被删除以减少包体。
|
||||||
|
|
||||||
|
具体流程如下:
|
||||||
|
|
||||||
|
- 点击编辑器界面右上角的 **编辑器** 按钮,并跳转到 `Resources/builtin/adapters/platforms/wechat/res` 目录。
|
||||||
|
- 删除该目录下的 `workers` 目录。
|
||||||
|
- 打开该目录下的 `game.json`,删除 `workers` 字段并保存。
|
||||||
|
|
||||||
|
**启用多线程支持时输出的检测错误:**
|
||||||
|
|
||||||
|
这是由于你按照上面的步骤删除了多线程所需的文件和配置,所以无法启用多线程支持,只需恢复或者重新安装完整的社区版即可。
|
||||||
|
|
||||||
|
:::tip 注意
|
||||||
|
|
||||||
|
**如果你正在使用付费扩展,则无需手动删除或重新安装,也不会输出任何警告或者错误。**
|
||||||
|
|
||||||
|
因为付费扩展会存储已安装版本的备份文件,所以能够自动删除或重新安装。
|
||||||
|
|
||||||
|
如果付费扩展依然输出错误则可能是文件损坏,重新一键安装即可。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
阅读下面的文档了解详情:
|
||||||
|
|
||||||
|
<DocCardList items={useCurrentSidebarCategory().items}/>
|
@ -0,0 +1,171 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 4
|
||||||
|
description: "在多线程中使用 WebSocket。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# WebSocket
|
||||||
|
|
||||||
|
依次点击编辑器的菜单项 **项目 - 社区版设置**,然后勾选 **多线程驱动 WebSocket**,即可启用这一特性。
|
||||||
|
|
||||||
|
启用后,有关于 WebSocket 的操作将会在 Worker 线程中执行,完全释放对主线程的占用。
|
||||||
|
|
||||||
|
并且,通过 [自定义多线程扩展](./thread-custom) 你还可以将项目本身的 WebSocket 数据解析操作转移至线程内执行,接下来我们会用一个例子来详细介绍。
|
||||||
|
|
||||||
|
下面是在 Android 设备上,在优化前对游戏帧耗时的分析图:
|
||||||
|
|
||||||
|
![alt text](./assets/th-a.png)
|
||||||
|
|
||||||
|
下面是优化后:
|
||||||
|
|
||||||
|
![alt text](./assets/th-b.png)
|
||||||
|
|
||||||
|
可以看到网络请求的耗时从 ms 降低至 ms。
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
需注意,不是任何情况下启用多线程支持都能得到性能提升,因为线程之间有通信成本,如果收发大量数据可能导致卡顿,请实际测试性能是否有提升!
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 自定义数据解析
|
||||||
|
|
||||||
|
接下来我们以一个使用 Protobuf + WebSocket 进行网络通信的游戏为例子来介绍如何将所有网络层逻辑都移至线程中执行。
|
||||||
|
|
||||||
|
我们首先启用 **多线程驱动 WebSocket**,这时候,项目无需任何改动,WebSocket 实际操作即已在线程中进行。
|
||||||
|
|
||||||
|
但是在发送数据到 WebSocket 前;或者从 WebSocket 接收到数据后,都需要首先使用 Protobuf 进行编解码,这部分的逻辑也应该移至线程中进行。
|
||||||
|
|
||||||
|
首先创建 **自定义多线程扩展**,然后我们可以新建一个 `ws-parser.js` 脚本文件,先编写下面的代码:
|
||||||
|
|
||||||
|
```js
|
||||||
|
globalThis.hookWSSend = function (data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.hookWSRecv = function (data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
|
||||||
|
不要忘记在扩展的 `index.js` 入口脚本中导入该文件
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
`hookWSSend` 和 `hookWSRecv` 是社区版增加的两个特殊接口。
|
||||||
|
|
||||||
|
WebSocket 在发送时会尝试调用 `hookWSSend` 函数,并传入即将发送的数据,并实际发送函数的返回值。
|
||||||
|
|
||||||
|
WebSocket 在接收时会尝试调用 `hookWSRecv` 函数,并传入收到的数据,并实际返回函数的返回值到主线程中。
|
||||||
|
|
||||||
|
有了这两个接口,我们可以很轻松地将数据解析移至线程中实现。
|
||||||
|
|
||||||
|
假设以下是主线程中的 `net.ts` 文件:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { protocol } from './proto';
|
||||||
|
|
||||||
|
export function login(obj) {
|
||||||
|
const buffer = protocol.LoginRequest.encode(obj);
|
||||||
|
webSocket.send(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onMessage(data) {
|
||||||
|
const obj = protocol.LoginRespone.decode(data);
|
||||||
|
console.log("login result:", obj);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
那么我们可以将解析移至刚刚的 `ws-parser.js` 文件,并注释掉原来的解析代码:
|
||||||
|
|
||||||
|
`ws-parser.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
const protocol = require("./proto");
|
||||||
|
|
||||||
|
globalThis.hookWSSend = function (data) {
|
||||||
|
return protocol.LoginRequest.encode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.hookWSRecv = function (data) {
|
||||||
|
return protocol.LoginRespone.decode(data);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`net.ts`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// import { protocol } from './proto';
|
||||||
|
|
||||||
|
export function login(obj) {
|
||||||
|
// 直接发送对象即可,会直接发送给 hookWSSend 函数进行编码
|
||||||
|
// const buffer = protocol.LoginRequest.encode(obj);
|
||||||
|
webSocket.send(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onMessage(data) {
|
||||||
|
// WebSocket onmessage 回调的参数即是 hookWSRecv 函数的返回值,所以可以直接使用
|
||||||
|
// const obj = protocol.LoginRespone.decode(data);
|
||||||
|
console.log("login result:", data);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这样我们就已经将数据解析的操作移至 Worker 线程中执行,彻底释放主线程了!
|
||||||
|
|
||||||
|
## 解决细节问题
|
||||||
|
|
||||||
|
当然,以上是情况简单,比较理想的伪代码,如果你也是使用 [protobufjs](https://www.npmjs.com/package/protobufjs) 库,实际上还有以下细节问题需要解决:
|
||||||
|
|
||||||
|
- 将 Protobuf 从 node_modules 中抽离
|
||||||
|
- 将 Protobuf 引用的 Long 库从 node_modules 中抽离
|
||||||
|
- 既要兼容不支持 Worker 的设备,也要避免加载两份代码
|
||||||
|
|
||||||
|
首先如果直接将从 proto 文件编译出来的 `.js` 文件放到 worker 目录中引用,会因为该文件引用 npm 库而报错。
|
||||||
|
|
||||||
|
所以每次编译出来的 `.js` 文件,我们需要将里面的
|
||||||
|
|
||||||
|
```js
|
||||||
|
var $protobuf = require("protobufjs/minimal");
|
||||||
|
```
|
||||||
|
|
||||||
|
修改为
|
||||||
|
|
||||||
|
```js
|
||||||
|
var $protobuf = require("./protobuf.js");
|
||||||
|
```
|
||||||
|
|
||||||
|
为了减少麻烦,可以写一个自动脚本进行编译并修改。
|
||||||
|
|
||||||
|
然后我们把项目中的 `node_modules/protobufjs/dist/minimal/protobuf.min.js` 文件复制一份到 worker 目录中,并重命名为 `protobuf.js`。
|
||||||
|
|
||||||
|
需在 `protobuf.js` 的首行插入:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export let protobuf;
|
||||||
|
```
|
||||||
|
|
||||||
|
然后在大段代码中查找 `"object"==typeof module&&module&&module.exports&&(module.exports=n)`,在末尾加上 `,protobuf=n`,这样 `protobuf.js` 就可以作为单独的脚本文件进行导入了。
|
||||||
|
|
||||||
|
为了不同时加载两份 Protobuf 库和协议文件,可以先将 workers 设为子包,方法是在设置面板开启 **设为小游戏子包**。
|
||||||
|
|
||||||
|
然后将 Protobuf 库移至项目的子包中,并修改项目的协议文件以引用 `protobuf.js` 而不是 npm 库。
|
||||||
|
|
||||||
|
你还可以使用宏来实现支持 Worker 时不在主线程加载 Protobuf 子包,不支持时则回退到原逻辑:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
if(cc.sys.platform === cc.sys.WECHAT_GAME && CC_WORKER_WEBSOCKET) {
|
||||||
|
webSocket.send(obj);
|
||||||
|
} else {
|
||||||
|
cc.assetManager.loadBundle("protobuf", async (err, bundle) => {
|
||||||
|
const { protocol } = await import("./proto");
|
||||||
|
webSocket.send(protocol.LoginRequest.encode(obj));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
最后,我们处理 long 库,这是 protobufjs 依赖的大数库。
|
||||||
|
|
||||||
|
所幸 long 库的编写比较现代化,我们可以直接将 `node_modules/long/index.js` 文件复制一份到 worker 目录中,并重命名为 `long.js`。
|
||||||
|
|
||||||
|
然后在 `protobuf.js` 文件中找到 `inquire("long")`,改为 `inquire("./long.js")`,即可完成修改正常导入。
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"label": "Spine",
|
||||||
|
"position": 4,
|
||||||
|
"collapsed": true,
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "spine-intro"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "像其它渲染组件一样在 Spine 组件上使用动态合图。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 动态合图
|
||||||
|
|
||||||
|
你可以像其它渲染组件一样在 Spine 组件上使用动态合图,如果想了解有关动态合图的更多详情,可以阅读 [动态合图](../dynamic-batcher/dynamic-batcher-intro) 文档。
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
由于引擎实现差异,在原生平台上 Spine 暂不支持与其它组件合批。
|
||||||
|
|
||||||
|
:::
|
@ -0,0 +1,14 @@
|
|||||||
|
import DocCardList from '@theme/DocCardList';
|
||||||
|
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
# Spine
|
||||||
|
|
||||||
|
社区版解决了 Spine 在之前的三大痛点:
|
||||||
|
|
||||||
|
- 不参与动态合图
|
||||||
|
- 无法与其它组件合批
|
||||||
|
- 不能使用 SpriteFrame 换装
|
||||||
|
|
||||||
|
你可以阅读下面的文档了解详情:
|
||||||
|
|
||||||
|
<DocCardList items={useCurrentSidebarCategory().items}/>
|
@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "随心所欲地更换 Spine 动画的部份纹理。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# SpriteFrame 换装
|
||||||
|
|
||||||
|
引擎提供了一个替换插槽的 attachment 对象进行换装的方法,可以在 [Spine 组件参考](https://docs.cocos.com/creator/2.4/manual/zh/components/spine.html) 官方文档进行了解。
|
||||||
|
|
||||||
|
但是使用替换插槽的 attachment 对象这种方法比较绕,并且 Spine 动画中有切换 attachment 的关键帧时这种方法就没用了。
|
||||||
|
|
||||||
|
而 attachment 对象持有一个 region 对象,这个对象类似引擎的 SpriteFrame,所以我们可以通过修改 region 对象的数据来进行换装。
|
||||||
|
|
||||||
|
---
|
||||||
|
## 认识 RegionData
|
||||||
|
|
||||||
|
请勿混淆 RegionData 与 region,RegionData 主要作为 SpriteFrame 和 region 对象之间的桥梁,实现两者的相互转换。
|
||||||
|
|
||||||
|
将 SpriteFrame 转换为 RegionData:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const regionData = new sp.RegionData(spriteFrame);
|
||||||
|
```
|
||||||
|
|
||||||
|
将 RegionData 转换为 SpriteFrame:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const spriteFrame = regionData.toSpriteFrame();
|
||||||
|
```
|
||||||
|
|
||||||
|
将 attachment 对象的 region 数据转换为 RegionData :
|
||||||
|
|
||||||
|
```js
|
||||||
|
const regionData = new sp.RegionData(attachment);
|
||||||
|
```
|
||||||
|
|
||||||
|
将 RegionData 数据更新到 attachment 对象上:
|
||||||
|
|
||||||
|
```js
|
||||||
|
regionData.assignToAttachment(attachment);
|
||||||
|
```
|
||||||
|
|
||||||
|
:::caution 注意
|
||||||
|
|
||||||
|
Spine 的 Region 支持 `0`、`90`、`180`、`270` 四种旋转角度,而 Cocos Creator 的 SpriteFrame 只支持 `0` 与 `270` 两种旋转角度,所以如果是 RegionData 转为 SpriteFrame 则可能导致方向不同的问题。
|
||||||
|
|
||||||
|
**要进行换装的话使用的是 SpriteFrame 转为 RegionData,所以不用担心这个问题。**
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
---
|
||||||
|
## 使用 SpriteFrame 修改 Region 数据
|
||||||
|
|
||||||
|
虽然使用上面的 RegionData 即可实现使用 SpriteFrame 换装的需求,但我们还在 Spine 组件上提供了两个更方便的接口:
|
||||||
|
|
||||||
|
只使用 `regionData.assignToAttachment(attachment)` 只会修改 SkeletonData 的数据,但不会触发 Spine 组件的渲染更新。
|
||||||
|
|
||||||
|
推荐直接使用:
|
||||||
|
|
||||||
|
```js
|
||||||
|
skeletonComponent.setRegionData('Head', 'Head', new sp.RegionData(spriteFrame));
|
||||||
|
```
|
||||||
|
|
||||||
|
在修改的同时刷新组件的渲染实现换装,并且不会打断当前正在播放的动画。
|
||||||
|
|
||||||
|
还提供了一个通过 attachment 名称获取 RegionData 的接口:
|
||||||
|
|
||||||
|
```js
|
||||||
|
a.getRegion(slotName, attachmentName);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
|
||||||
|
### 多实例问题
|
||||||
|
|
||||||
|
由于是直接修改 Spine 组件所使用 SkeletonData 的 attachment 数据,所以所有 Spine 组件都会受到影响。
|
||||||
|
|
||||||
|
如果你只想替换其中一个组件,那么就可以克隆这个 SkeletonData 让每个组件都使用不同的 SkeletonData 实例进行渲染。
|
||||||
|
|
||||||
|
社区版提供了一个克隆数据的接口来实现这个需求:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const clonedSkeletonData = skeletonData.clone();
|
||||||
|
```
|
||||||
|
|
||||||
|
使用以上代码克隆 SkeletonData 后再进行换装,赋值给 Spine 组件,那么替换操作就只会对这个 Spine 组件生效。
|
||||||
|
|
||||||
|
---
|
||||||
|
以上所有用法你可以在演示项目中找到示范代码。
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"label": "文本渲染",
|
||||||
|
"position": 2,
|
||||||
|
"collapsed": true,
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "text-render-intro"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 26 KiB |
@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
description: "详细了解该缓存模式重构后的所有新特性。"
|
||||||
|
---
|
||||||
|
# Char 缓存模式
|
||||||
|
|
||||||
|
在 [提升游戏性能](../../best-practices/performance-guide) 中,我们提到了社区版使 Bitmap 与 Char 缓存模式都支持了废弃字符空间复用的特性。
|
||||||
|
|
||||||
|
除此之外,Char 缓存模式还提供了一些可调整的设置。
|
||||||
|
|
||||||
|
## 在场景切换时清空所有字符图集
|
||||||
|
|
||||||
|
控制在场景切换时是否会清空所有的字符图集,考虑到旧项目兼容,默认为开启状态。
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.sp.charAtlasAutoResetBeforeSceneLoad = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
在引擎原来的设计中,该机制不可被关闭,现在推荐关闭该机制。
|
||||||
|
|
||||||
|
## 字符图集的数量与内置材质
|
||||||
|
|
||||||
|
现在在内部最多会创建 8 张字符图集,与多纹理材质的最大纹理插槽数一致,如果合理使用缓存模式,8 张应该对所有项目都是足够的。
|
||||||
|
|
||||||
|
Char 缓存模式会在内部维护一个使用内置多纹理 Effect 着色器的材质,Label 在渲染时会先判断当前所使用的是否为多纹理材质,是的话则判断是否能在材质纹理插槽中找到字符图集纹理。
|
||||||
|
|
||||||
|
若任何判断不满足则会将组件的材质设为内部维护的材质。
|
||||||
|
|
||||||
|
如果你有特殊的用途,可以通过以下代码获取:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.Label._shareAtlas.material;
|
||||||
|
```
|
||||||
|
|
||||||
|
需注意 Char 字符图集是运行时才创建的,所以现在**暂时无法在 Char 缓存模式下设置组件的自定义材质**。
|
||||||
|
|
||||||
|
要解决这个问题,你可以通过代码创建含有当前所有字符图集纹理的自定义材质来解决这个问题,但内部会不断创建新的字符图集(直到 8 张),所以需注意同步更新这个自定义材质,建议有自定义材质需求时使用 Bitmap 缓存模式。
|
||||||
|
|
||||||
|
## 与动态图集合批的注意事项
|
||||||
|
|
||||||
|
多纹理材质只有 8 个纹理插槽,默认情况下字符图集自动多纹理合批的数量为 1,也就是说会将第 1 张字符图集纹理放入材质。
|
||||||
|
|
||||||
|
**你可以自己调整这个分配值,但请注意调整的时机。**
|
||||||
|
|
||||||
|
下面举一个例子,假设有一个脚本 `example.ts` 的部分内容是:
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
class Example extends cc.Component {
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
// 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
一般情况下,代码位置 2 是当用户脚本被加载时就会被执行,而代码位置 1 可能需要等到引擎首场景加载后的某个时间执行。
|
||||||
|
|
||||||
|
社区版会自动调整动态图集的最大数量,这个调整的时机是在代码位置 2 之后的,所以比如你的项目对 Char 缓存模式使用量比较大时,想尝试将动态图集最大数量调整为 6,自动合批的字符图集数量调整为 2,那么你只需要在代码位置 2 修改字符图集自动多纹理合批的数量:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.sp.charAtlasAutoBatchCount = 2;
|
||||||
|
```
|
||||||
|
|
||||||
|
之后社区版会自动将动态图集的最大数量调整为 `8 - 2`,即 6。
|
||||||
|
|
||||||
|
这个自动调整的时机并不意味着你在代码位置 2 修改动态图集的最大数量是无效的,因为一开始动态图集的最大数量为 `-1`,你打印一下可以看到
|
||||||
|
|
||||||
|
```js
|
||||||
|
console.log(cc.dynamicAtlasManager.maxAtlasCount); // -1
|
||||||
|
```
|
||||||
|
|
||||||
|
如果你在代码位置 2 修改了动态图集的最大数量,社区版就不会调整该值了。
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.maxAtlasCount = 5;
|
||||||
|
```
|
||||||
|
|
||||||
|
这时候动态图集的最大数量是 5,字符图集自动多纹理合批的数量依旧默认为 1,多纹理材质会有 2 个空纹理插槽。
|
||||||
|
|
||||||
|
如果这两个数量加起来超过 8 就会使用更多的材质进行渲染,这会导致项目的 Draw Call 数量升高,建议保持加起来的数量不超过 8 张,能保持 1 Draw Call。
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.dynamicAtlasManager.maxAtlasCount = 13;
|
||||||
|
cc.sp.charAtlasAutoBatchCount = 3;
|
||||||
|
```
|
||||||
|
|
||||||
|
比如上面这个设置,这会使得引擎需要用 2 个材质进行渲染,但是可用的动态图集扩充到了 13 张,Char 能自动合批的图集数量扩充到了 3 张,对于某些项目来说可能并不是一件坏事。
|
||||||
|
|
||||||
|
社区版使用这个 “7 + 1” 的默认值有以下几点原因:
|
||||||
|
|
||||||
|
- 引擎原本就只有 1 张 Char 字符图集
|
||||||
|
- 大多数项目使用 1 张 Char 字符图集是足够的
|
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "一行代码开启高清文本渲染。"
|
||||||
|
---
|
||||||
|
# 高 DPI 支持
|
||||||
|
|
||||||
|
可阅读 [提升游戏质量](../../best-practices/quality-guide) 了解基本的使用方法。
|
||||||
|
|
||||||
|
## 调整全局开关
|
||||||
|
|
||||||
|
使用下面的代码控制所有组件是否默认开启高 DPI 支持:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.sp.enableLabelRetina = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 调整渲染缩放比例
|
||||||
|
|
||||||
|
使用下面的代码调整内部渲染的缩放倍数:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cc.sp.labelRetinaScale = 2;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 控制单个组件开关
|
||||||
|
|
||||||
|
![reinasettings](./assets/reina-settings.png)
|
||||||
|
|
||||||
|
除了在编辑器调整,也可以通过代码控制:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// cc.RenderComponent.EnableType
|
||||||
|
// GLOBAL: 全局默认值
|
||||||
|
// ENABLE: 开启
|
||||||
|
// DISABLE: 关闭
|
||||||
|
label.enableRetina = cc.RenderComponent.EnableType.ENABLE;
|
||||||
|
```
|
@ -0,0 +1,8 @@
|
|||||||
|
import DocCardList from '@theme/DocCardList';
|
||||||
|
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
# 文本渲染
|
||||||
|
|
||||||
|
文本渲染一般是游戏性能优化需要重点关注的地方,并且其显示效果也非常重要,所以社区版提供了以下新特性:
|
||||||
|
|
||||||
|
<DocCardList items={useCurrentSidebarCategory().items}/>
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
description: "就像在其它组件里一样使用自定义材质。"
|
||||||
|
---
|
||||||
|
# RichText 自定义材质
|
||||||
|
|
||||||
|
![richtextsettings](./assets/richtext-settings.png)
|
||||||
|
|
||||||
|
像往常一样使用即可。
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"label": "TiledMap",
|
||||||
|
"position": 5,
|
||||||
|
"collapsed": true,
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "tiledmap-intro"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 8.5 KiB |
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
description: "像其它渲染组件一样在 Spine 组件上使用动态合图。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 复用 Culling 数据
|
||||||
|
|
||||||
|
一个 TiledMap 可能会有很多 TiledLayer,如果开启了 Culling,那这些 Layer 都需要单独计算 Culling 数据,社区版新增了在满足一定条件的情况下可以复用 Culling 数据的特性,以减少 CPU 的性能消耗。
|
||||||
|
|
||||||
|
## 大致原理
|
||||||
|
|
||||||
|
通过为 TiledLayer 指定另一个 TiledLayer,来复用指定的 TiledLayer 的 Culling 数据。
|
||||||
|
|
||||||
|
## 前提条件
|
||||||
|
|
||||||
|
- 两者的世界矩阵(也是大小、位置、缩放等属性)一致
|
||||||
|
- 两者所用的 Camera 一致
|
||||||
|
- 两者的 TiledLayer 类型、属性一致
|
||||||
|
- 确保被复用的 Layer 会更早进行渲染(否则 Culling 数据同步可能会延迟一帧)
|
||||||
|
|
||||||
|
:::tip 注意
|
||||||
|
|
||||||
|
复用可以是不同 TiledMap 的 TiledLayer,但一定要满足上面几个条件,否则 Culling 结果可能不正确。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
假设项目中有 TiledMap 由 5 个 TiledLayer 组合成一张地图。
|
||||||
|
|
||||||
|
![](./assets/tiledmap-nodetree.png)
|
||||||
|
|
||||||
|
这种情况如果需要复用 Culling 数据只需要多选后四个节点,然后将第一个节点拖入这四个节点的 `Culling Layer` 属性中即可。
|
||||||
|
|
||||||
|
![](./assets/tiledmap-inspector.png)
|
||||||
|
|
||||||
|
这样就使后四个 Layer 复用第一个 Layer 的 Culling 数据了。
|
@ -0,0 +1,12 @@
|
|||||||
|
import DocCardList from '@theme/DocCardList';
|
||||||
|
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
# TiledMap
|
||||||
|
|
||||||
|
社区版对 TiledMap 进行了优化:
|
||||||
|
|
||||||
|
- 复用 TiledLayer 的 Culling 数据
|
||||||
|
|
||||||
|
你可以阅读下面的文档了解详情:
|
||||||
|
|
||||||
|
<DocCardList items={useCurrentSidebarCategory().items}/>
|
8
docs/versioned_sidebars/version-2.4.0-sidebars.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"tutorialSidebar": [
|
||||||
|
{
|
||||||
|
"type": "autogenerated",
|
||||||
|
"dirName": "."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
[
|
[
|
||||||
|
"2.4.0",
|
||||||
"2.3.0",
|
"2.3.0",
|
||||||
"2.2.0",
|
"2.2.0",
|
||||||
"2.1.0",
|
"2.1.0",
|
||||||
|