跳到主要内容

资源加载与释放:文件存在还不够

图片在项目里、进入构建产物、已加载到内存、已显示到 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 或业务模块约定资源所有权。

排查空白图片​

  1. 控制台是否有编译或加载错误?
  2. 路径是否相对正确目录,大小写是否一致?
  3. 加载的类型是否是 SpriteFrame,而非 Texture?
  4. 回调回来时节点是否还在、此次请求是否过期?
  5. Sprite 的尺寸、颜色、透明度、层级和相机是否正确?

给 AI:“请先写出资源目录和加载类型;包含 loading/error/ready 状态,失败可手动重试;解释资源引用由谁持有、何时释放。”

依据:加载资源、资源释放。