问题
The goal is a two step process in which a Prefab of a Vuforia GroundPlane object is loaded from an AssetBundle, and passed to the AnchorBehavior variable declaration in order to place the gameObject.
And my apologies if as a newcomer to Unity C# i am not as accurate as i would like
Tried various approaches to equate the loaded Prefab to the AnchorBehavior. But because these are two types of objects, errors occur indicating these can't be implicitly equal
The declarations are the following:
public PlaneFinderBehaviour plane;
public ContentPositioningBehaviour planeFinder;
public AnchorBehaviour model;
public string nameOfAssetBundle;
public string nameOfObjectToLoad;
Idea was to pass the "nameOfObjectToLoad" representing the Prefab and pass it to the "AnchorBehavior" value, then the following method could be used "onClick", when the script is attached to a button.
public void create()
{
planeFinder.AnchorStage = model.GetComponent<AnchorBehaviour>();
}
The expectation was that the Prefab would be passed to the AnchorBehavior and instantiate the Prefab "onClick"
Here is the full script from which these snippets were extracted.
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>();
}
}
Further research exposes my novice skills. The final intent, of "model = loadedAsset;" is that i am attempting to directly convert from one data type to another, which can't be done explicitly. But thus far, my research has not found a means to take the loaded Prefab from the AssetBundle and feed it to the AnchorBehaviour variable.
If anyone has any experience with approaches to this kind of conversion between data types, your guidance is very much appreciated.
UPDATE By casting the declaration correctly, the conversion error was eliminated.
model = (asset.asset as AnchorBehaviour);
but now i have a the NullReference error, indicating that i have failed to declare the value correctly, in this line
{
planeFinder.AnchorStage = model.GetComponent<AnchorBehaviour>();
}
And this now is my new dilemma, as i am not sure where i have failed to declare the variable correctly.
UPDATE This error is resolved when the AnchorBehaviour variable is set as private. So, now the script compiles, but fails to produce the intended results. This likely points to a need to change how to load the AssetBundle component as described in the IEnumerator section. The Unity Console prints out the following logging comment
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)
After all the advice, here is the script to-date, which unfortunately does not place the content from the AssetBundle. More research and testing on my part i can see.
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;
}
}
After adding the if statement for the asset, which compiles fine, i continue to have the console alert that the "model" (AnchorBehavior/Anchor Stage) field needs to have a value. So it seems that what the script is either not passing the AssetBundle Object declared in the "nameOfObjectToLoad" field, or what is passing is not matching. So for the same of explanation, i temporarily made the "model" field public and manually populated the field. You will see it identifies the Prefab as being a "AnchorBehavior" object. Button Values in Editor - Desired Outcome
Here is the full error in the Editor Console when attempting to place the object.
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()
In an effort to further debug the issue, i ammended the following section to this.
if (assetBundle == null)
{
Debug.Log("Failed to Load assetBundle!!");
yield break;
}
The intent was to an attempt to identify if the AssetBundle was in fact being loaded. This was very useful, but it produced a very odd result for which i would solicit advise.
This script is attached to a series of buttons so that when the button is clicked, the create() function is used to instantiate the Prefab based on the variables.
These buttons are grouped on 3 different UI panels. The user clicks on the selected Panel button to expose the desire button panel.
What is very strange is that when the user clicks on the Panel Selection Button, the following errors are placed in the Editor Log. This is before the actual button which has the attached script has been clicked.
On Panel Button Click
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)
On Button Click (script attached button)
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()
I do not understand why the script is being called upon click of Panel Button, which does not have the script attached.
Any advise is most appreciated on this puzzling issue.
回答1:
Checkout 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.
You can't simply typecast the retrieved GameObject
reference to a AnchorBehaviour
reference. Instead you have to use GetComponent.
So what you should do is
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>();
Now that you already have a reference of type AnchorBehaviour
the second GetComponent
call would be redundant since it returns the same reference. So now only use
public void create()
{
planeFinder.AnchorStage = model;
}
In case you have multiple instances of this script it might make sense to load the assetBundle only once like
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>();
}
来源:https://stackoverflow.com/questions/57368753/can-a-prefab-loaded-from-asssetbundle-be-passed-to-vuforia-anchorbehavior-declar