从 asssetBundle 加载的预制件是否可以传递给 Vuforia AnchorBehavior 声明

Can a Prefab loaded from asssetBundle be passed to Vuforia AnchorBehavior declaration

目标是一个两步过程,其中从 AssetBundle 加载 Vuforia GroundPlane 对象的预制件,并传递给 AnchorBehavior 变量声明以放置游戏对象。

很抱歉,作为 Unity C# 的新手,我没有像我希望的那样准确

尝试了各种方法将加载的预制件等同于 AnchorBehavior。但是因为这是两种类型的对象,所以会出现错误,表明它们不能隐式相等

声明如下:

public PlaneFinderBehaviour plane;
public ContentPositioningBehaviour planeFinder;
public AnchorBehaviour model;
public string nameOfAssetBundle;
public string nameOfObjectToLoad;

想法是将代表Prefab的"nameOfObjectToLoad"传递给"AnchorBehavior"值,然后可以使用下面的方法"onClick",当脚本附加到按钮。

public void create()
{
    planeFinder.AnchorStage = model.GetComponent<AnchorBehaviour>();
}

期望将预制件传递给 AnchorBehavior 并实例化预制件 "onClick"

这是从中提取这些片段的完整脚本。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;

public class anchorManagerBundles : MonoBehaviour
{
    public PlaneFinderBehaviour plane;
    public ContentPositioningBehaviour planeFinder;
    public AnchorBehaviour model;
    public string nameOfAssetBundle;
    public string nameOfObjectToLoad;

    void Start()
    {
        StartCoroutine(LoadAsset(nameOfAssetBundle, nameOfObjectToLoad));
    } 

    IEnumerator LoadAsset(string assetBundleName, string objectNameToLoad)
    {
        string filePath = System.IO.Path.Combine(Application.streamingAssetsPath, "AssetBundles");
        filePath = System.IO.Path.Combine(filePath, assetBundleName);

        //Load "nameOfAssetBundle" AssetBundle
        var assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(filePath);
        yield return assetBundleCreateRequest;

        AssetBundle assetBundle = assetBundleCreateRequest.assetBundle;

        //Load the "nameOfOjectToLoad" Asset (Use Texture2D since it's a Texture. Use GameObject if prefab)
        AssetBundleRequest asset = assetBundle.LoadAssetAsync<GameObject>(objectNameToLoad);
        yield return asset;

        //Retrieve the object (Use Texture2D since it's a Texture. Use GameObject if prefab)
        GameObject loadedAsset = asset.asset as GameObject;

        //Do something with the loaded loadedAsset  object (Load to RawImage for example) 
        //model = loadedAsset;
    }
    public void create()
    {
        planeFinder.AnchorStage = model.GetComponent<AnchorBehaviour>();
    }

}

进一步的研究揭示了我的新手技能。 "model = loadedAsset;" 的最终意图是我试图直接从一种数据类型转换为另一种数据类型,这无法明确完成。但到目前为止,我的研究还没有找到一种方法来从 AssetBundle 中获取加载的预制件并将其提供给 AnchorBehaviour 变量。

如果有人对这种数据类型之间的转换方法有任何经验,非常感谢您的指导。

更新 通过正确转换声明,消除了转换错误。

model = (asset.asset as AnchorBehaviour);

但现在我有一个 NullReference 错误,表明我未能在这一行中正确声明该值

    {
        planeFinder.AnchorStage = model.GetComponent<AnchorBehaviour>();
    }

现在这是我的新困境,因为我不确定我在哪里没有正确声明变量。

更新 当 AnchorBehaviour 变量设置为私有时,此错误得到解决。因此,现在脚本可以编译,但无法产生预期的结果。这可能表明需要更改加载 AssetBundle 组件的方式,如 IEnumerator 部分所述。 Unity 控制台打印出如下日志注释

There is no content to place at the anchor. Set the "Anchor Stage" field to the content you wish to place.
UnityEngine.Debug:LogError(Object)

在所有建议之后,这是最新的脚本,不幸的是它没有放置 AssetBundle 中的内容。我可以看到更多关于我的研究和测试。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;

public class anchorManagerBundles : MonoBehaviour
{
    public PlaneFinderBehaviour plane;
    public ContentPositioningBehaviour planeFinder;
    private AnchorBehaviour model;
    public string nameOfAssetBundle;
    public string nameOfObjectToLoad;
    private static bool alreadyLoading;
    private static AssetBundle assetBundle;

    void Start()
    {
        // only load the bundle once
        if (!alreadyLoading)
        {
            // set the flag to make sure this is never done again
            alreadyLoading = true;
            StartCoroutine(LoadAsset(nameOfAssetBundle, nameOfObjectToLoad));
        }
        else
        {
            LoadObjectFromBundle(nameOfObjectToLoad);
        }
    }

