【问题标题】:Attempting to load AssetReference that has already been loaded尝试加载已加载的 AssetReference
【发布时间】:2021-10-17 19:06:00
【问题描述】:

当我开始主场景并测试一个新角色时,为什么会显示此错误?

Attempting to load AssetReference that has already been loaded. Handle is exposed through getter OperationHandle
UnityEngine.AddressableAssets.AssetReference:LoadAssetAsync<UnityEngine.GameObject> ()
TrackManager/<SpawnFromAssetReference>d__104:MoveNext () (at Assets/Scripts/Tracks/TrackManager.cs:565)
UnityEngine.MonoBehaviour:StartCoroutine (System.Collections.IEnumerator)
TrackManager:SpawnObstacle (TrackSegment) (at Assets/Scripts/Tracks/TrackManager.cs:556)
TrackManager/<SpawnNewSegment>d__102:MoveNext () (at Assets/Scripts/Tracks/TrackManager.cs:538)
UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)

我的代码:

    if (m_SafeSegementLeft <= 0)
    {
        SpawnObstacle(newSegment);
    }
    else
        m_SafeSegementLeft -= 1;

    m_Segments.Add(newSegment);

    if (newSegmentCreated != null) newSegmentCreated.Invoke(newSegment);
}


public void SpawnObstacle(TrackSegment segment)
{
    if (segment.possibleObstacles.Length != 0)
    {
        for (int i = 0; i < segment.obstaclePositions.Length; ++i)
        {
            AssetReference assetRef = segment.possibleObstacles[Random.Range(0, segment.possibleObstacles.Length)];
            StartCoroutine(SpawnFromAssetReference(assetRef, segment, i));
        }
    }

    StartCoroutine(SpawnCoinAndPowerup(segment));
}

private IEnumerator SpawnFromAssetReference(AssetReference reference, TrackSegment segment, int posIndex)
{
    AsyncOperationHandle op = reference.LoadAssetAsync<GameObject>();
    yield return op; 
    GameObject obj = op.Result as GameObject;
    if (obj != null)
    {
        Obstacle obstacle = obj.GetComponent<Obstacle>();
        if (obstacle != null)
            yield return obstacle.Spawn(segment, segment.obstaclePositions[posIndex]);
    }
}

它说我在第 565 行有错误,即AsyncOperationHandle op = reference.LoadAssetAsync&lt;GameObject&gt;();

这里有什么错误?

【问题讨论】:

  • 好吧,你从segment.possibleObstacles 的列表中随机选择AssetReferences ...不太可能两次获得相同的元素=>您尝试再次加载它...我会保留已加载引用的列表,如果之前已加载当前选定的引用,则跳过加载!
  • 一般情况下请使用正确的标签! unityscript 是或更好的一种自定义 JavaScript 风格,类似于早期 Unity 版本中使用的语言,并且现在很长不推荐使用,您的代码显然是 c#unity-conainer 也与您使用的 Unity3d GameEngine 完全无关....

标签: c# unity3d


【解决方案1】:

错误消息听起来很不言自明:您尝试加载相同的可寻址两次。

如上所说

AssetReference assetRef = segment.possibleObstacles[Random.Range(0, segment.possibleObstacles.Length)];

您从可用的可寻址对象中选择一个随机条目。但是,这里没有什么可以阻止您随便两次获得相同的元素。

我宁愿跟踪哪些已经加载

Dictionary<AssetReference, GameObject> loadedAssets = new Dictionary<AssetReference, GameObject>();

然后做

private IEnumerator SpawnFromAssetReference(AssetReference reference, TrackSegment segment, int posIndex)
{
    if(!loadedAssets.TryGetValue(reference, out var obj || !obj)
    {
        loadedAssets.Add(reference, null);
        AsyncOperationHandle op = reference.LoadAssetAsync<GameObject>();
        yield return op; 
        obj = op.Result;

        loadedAssets[reference] = obj;
    }
    
    if(!obj.TryGetComponent<Obstacle>(out var obstacle))
    {
        Debug.LogError($"No {nameof(Obstacle)} component on loaded object!");
        yield break;
    }
    
    yield return obstacle.Spawn(segment, segment.obstaclePositions[posIndex]);
}

当然,每当您 Release 您还想要加载的资产之一时

loadedAssets.Remove(reference);

或者根据您的用例和需要将它们全部加载,然后如果您要更频繁地生成它们,则启动您的应用程序。

【讨论】:

  • 这种方法非常适合预加载对象,或加载 1 个对象然后有时间加载下一个对象,但如果您需要加载,比如说 5 或 6,它仍然会尝试加载地址在添加到列表之前已经存在,到目前为止,可寻址对象似乎不是快速实例化的错误选择,除非您使用 LoadAssetAsync().WaitForCompletion(),在我的情况下,这会使游戏在控制台上非常滞后
  • 当您开始加载 null 值并在加载完成后填写该值时,您当然可以将其“预添加”到字典中
  • 我最终应用了您的方法,但将任务保存在字典中,而不是任务结果,因此我可以随时检查结果是否已加载并决定要做什么,在我的游戏我确实需要经常实例化并返回任何实例化的东西,经过 4 年的开发,现在改变它会让人头疼,我觉得整个可寻址的方法仍处于测试阶段
【解决方案2】:

按照 derHugo 存储值的方法,我想出了这个方法,始终准备好可寻址结果作为游戏对象返回,但防止它被多次加载,可寻址对象也有一个 'address.LoadAssetAsync().WaitForCompletion( )' 方法,但在我的情况下,当加载很多东西时它变得太迟钝了。

    private async Task<T> GetNewInstance<T>(AssetReferenceGameObject address) where T : MonoBehaviour
    {
        if (!loadedAssetsTask.ContainsKey(address))
        {
            LoadNewAddress<T>(address);
        }

        var asset = await loadedAssetsTask[address];
        var newGameObject = Instantiate(asset);
        var component = newGameObject.GetComponent<T>();

        return component;
    }

    private void LoadNewAddress<T>(AssetReferenceGameObject address) where T : MonoBehaviour
    {
        var loadedAssetTask = address.LoadAssetAsync().Task;
        loadedAssetsTask.Add(address, loadedAssetTask);
    }

【讨论】:

    猜你喜欢
    • 2011-02-12
    • 2014-01-30
    • 1970-01-01
    • 2023-03-29
    • 2022-10-22
    • 2021-08-30
    • 2014-05-01
    • 2017-08-16
    • 1970-01-01
    相关资源
    最近更新 更多