跳转至

异步加载与 LRU 缓存

当项目需要按需加载特效、图标、UI 面板或关卡资源,并希望统一处理缓存、并发请求、取消和调试快照时,可以使用 GFAssetUtility

基本用法

var assets := Gf.get_utility(GFAssetUtility) as GFAssetUtility

# 异步加载一个带路径的资源。缓存命中时直接返回,
# 如果已有相同请求,则共用同一次加载;如果没有,则发起新的 threaded request。
assets.load_async("res://actors/runtime_actor.tscn", func(res: Resource) -> void:
    var actor_scene := res as PackedScene
    if actor_scene != null:
        add_child(actor_scene.instantiate())
)

它内置 LRU 上限。当缓存过大时,会自动清理长期未被提取引用的资源。max_cache_size = 0 会禁用并清空缓存;pin_cache(path) 会用引用计数锁定关键资源,重复 pin 需要对应次数 unpin_cache(path) 后才会重新参与 LRU 淘汰。

assets.max_cache_size = 128
assets.load_async("res://ui/inventory_panel.tscn", _on_panel_loaded, "PackedScene")

assets.pin_cache("res://ui/common_icons.tres")

同一路径的并发加载会合并到同一个 threaded request。如果已存在请求或缓存的资源类型与新的 type_hint 明显不兼容,回调会收到 null。命中缓存时回调会同步执行。

加载通道与并发上限

默认情况下,GFAssetUtility 会立即发起新的 ResourceLoader.load_threaded_request()。当项目需要控制同类资源的并发数量时,可以在 load_async()load_handle_async()preload_group_async() 的 options 中传入 serial_lane_id / lane_idmax_concurrent_loads

assets.load_async(
    "res://levels/chunk_01.tscn",
    _on_chunk_loaded,
    "PackedScene",
    {
        "lane_id": &"chunk_streaming",
        "max_concurrent_loads": 1,
    }
)

非空通道没有显式上限时会按串行处理;max_concurrent_loads = 0 表示不限制并发。default_max_concurrent_loads 可为未声明上限的请求提供默认值。被通道限制挡住的请求会进入队列并发出 asset_load_queued(path, lane_id),此时 is_loading(path) 仍返回 trueget_load_progress(path) 返回 0.0,直到请求真正开始。

preload_group_async() 如果设置了 max_concurrent_loads 但没有显式通道,会用 group id 作为加载通道,适合让一个预热组内部受限,同时不影响其他组或单资源请求。

进度反馈

需要把加载状态接到 UI 或诊断面板时,可以监听 asset_load_progress(path, progress),也可以用 get_load_progress(path) 主动读取最近一次轮询到的进度。已缓存资源返回 1.0,无请求或已取消请求返回 0.0

assets.asset_load_progress.connect(func(path: String, progress: float) -> void:
    if path == "res://ui/inventory_panel.tscn":
        loading_bar.value = progress
)

assets.load_async("res://ui/inventory_panel.tscn", _on_panel_loaded, "PackedScene")