    private IEnumerator LoadAsset(string assetBundleName, string objectNameToLoad)
    {
        string filePath = System.IO.Path.Combine(Application.streamingAssetsPath, "AssetBundles");
        filePath = System.IO.Path.Combine(filePath, assetBundleName);

        if (assetBundle == null) 
        { 
            var assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(filePath); 
            yield return assetBundleCreateRequest; assetBundle = assetBundleCreateRequest.assetBundle; 
        }

    private IEnumerator LoadObjectFromBundle(string objectNameToLoad)
    {
        AssetBundleRequest assetRequest = assetBundle.LoadAssetAsync<GameObject>(objectNameToLoad);
        yield return assetRequest;

        GameObject loadedAsset = (GameObject)assetRequest.asset;

        model = loadedAsset.GetComponent<AnchorBehaviour>();
    }

    public void create()
    {
        planeFinder.AnchorStage = model;
    }
}

为资产添加 if 语句后,编译正常,我继续让控制台警告 "model" (AnchorBehavior/Anchor Stage) 字段需要有一个值。因此,脚本似乎没有传递在 "nameOfObjectToLoad" 字段中声明的 AssetBundle 对象,或者传递的内容不匹配。因此,为了同样的解释,我暂时将 "model" 字段设置为 public 并手动填充该字段。您会看到它将 Prefab 标识为 "AnchorBehavior" 对象。 Button Values in Editor - Desired Outcome

以下是尝试放置对象时编辑器控制台中的完整错误。

There is no content to place at the anchor. Set the "Anchor Stage" field to the content you wish to place.
UnityEngine.Debug:LogError(Object)
Vuforia.ContentPositioningBehaviour:CreateAnchorAndPlaceContent(Func`2, Vector3, Quaternion)
Vuforia.ContentPositioningBehaviour:PositionContentAtPlaneAnchor(HitTestResult)
UnityEngine.Events.UnityEvent`1:Invoke(HitTestResult)
Vuforia.PlaneFinderBehaviour:PerformHitTest(Vector2)
UnityEngine.Events.UnityEvent`1:Invoke(Vector2)
Vuforia.AnchorInputListenerBehaviour:Update()

为了进一步调试问题,我修改了以下部分。

        if (assetBundle == null)
        {
            Debug.Log("Failed to Load assetBundle!!");
            yield break;
        }

目的是尝试确定 AssetBundle 是否实际上正在加载。这非常有用,但它产生了一个非常奇怪的结果,我会征求意见。

此脚本附加到一系列按钮,以便在单击按钮时,使用 create() 函数根据变量实例化预制件。

这些按钮分为 3 个不同的 UI 面板。用户点击选中的面板按钮,显示出想要的按钮面板。

非常奇怪的是,当用户点击Panel Selection Button时,Editor Log中会出现以下错误。这是在单击具有附加脚本的实际按钮之前。

点击面板按钮

Failed to Load assetBundle!!
UnityEngine.Debug:Log(Object)
<LoadAsset>d__8:MoveNext() (at Assets/Scripts/anchorManagerBundles.cs:38)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
anchorManagerBundles:Start() (at Assets/Scripts/anchorManagerBundles.cs:23)

点击按钮(脚本附加按钮)

There is no content to place at the anchor. Set the "Anchor Stage" field to the content you wish to place.
UnityEngine.Debug:LogError(Object)
Vuforia.ContentPositioningBehaviour:CreateAnchorAndPlaceContent(Func`2, Vector3, Quaternion)
Vuforia.ContentPositioningBehaviour:PositionContentAtPlaneAnchor(HitTestResult)
UnityEngine.Events.UnityEvent`1:Invoke(HitTestResult)
Vuforia.PlaneFinderBehaviour:PerformHitTest(Vector2)
UnityEngine.Events.UnityEvent`1:Invoke(Vector2)
Vuforia.AnchorInputListenerBehaviour:Update()

我不明白为什么在单击没有附加脚本的面板按钮时调用脚本。

对于这个令人费解的问题,我们非常感谢任何建议。

结帐AssetBundle.LoadAssetAsync

Prior to version 5.0, users could fetch individual components directly using LoadAsync. This is not supported anymore. Instead, please use LoadAssetAsync to load the game object first and then look up the component on the object.


您不能简单地将检索到的 GameObject 引用类型转换为 AnchorBehaviour 引用。相反,您必须使用 GetComponent.


所以你应该做的是

AssetBundleRequest assetRequest = assetBundle.LoadAssetAsync<GameObject>(objectNameToLoad);
yield return assetRequest;

GameObject loadedAsset = (GameObject)assetRequest.asset;

// since model is already of type AnchorBehaviour
// you should do the GetComponent already here
model = loadedAsset.GetComponent<AnchorBehavior>();

现在您已经有了 AnchorBehaviour 类型的引用,第二个 GetComponent 调用将是多余的,因为它 returns 是相同的引用。所以现在只用

public void create()
{
    planeFinder.AnchorStage = model;
}

如果你有这个脚本的多个实例,只加载一次 assetBundle 可能是有意义的,比如

private static bool alreadyLoading;
private static AssetBundle assetBundle;

void Start()
{
    // only load the bundle once
    if(!alreadyLoading) 
    {
        // set the flag to make sure this is never done again
        alreadyLoading = true;
        StartCoroutine(LoadAsset(nameOfAssetBundle, nameOfObjectToLoad));
    }
    else
    {
        LoadObjectFromBundle(nameOfOjectToLoad);
    }
} 

private IEnumerator LoadAsset(string assetBundleName, string objectNameToLoad)
{
    string filePath = System.IO.Path.Combine(Application.streamingAssetsPath, "AssetBundles");
    filePath = System.IO.Path.Combine(filePath, assetBundleName);

    var assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(filePath);
    yield return assetBundleCreateRequest;

    assetBundle = assetBundleCreateRequest.assetBundle;

    LoadObjectFromBundle(objectNameToLoad);
}

private IEnumerator LoadObjectFromBundle(string objectNameToLoad)
{
    AssetBundleRequest assetRequest = assetBundle.LoadAssetAsync<GameObject>(objectNameToLoad);
    yield return assetRequest;

    GameObject loadedAsset = (GameObject)assetRequest.asset;

    model = loadedAsset.GetComponent<AnchorBehavior>();
}