资源加载与释放:文件存在还不够
图片在项目里、进入构建产物、已加载到内存、已显示到 Sprite,是四件不同的事。先定位在哪一步失败,再处理问题。
两种常见引用方式
检查器直接绑定 SpriteFrame,场景或 Prefab 就有明确依赖,适合启动必须用的内容。运行时按路径加载,适合可选内容,但需要加载状态、错误处理和生命周期管理。
普通动态资源示例放在 assets/resources/icons/gem.png。加载 SpriteFrame 时,使用 icons/gem/spriteFrame,不带 assets/resources 前缀与文件扩展名。图像、Texture 与 SpriteFrame 是不同类型,路径和类型应匹配。
一个会处理过期回调的图标组件
下载 SafeIcon.ts。把它挂到有效 UI 节点,绑定显示图标的 Sprite,并放好上述图片。启用会加载;停用会取消本次结果的使用并清除引用;手动调用 loadIcon 可重试。
展开完整异步资源示例
SafeIcon.ts
import { _decorator, Component, resources, Sprite, SpriteFrame } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('SafeIcon')
export class SafeIcon extends Component {
@property(Sprite) icon: Sprite | null = null;
private generation = 0;
private held: SpriteFrame | null = null;
onEnable() { this.loadIcon(); }
onDisable() {
++this.generation;
if (this.icon) this.icon.spriteFrame = null;
this.held?.decRef();
this.held = null;
}
loadIcon() {
const ticket = ++this.generation;
// 实际文件应为 assets/resources/icons/gem.png;不带扩展名。
resources.load('icons/gem/spriteFrame', SpriteFrame, (err, frame) => {
if (!this.isValid || !this.enabledInHierarchy || ticket !== this.generation) return;
if (err || !frame) { console.warn('图标加载失败,请检查资源路径后重试'); return; }
if (!this.icon) return;
frame.addRef();
this.icon.spriteFrame = frame;
this.held?.decRef();
this.held = frame;
});
}
}
请求代号 generation 不取消底层下载,它只防止旧结果覆盖新界面。每次尝试都会得到一个代号,回调只处理当前代号且仍有效的组件。
释放需要确定所有者
示例只为自己持有的 SpriteFrame 增减引用:成功绑定前 addRef,不再持有时 decRef。不要在离开一个页面时调用“释放所有资源”,其他页面可能还在使用共享图集或音频。
同样,不要为了清理一次加载失败,随意释放一个可能被其他地方持有的资源对象。较复杂项目应按场景、Bundle 或业务模块约定资源所有权。
排查空白图片
- 控制台是否有编译或加载错误?
- 路径是否相对正确目录,大小写是否一致?
- 加载的类型是否是 SpriteFrame,而非 Texture?
- 回调回来时节点是否还在、此次请求是否过期?
- Sprite 的尺寸、颜色、透明度、层级和相机是否正确?
给 AI:“请先写出资源目录和加载类型;包含 loading/error/ready 状态,失败可手动重试;解释资源引用由谁持有、何时释放。”