You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ResourceLoader是否应缓存已加载资源?异步加载二次获取异常疑问

问题

在MacOS(Intel)平台使用Godot 4.2测试异步资源加载功能时遇到异常:调用ResourceLoader.load_threaded_request发起加载请求后,首次调用load_threaded_get_status和load_threaded_get能正常获取已加载的资源,但第二次调用时状态变为THREAD_LOAD_INVALID_RESOURCE(表示资源无效或未通过load_threaded_request加载)。

原本以为ResourceLoader会缓存已加载资源,因为load_threaded_request的默认缓存模式是CACHE_MODE_REUSE,但官方文档未明确该缓存模式的具体含义,不确定这是Bug还是对API的理解偏差。相关代码片段如下:

const ROCKET_SCENE_FILE: String = "res://scenes/rocket.tscn"


func _ready():
    ResourceLoader.load_threaded_request(ROCKET_SCENE_FILE) # 异步加载火箭场景


func fire_rocket() -> void:
    var rocket_loading_status: ResourceLoader.ThreadLoadStatus = ResourceLoader.load_threaded_get_status(ROCKET_SCENE_FILE)
    if  rocket_loading_status == ResourceLoader.THREAD_LOAD_LOADED:
        var rocket_scene: Resource = ResourceLoader.load_threaded_get(ROCKET_SCENE_FILE) # 获取已加载的场景
        var rocket_instance: Node = rocket_scene.instantiate() # 实例化场景
        add_child(rocket_instance) # 添加到当前场景
    else:
        assert(rocket_loading_status == ResourceLoader.THREAD_LOAD_IN_PROGRESS, "FAULT failure loading rocket resource: ResourceLoader.ThreadLoadStatus = " + str(rocket_loading_status))


func _process(delta: float) -> void:
    if handle_actions:
        process_actions() # 读取输入并设置状态/触发器
    
    if shoot == true:
        fire_rocket()
        shoot = false # 重置射击触发器

疑问:ResourceLoader是否设计为缓存资源以支持多次获取?还是仅允许单次通过异步API获取?

解答

核心结论

ResourceLoader的全局资源缓存是存在的,但异步加载请求的状态仅在单次load_threaded_get调用前有效——调用load_threaded_get获取资源后,对应的异步请求会被销毁,因此后续再调用load_threaded_get_status会返回THREAD_LOAD_INVALID_RESOURCE,这是API的正常行为,不是Bug。

关于CACHE_MODE_REUSE的说明

CACHE_MODE_REUSE控制的是资源本身是否被存入全局资源缓存,而非异步加载请求的缓存。当你第一次通过load_threaded_get拿到资源后,该资源已经被缓存到全局缓存中,但异步加载的请求状态会被清理,所以无法再通过异步API的状态检查来获取它。

解决方案

针对你的场景,有两种更合理的处理方式:

  1. 自行保存资源引用
    在首次获取到资源后,将其保存为类成员变量,后续直接使用该引用实例化,无需再调用异步加载相关API:

    const ROCKET_SCENE_FILE: String = "res://scenes/rocket.tscn"
    var rocket_scene: Resource = null
    
    func _ready():
        ResourceLoader.load_threaded_request(ROCKET_SCENE_FILE)
    
    func fire_rocket() -> void:
        if rocket_scene:
            var rocket_instance: Node = rocket_scene.instantiate()
            add_child(rocket_instance)
            return
    
        var rocket_loading_status = ResourceLoader.load_threaded_get_status(ROCKET_SCENE_FILE)
        if rocket_loading_status == ResourceLoader.THREAD_LOAD_LOADED:
            rocket_scene = ResourceLoader.load_threaded_get(ROCKET_SCENE_FILE)
            var rocket_instance: Node = rocket_scene.instantiate()
            add_child(rocket_instance)
        else:
            assert(rocket_loading_status == ResourceLoader.THREAD_LOAD_IN_PROGRESS, "加载火箭资源失败: " + str(rocket_loading_status))
    
  2. 利用全局缓存直接同步加载
    因为资源已经被缓存,后续调用ResourceLoader.load()会直接从缓存中获取,不会产生实际的IO操作,性能几乎和直接使用引用一致:

    func fire_rocket() -> void:
        var rocket_loading_status = ResourceLoader.load_threaded_get_status(ROCKET_SCENE_FILE)
        if rocket_loading_status == ResourceLoader.THREAD_LOAD_LOADED:
            # 首次获取后,后续直接用同步加载从缓存取
            var rocket_scene: Resource = ResourceLoader.load(ROCKET_SCENE_FILE)
            var rocket_instance: Node = rocket_scene.instantiate()
            add_child(rocket_instance)
        elif rocket_loading_status == ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
            # 异步请求已销毁,但资源在缓存中,直接加载
            var rocket_scene: Resource = ResourceLoader.load(ROCKET_SCENE_FILE)
            var rocket_instance: Node = rocket_scene.instantiate()
            add_child(rocket_instance)
        else:
            assert(rocket_loading_status == ResourceLoader.THREAD_LOAD_IN_PROGRESS, "加载火箭资源失败: " + str(rocket_loading_status))
    

补充说明

如果确实需要多次发起异步加载(比如资源可能被卸载后重新加载),可以在每次调用fire_rocket时先检查状态,若为THREAD_LOAD_INVALID_RESOURCE则重新调用load_threaded_request,但这种场景在你的案例中并不必要,因为资源一旦加载就会被缓存,除非手动调用ResourceLoader.unload()清除缓存。


内容的提问来源于stack exchange,提问作者Ty N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 10:52:37