关于 optimize-scene
optimize-scene 是面向 Decentraland 场景开发者的性能优化技能,汇总场景限制并提供实体、模型、三角形和纹理优化方法。它也介绍移动端预算及如何依据性能数据选择优化方向。
主要功能
- 01查看按地块数量变化的场景限制
- 02说明移动端软性与硬性预算
- 03通过复用和合并优化重复模型
- 04使用 LOD 与低面数资源
- 05优化纹理和 Blender 模型资源
适用场景
- 01检查场景是否接近资源限制
- 02为移动客户端减少场景负载
- 03处理重复道具造成的渲染开销
- 04压缩纹理并精简模型几何
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ decentraland/ sdk-skills --skill optimize-scene安装后在 Claude Code 中输入/optimize-scene确认已加载。
详细介绍
优化 Decentraland 场景
场景限制(按地块数量)
以下所有限制都随地块数量 n 变化。三角形、实体和物理体按线性比例增长。材质和纹理按对数比例增长。高度限制不会随之变化——每个场景的上限均为 330 m(见下文)。
| 资源 | 公式 | 1 个地块 | 2 个地块 | 3 个地块 | 4 个地块 | 6 个地块 | 9 个地块 | 16 个地块 | 20 个地块 |
|---|---|---|---|---|---|---|---|---|---|
| 三角形 | n x 10,000 | 10,000 | 20,000 | 30,000 | 40,000 | 60,000 | 90,000 | 160,000 | 200,000 |
| 实体 | n x 200 | 200 | 400 | 600 | 800 | 1,200 | 1,800 | 3,200 | 4,000 |
| 物理体 | n x 300 | 300 | 600 | 900 | 1,200 | 1,800 | 2,700 | 4,800 | 6,000 |
| 材质 | log2(n+1) x 20 | 20 | 31 | 40 | 46 | 56 | 66 | 81 | 87 |
| 纹理 | log2(n+1) x 10 | 10 | 15 | 20 | 23 | 28 | 33 | 40 | 43 |
所有场景的高度限制均为 330 m,与地块数量无关。 世界坐标位置超过此高度的实体会被隐藏。⚠️ 高于约 200 m 的内容可能会出现多人游戏同步问题——请将与游戏玩法相关的内容和联网内容保持在该高度以下。
请谨慎解读“材质”这一行。 客户端会为每个渲染对象实例化一个材质,因此这个数字反映的是场景渲染的对象数量,而不是场景创建的不同材质数量——正确地重复使用同一个模型的场景仍会符合此上限。应将其视为内存指标,并通过 shaderVariants 判断帧时间风险(见下文的 重复模型)。
文件限制: 每个地块 15 MB,总计最多 300 MB,每个地块 200 个文件,单个文件最大 50 MB。
文件限制只统计部署时实际上传的内容。请确保项目根目录下的 .dclignore 排除所有工作文件——Blender/FBX 源文件、草稿模型、概念艺术、电子表格、Markdown 文档——因为这些文件通常占项目的大部分空间,且运行时并不需要。请参阅 deploy-scene 技能中的 .dclignore 部分。
重要提示:除 MB 大小限制外,其他限制均可超出。由于会影响性能,通常不建议超出这些限制;但如果用户测试了自己的场景并确认效果足够好,就可以发布。
移动端场景限制
场景在移动客户端上运行时,适用一组独立的固定限制,不会随地块数量变化。软性限制会在移动端性能面板中发出警告;硬性限制会阻止场景加载。已根据文档提交 8139a7f(2026 年 10 月)验证。
| 指标 | 软性限制 | 硬性限制 |
|---|---|---|
| 三角形 | 1,000,000 | 1,200,000 |
| 实体 | 4,800 | 6,000 |
| 网格(实体) | 2,400 | 3,000 |
| 几何体 | 1,000 | 2,000 |
| 材质 | 400 | 500 |
| 纹理 | 400 | 500 |
| 碰撞体 | 1,200 | 1,500 |
| VFX 发射器 | 8 | 10 |
| 活动粒子 | 25,000 | 50,000 |
| 光源 | 8 | 16 |
| 投射阴影的灯光 | 2 | 4 |
| 内容大小 | 120 MB | 150 MB |
| 外部内容 | 40 MB | 50 MB |
| 内存(进程 RSS) | 1,638 MB | 2,048 MB |
| 绘制调用 | 1,000 | 2,000 |
| 性能(越高越好) | 90% | 85% |
灯光和粒子按创作的数量计数,而非引擎实际运行的数量。
- 每种移动端图形配置只会启用一定预算内的最近光源——从“非常低”配置下的 0 个到“高”配置下的 8 个——并且可能完全禁用阴影。即使最多只有 8 盏灯实际发光,创作 20 盏灯仍会承担完整的场景开销。请将创作数量控制在 8/16 的限制内;不要依赖运行时预算来降低过度照明场景的成本。参见 lighting-environment。
- 粒子还会在运行时受到限制:每个发射器最多 5,000 个,每个场景最多 50,000 个。超过这些上限的创作数量会被静默削减,因此超出上限只会浪费场景预算,不会带来视觉收益。参见 particle-system。
无需设备即可测量:Creator Hub > Preview > Show QR Code for Mobile,然后在手机上打开 Scene Limits Preview(右上角带红色统计信息的显示器图标)。在中端设备的 High 配置下,将**性能 > 90%**作为目标。
实体数量优化
重用实体
仅在场景需要动态生成和移除实例时使用此模式。
// BAD: Creating new entity each time
function spawnBullet() {
const bullet = engine.addEntity() // Creates entity every call
// ...
}
// GOOD: Object pooling
const bulletPool: Entity[] = []
function getBullet(): Entity {
const existing = bulletPool.find((e) => !ActiveBullet.has(e))
if (existing) return existing
const newBullet = engine.addEntity()
bulletPool.push(newBullet)
return newBullet
}
移除未使用的实体
const removed = engine.removeEntity(entity) // returns boolean
// true: components purged, id released for reuse
// false: entity is renderer-reserved (avatar range) — components untouched
使用父子层级
不要为每个子实体单独设置变换值,而是使用实体层级:
const parent = engine.addEntity()
Transform.create(parent, { position: Vector3.create(8, 0, 8) })
// Children inherit parent transform
const child1 = engine.addEntity()
Transform.create(child1, { position: Vector3.create(0, 1, 0), parent })
const child2 = engine.addEntity()
Transform.create(child2, { position: Vector3.create(1, 1, 0), parent })
重复模型(复用与合并)
对于重复内容——路灯、椅子、树木、栅栏——**默认每个副本使用一个实体,并全部指向同一个 .glb。**引擎会按来源去重:无论有多少实体使用它,都只需下载一次、进行一次资源包转换,并在内存中保留一份网格和纹理。去重在整个会话中全局生效,因此相邻场景使用同一文件时也无需额外开销。
// GOOD: one file, many entities — engine shares the loaded asset
for (const position of lampPostPositions) {
const lampPost = engine.addEntity()
Transform.create(lampPost, { position })
GltfContainer.create(lampPost, { src: 'assets/scene/lampPost.glb' })
}
// BAD: near-identical files — 20 downloads, 20 copies in memory
// lampPost_01.glb, lampPost_02.glb, ... lampPost_20.glb
**复用并不会节省绘制调用。**无论使用 20 个实体,还是使用一个内含 20 个路灯的 .glb,20 个路灯都对应 20 个渲染器。场景内容不支持运行时批处理或 GPU 实例化:客户端会在运行时流式加载并构建场景,因此无法像处理预烘焙内容的引擎那样将重复对象分组。绘制数量在创作时就已固定。
| 方案 | 下载次数 | 网格与纹理 | 渲染器 | 材质 | 剔除 | 可移动/可点击 |
|---|---|---|---|---|---|---|
N 个实体,共用 1 个 .glb | 1 | 1 份 | N | N × 插槽 | 按对象 | 是 |
1 个 .glb,内部有 N 个独立对象 | 1 | 1 份 | N | N × 插槽 | 按对象 | 否 |
1 个 .glb,在 Blender 中合并网格 | 1 | 几何体重复 | 1 | 1 × 插槽 | 全部或无 | 否 |
只有第三种方案能减少绘制调用,但代价是文件体积和内存占用增加,并且无法按对象进行剔除。**仅在以下情况推荐这种方案:有许多小型道具、它们始终在同一位置同时可见、从不需要交互,且测量结果表明渲染器数量确实是瓶颈。**其他情况下,建议复用。
对于分散的道具,可以采用折中方案:每个道具簇使用一个 .glb(例如一个街区或一个房间的家具),而不是每个道具一个文件,也不是整个场景只用一个文件。这样既能减少渲染器数量,又能让道具簇保持足够小,使剔除机制发挥作用。
还有三个值得了解的影响:
- **复用也不会减少材质数量。**客户端会为每个渲染器实例化一个材质,用于写入场景的边界裁剪,因此统计数据中的
materials反映的是已渲染对象,而非不同模型的数量。实测:14 个实体共用一个.glb,会显示 28 个渲染器和 28 个材质,而geometries和textures仍各为一份。这些实例共用相同的纹理和着色器变体,因此只会少量增加内存开销,不会影响帧时间。在检查shaderVariants之前,切勿将材质去重作为解决帧时间问题的建议。 - **要一次生成许多副本?**先用
AssetLoad预加载(见下文),这样副本就能从已驻留的资源创建,而不必各自等待同一次下载。预加载并不会让创建副本没有开销:即使资源已驻留,构建副本仍占突发开销的约 90%,因此大量集中生成仍可能占用一帧。 - **一个超大模型对加载流畅度的影响比许多小模型更大。**资源创建会在帧时间预算内分散到多帧完成,但单个超大模型无法拆分——它会集中在一帧内加载,因此更容易造成明显卡顿。
请参阅 gltf-reuse-vs-merge 基准测试场景,了解具体测量结果:该场景通过复用(一个 .glb,多个实体)、使用重复文件以及使用合并后的 .glb 来生成相同的路灯模型,并对比渲染器/材质数量、内存占用和加载行为。它还演示了如何使用 AssetLoad 预加载,以及如何调整碰撞掩码以支持突发生成。
三角形数量优化
使用低多边形模型
- 小型道具:100-500 个三角形
- 中型物体:500-1,500 个三角形
- 大型建筑:1,500-5,000 个三角形
- 主体模型:最多 10,000 个三角形
使用 LOD(细节层次)
远距离时显示更简单的模型:
engine.addSystem(() => {
// Check distance to player and swap models
const playerPos = Transform.get(engine.PlayerEntity).position
const objPos = Transform.get(myEntity).position
const distance = Vector3.distance(playerPos, objPos)
const gltf = GltfContainer.getMutable(myEntity)
if (distance > 30) {
gltf.src = 'models/building_lod2.glb' // Low poly
} else if (distance > 15) {
gltf.src = 'models/building_lod1.glb' // Medium poly
} else {
gltf.src = 'models/building_lod0.glb' // High poly
}
})
使用基本几何体代替模型
对于简单形状,MeshRenderer 比加载 .glb 更轻量:
MeshRenderer.setBox(entity) // Very cheap
MeshRenderer.setSphere(entity) // Cheap
MeshRenderer.setPlane(entity) // Very cheap
在 Blender 中修复高开销模型
如果统计信息指向特定模型——例如包含数万三角形的道具、带有 2048+ 纹理的 GLB,或布满玩家永远看不到的面的网格——应通过驱动 Blender 来直接修复资源,而不是在 SDK 代码中补救。完整指南(无头 CLI 与 Blender MCP、设置、导出规则)位于 {baseDir}/../add-3d-models/references/blender-authoring.md,其中 {baseDir}/../add-3d-models/references/blender-patterns.md 提供了可直接使用的 bpy 脚本。常见的补救编辑:
- 使用 Decimate 修改器降低导入模型或细节过多模型的三角形数量(这是修复现有资源的工具——新模型从一开始就应采用低多边形建模)。
- 调整纹理尺寸,使其符合二次幂,尺寸为 1024×1024 或更小,并将纹理重新打包到 GLB 中。
- 删除永远不可见的面(底面、被遮挡的背面),并启用背面剔除,而不是重复几何体。
- 将每种颜色的材质合并为单个调色板纹理材质,并将每个部件的纹理合并到一张图集中。
- 移除
_collider网格上的灯光、摄像机和材质——这些都是引擎会忽略或重复处理的冗余内容。
使用 patterns 文件中的三角形计数和材质审计脚本,在编辑前后进行检查;导出到同一路径(预览会热重载该文件),然后再次检查统计面板。若想快速进行非 Blender 处理,可使用 gltf-transform(见下文),压缩并精简 GLB,而不更改其几何体设置。
纹理优化
- 尺寸必须是二次幂:256、512、1024
- 最大尺寸为 1024x1024。 asset-bundle-converter 会强制执行
DESKTOP_MAX_TEXTURE_SIZE = 1024(AssetBundleConverter.cs→ReduceTextureSizeIfNeeded):任何更大的纹理(例如 2048)都会在转换时缩小到 1024,因此制作超过 1024 的纹理只会增加源文件大小,不会带来视觉收益。 - 建议尺寸:大多数物体使用 512x512,主体部件使用 1024x1024
- 透明 UI/精灵图使用
.png - 不透明照片和纹理使用
.jpg - 尽可能优先使用 WebP 等压缩格式,而非原始 PNG
- 使用纹理图集(将多个纹理合并到一张图片中)以减少绘制调用和材质数量
- 在材质间共享纹理引用——不要重复纹理文件
- 在实体间复用材质:
// GOOD: Define material once, apply to many
Material.setPbrMaterial(entity1, {
texture: Material.Texture.Common({ src: 'images/wall.jpg' }),
})
Material.setPbrMaterial(entity2, {
texture: Material.Texture.Common({ src: 'images/wall.jpg' }),
})
// Same texture URL = shared in memory
按使用场景划分的纹理尺寸指南
| 使用场景 | 建议尺寸 | 最大尺寸 |
|---|---|---|
| 场景物体(墙壁、地板) | 1024x1024 | 1024x1024 |
| 道具和家具 | 512x512 | 1024x1024 |
| UI 元素 / 图标 | 256x256 | 512x512 |
| 天空盒 / 环境贴图 | 1024x1024 | 1024x1024 |
纹理不必是正方形——只要两个维度都是二次幂,512x1024 就是有效尺寸。
背面剔除
背面剔除会跳过渲染玩家从背面永远看不到的多边形内侧。这项设置应在 3D 建模工具(Blender、Maya 等)中完成——而不是在 SDK 代码中设置。
**经验法则:**默认对所有材质启用背面剔除。只有在表面必须从两侧都可见时才禁用(例如树上的叶片平面、薄墙)。
系统优化
避免逐帧分配
// BAD: Creates new Vector3 every frame
engine.addSystem(() => {
const target = Vector3.create(8, 1, 8) // Allocation!
})
// GOOD: Reuse constants
const TARGET = Vector3.create(8, 1, 8)
engine.addSystem(() => {
// Use TARGET
})
限制高开销操作的频率
let lastCheck = 0
engine.addSystem((dt) => {
lastCheck += dt
if (lastCheck < 0.5) return // Only run every 0.5 seconds
lastCheck = 0
// Expensive operation here
})
不需要时移除系统
const systemFn = (dt: number) => {
/* ... */
}
engine.addSystem(systemFn)
// When no longer needed:
engine.removeSystem(systemFn)
资源预加载(AssetLoad 组件)
使用 AssetLoad 提前将资源加载到内存中,使其在需要时立即显示。PBAssetLoad 只有一个字段:assets: string[],即要加载的资源路径列表。通常在 engine.RootEntity 上创建,但任何实体都可以。
import {
engine,
AssetLoad,
assetLoadLoadingStateSystem,
LoadingState,
} from '@dcl/sdk/ecs'
// Queue assets to pre-load (any entity works — commonly a dedicated cube/root).
// AssetLoad.getOrCreateMutable lets you also PUSH more paths later:
// AssetLoad.getOrCreateMutable(entity, { assets: [...] }).assets.push(morePath)
AssetLoad.create(entity, { assets: ['models/big.glb', 'sounds/win.mp3'] })
// React to loading state. The callback fires PER ASSET (once per path in the
// list), receiving { asset, currentState } — NOT one batch-level event.
assetLoadLoadingStateSystem.registerAssetLoadLoadingStateEntity(
entity,
(state: { asset: string; currentState: LoadingState }) => {
if (state.currentState === LoadingState.FINISHED) {
// state.asset finished loading and is now cached
}
},
)
// Stop listening: assetLoadLoadingStateSystem.removeAssetLoadLoadingStateEntity(entity)
LoadingState 枚举成员:LOADING、FINISHED、FINISHED_WITH_ERROR(找到资源但加载失败)、NOT_FOUND(路径不存在)、UNKNOWN(初始/默认状态)。缺失或拼写错误的 src 会得到 NOT_FOUND,而不会抛出错误。
注意事项:
- 状态回调针对每个资源,以
asset路径字符串为键 — 根据state.asset分发,以更新对应的实体。没有统一的“全部完成”事件;请自行统计每个资源的FINISHED/错误状态来跟踪完成情况。 AssetLoad只会将资源添加到内存中。从assets列表中移除路径不会释放内存 —AssetLoad不提供卸载功能。- 预加载路径不会创建或渲染任何内容 — 你仍需在实体上通过
getOrCreateMutable获取真实组件(GltfContainer、AudioSource、VideoPlayer、Material纹理)才能使用;预加载只是让之后的使用立即生效。 - 如果资源在场景启动时就会立即使用,则无需使用
AssetLoad。只预加载启动时不需要的资源 — 即稍后出现或由玩家交互触发的资源。
本地资源包预览
在发布前,先在本地重现服务端的资源包转换。这可以发现转换问题(缺失纹理、压缩后模型损坏),并让预览使用经过生产级优化的模型进行渲染。
- **Creator Hub:**Play Options(Play 按钮上的下拉箭头)> 将鼠标悬停在 Desktop Client 上 > 勾选 Compress Assets。Linux 系统以及
@dcl/sdk-commands版本过旧、不支持该标志的场景中,此开关会隐藏。 - CLI:
npm run start -- --asset-bundles(该参数会传入 Explorer 深层链接,作为local-ab=true;--local-ab是深层链接参数,不是 CLI 标志)
Desktop Explorer 会在你的机器上将所有 .gltf/.glb 模型转换为资源包。大型场景首次运行可能需要几分钟;转换后的模型会缓存,因此后续预览只会重新转换新增或修改的资源。如果资源转换失败,预览会回退到原始模型。仅 Desktop Client 支持此功能(Bevy Web 不支持)。
这相当于每次发布后运行的服务端转换(参见 deploy-scene 技能)。请在发布前定期使用,尤其是在举办现场活动之前。
加载时间优化
- 尽可能为大型共享资源使用 CDN URL
大型场景的加载区域
对于包含许多 3D 模型的场景(例如配有家具的多房间建筑),请避免一次性渲染所有内容。使用触发区域,在玩家经过场景时加载和卸载内容:
import { engine, Transform, GltfContainer, TriggerArea, triggerAreaEventsSystem, ColliderLayer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
// Keep furniture hidden initially
let furnitureLoaded = false
// When player enters the building, spawn interior furniture
const trigger = engine.addEntity()
Transform.create(trigger, {
position: Vector3.create(8, 1, 8),
scale: Vector3.create(3, 3, 3)
})
TriggerArea.setBox(trigger, ColliderLayer.CL_PLAYER)
triggerAreaEventsSystem.onTriggerEnter(trigger, () => {
if (!furnitureLoaded) loadInterior()
furnitureLoaded = true
})
triggerAreaEventsSystem.onTriggerExit(trigger, () => {
if (furnitureLoaded) unloadInterior()
furnitureLoaded = false
})
此模式可让初始三角形数量和实体数量保持较低,并仅在需要时加载细节。
常见性能陷阱
| 陷阱 | 症状 | 修复方法 |
|---|---|---|
| 唯一材质过多 | 内存和纹理预算超限,批处理失效 | 使用图集纹理,复用模型;在归咎于帧时间之前检查 shaderVariants |
| 非 2 的幂次方纹理 | 内存膨胀、视觉伪影 | 将所有纹理调整为 256/512/1024(最大为 1024) |
| 快速创建/销毁实体 | 帧卡顿 | 使用实体池 |
| 每帧都进行大量计算 | FPS 持续偏低 | 添加计时器保护,降低执行频率 |
| 装饰物上未使用的碰撞体 | 超出物理体数量限制 | 从非交互对象中移除 MeshCollider |
| 未压缩的大型纹理 | 加载缓慢,文件大小超限 | 使用 WebP、降低分辨率、使用图集 |
| 部署时上传工作文件 | “Scene too large” 部署错误 | 将 Blender/FBX 源文件、概念图和文档添加到 .dclignore(参见 deploy-scene) |
| 透明材质过多 | 额外绘制调用、排序问题 | 尽量减少透明度,使用 alpha cutoff 代替混合 |
| 在系统中无保护地添加实体/组件 | 实体数量激增 | 系统每帧都会运行——创建前务必检查 |
| 无限制的实体查询 | CPU 突增 | 使用特定组件进行筛选,并缓存结果 |
| 所有细节在所有距离下都加载 | 三角形预算超限 | 实现 LOD 系统 |
每个副本都使用几乎相同的 .glb(chair_01.glb、chair_02.glb……) | 加载缓慢、内存膨胀、纹理数量超限 | 多个实体引用同一个共享 .glb——引擎会按源文件去重 |
整个场景使用一个单体 .glb | 出现时加载卡顿,且无法剔除任何内容 | 拆分为按群组划分的模型(如一个街区、一间房间) |
| 未预加载资源 | 游戏过程中突然出现 | 使用 AssetLoad 预加载稍后需要的资源(而非启动时的资源) |
场景统计监控
预览模式下
使用 npm run start 在本地运行场景时:
- 按 P 切换性能面板。
- 监控:FPS、绘制调用、三角形、实体、材质、纹理、内存。
- 场景限制会与当前用量一同显示,并使用绿色/黄色/红色指示。
需要关注的指标
- FPS 低于 30:某项开销过大。检查绘制调用和系统执行时间。
- 三角形数量接近上限:启用 LOD、减少模型细节、移除隐藏面。
- 实体数量持续增加:很可能存在泄漏——实体被创建后却从未销毁。实现对象池。
- 绘制调用超过 300(1 个地块):渲染的材质槽过多。使用图集并降低透明度,以减少每个对象的材质槽数量,同时减少渲染对象的数量。请注意,绘制调用数按渲染对象 × 其材质槽数量计算——在多个实体间复用一个模型不会降低该数量(参见上文的 重复模型),将多个独立对象打包到一个
.glb中也不会降低该数量。
推荐的优化工具
| 工具 | 用途 |
|---|---|
| Blender Decimate 修改器 | 减少导入模型的三角形数量(通过无头模式或 MCP 驱动 — 参见 在 Blender 中修复高负载模型) |
| Blender Limited Dissolve | 移除平坦表面上不必要的顶点 |
| Squoosh (squoosh.app) | 将图像转换为 WebP,并调整为 2 的幂次尺寸 |
| TexturePacker | 根据多张图像创建纹理图集 |
| gltf-transform CLI | 使用 Draco 压缩 GLB 文件,并移除未使用的数据 |
| glTF Validator | 在导入 DCL 前检查导出错误 |
| Creator Hub Scene Inspector | 用于查看实体数量、三角形数量和摆放位置的可视化工具 |
| Preview Debug Panel (P key) | 在 npm run start 期间实时查看性能指标 |
# Optimize a GLB with Draco compression
npx @gltf-transform/cli optimize input.glb output.glb --compress draco
⚠️ Draco 无法在移动设备上解码,而且故障不会报错,只会在预览中显现。 移动应用不包含运行时 Draco 解码器。Draco 压缩的 .gltf/.glb 只能通过场景的优化资源在手机上加载,因为资源服务器已对其进行解压。当场景没有优化资源时 — 本地预览、Creator Hub 移动预览、QR 预览 — 模型会加载失败,且完全没有错误提示:没有控制台消息,没有占位符,网格就这样消失了(文档提交 5f212e3,2026 年 10 月;godot-explorer #2432 正在跟踪错误对话框)。
问题在于这种不对称性:已发布的场景在手机上可能看起来正常,但同一个模型在所有移动预览中都不可见 — 而调试该预览的创作者无从查起。如果场景面向移动设备,请在导出时禁用 Draco;跨平台构建时使用 optimize 并添加 --compress false,同时改为减少三角形和纹理。[未验证:其他 gltf-transform 压缩模式(meshopt、quantize)是否能在移动设备上加载 — 文档只特别提到 Draco。]
示例场景
引擎团队的压力测试场景(应视为 API 结构的权威参考):
- https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/0,2-cube-wave-32x32 — 单个地块中约有 961 个基本立方体(远超 200 个实体的软限制),每帧通过一次
engine.getEntitiesWith(MeshRenderer)查询修改所有对象的Transform.position.y。此示例说明可以超过软限制,而且每帧查询数百个实体是预期用法(固定静态集合无需对象池 — 立方体在main()中创建一次,而非每帧创建)。 - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/73,-2-dbmonster — UI 压力测试:ReactEcs 树中有数十个嵌套的
<Label>,每帧都会使用Math.random()值重新构建。此示例说明 UI 渲染函数会在每帧重新运行,因此在.tsx中进行大量的逐帧分配会产生实际开销。 - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/88,-12-asset-load — 使用
AssetLoad预加载,并通过针对每个资源的assetLoadLoadingStateSystem状态回调处理(mp3 / 纹理 / 视频 / glb,另有一个特意设置的缺失路径,会解析为NOT_FOUND)。 - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/94,-10-gltf-reuse-vs-merge — 基准测试:比较复用 GltfContainer(一个
.glb、N 个实体)、重复文件和合并网格,并测量渲染器/材质数量、内存和加载流畅度。使用AssetLoad预加载,并调整碰撞掩码以控制突发生成。
交叉引用
- deploy-scene — 发布后资源包的转换时机、故障排查、
/detectabs命令 - add-3d-models — 模型加载、碰撞体、文件组织,以及驱动 Blender 修复或创作模型(
references/blender-authoring.md) - game-design — 性能预算、设计模式和 MVP 规划
- advanced-rendering — 使用 VisibilityComponent 的纹理模式、材质复用和 LOD
- scene-runtime — 使用
EngineInfo.sceneHidden,在场景被全屏 Explorer UI 遮挡时暂停耗费资源的系统