How to get hash from Unity AssetBundle to check cache? - c#

I am newbie in AssetBundle and try to learn about this. Currently, I can load the asset from the server, but I cannot get hash from the asset file itself. It's keeping return NullException.
I have tried follow the doc here:
https://docs.unity3d.com/ScriptReference/Caching.html
But still have no idea, and get the NullException at Hash too.
Found this as well and no luck:
How to get AssetBundleManifest from AssetBundle in Unity3D
Then I tried follow the instruction in this post:
Get Hash128 from AssetBundle for the Caching.IsVersionCached function
But still get NullException()
Here is the code I follow the latest link:
IEnumerator DownloadAssetBundle()
{
UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle("https://www.dropbox.com/s/66psd4aphlgpo2j/testasset?dl=1");
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(www);
AssetBundleRequest asset = bundle.LoadAssetAsync<AssetBundleManifest>("assetManifestName");
yield return asset;
//Get the AssetBundleManifest
AssetBundleManifest loadedAssetMf = asset.asset as AssetBundleManifest;
//Get Hash128 from the AssetBundleManifest
Hash128 tempHash128 = loadedAssetMf.GetAssetBundleHash("");
//Pass to the IsVersionCached function
Caching.IsVersionCached("https://www.dropbox.com/s/66psd4aphlgpo2j/testasset?dl=1", tempHash128);
//Instantiate(bundle.LoadAsset("RayBan_Sunglasses"));
}
}
Could anyone explain how to get the Hash from assetBundle what I have done wrong?
The file name of my Asset Bundle is "testasset"
If you please, specify where to put the "testasset", it would be a pleasure. Thank you so much for every answer.

the asset bundle manifest you need is stored at the platform bundle that has the same name as the folder that its inside according to this page,
first you need to load the asset bundle of the platform and then you get the the asset bundle manifest object from it, from that manifest you will be able to get the hash code
private IEnumerator LoadAssetBundleManifest(string uri)
{
using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(uri))
{
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.Log(uwr.error);
}
else
{
// Get downloaded asset bundle
bundleManifest = DownloadHandlerAssetBundle.GetContent(uwr);
AssetBundleManifest testManifst = bundleManifest.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
Debug.Log(testManifst.GetAssetBundleHash("bundlename").ToString());
}
}
}

Related

Unity Android load local MP3 or WAV as AudioClip

I'm able to load audio using UnityWebRequestMultimedia.GetAudioClip in the Unity Editor with the following code but when I run it on Android I am unable to load any files from the user's device.
void Start() {
audio = GetComponent<AudioSource>();
string fullPath = Path.Combine("file://" + previewSong);
StartCoroutine(GetAudioClip(fullPath));
}
IEnumerator GetAudioClip(string fullPath)
{
using (var uwr = UnityWebRequestMultimedia.GetAudioClip(fullPath, AudioType.MPEG))
{
((DownloadHandlerAudioClip)uwr.downloadHandler).streamAudio = true;
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
debugSongPath2.text = uwr.error;
yield break;
}
DownloadHandlerAudioClip dlHandler = (DownloadHandlerAudioClip)uwr.downloadHandler;
if (dlHandler.isDone)
{
audio.clip = dlHandler.audioClip;
if (audio.clip != null)
{
audio.clip = DownloadHandlerAudioClip.GetContent(uwr);
Debug.Log("Playing song using Audio Source!");
}
else
{
Debug.Log("Couldn't find a valid AudioClip.");
}
}
else
{
Debug.Log("The download process is not completely finished.");
}
}
}
The errors I experience vary depending on how I form the start of the URL.
Path.Combine("file:/" + previewSong);
malformed URL
Path.Combine("file://" + previewSong);
http:/1.1 404 not found
Path.Combine("file:///" + previewSong);
unknown error
Path.Combine("file:////" + previewSong);
unknown error
When I output the URLs on my phone they look correct. Here's an example path:
file:///storage/emulated/0/Music/Deorro/Deorro - Five Hours.mp3
I was previously loading audio from this URL successfully with WWW.GetAudioClip but that's obsolete. This leads me to believe the URL is correct. I've tried building with and without Development Mode. Not sure what else to try. I'd like to get this working with UnityWebRequestMultimedia but if there is an alternative, more effective way of loading local files I'm open to it.
Change your build settings to give write access to external sd card. I am using below code to get audio from specified location in android. I tried your code but somehow its not working.
IEnumerator GetAudioClip2(string fullPath)
{
debugSongPath2.text = previewSong;
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(fullPath, AudioType.MPEG))
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
AudioClip myClip = DownloadHandlerAudioClip.GetContent(www);
audioSource.clip = myClip;
audioSource.Play();
}
}
}
My full path was "file:///storage/emulated/0/Samsung/Music/Over the Horizon.mp3"
The issue was with the new Android 11 scoped storage which disables READ_EXTERNAL_STORAGE. I had to add the following to my AndroidManifest.xml:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage

Playing AudioClip fetched from Resources folder

I need to fetch the audioClip in the resources folder so as I understand I need to use Resources.Load<AudioClip>(path) or Resources.Load(path) as AudioClip but this just doesn't work, it doesn't even return a null it just stops the code
my code sample:
private void FetchAudioClipAndStartPlay(int userPos, int index)
{
AudioClip clip = Resources.Load($"Audio/Demo_ENG/D{userPos}a") as AudioClip;
Debug.Log("Starting Coroutine " + index);
StartCoroutine(PlayAudioClipAndStartRetrievingFromDatabase(index, clip));
}
IEnumerator PlayAudioClipAndStartRetrievingFromDatabase(int index, AudioClip clip)
{
Debug.Log("Starting to play " + index);
audioSource.PlayOneShot(clip, 1f);
yield return new WaitForSeconds(clip.length);
if (index < numberOfPlayers)
{
RetrieveFromDatabase(index++);
}
}
(it doesn't enter the coroutine)
All the audio files are .mp3
Any guidance and tips are very appreciated!
Unity does not support loading mp3 at runtime.
Unity does not recommend using the Resources folder:
Don't use it.
This strong recommendation is made for several reasons:
Use of the Resources folder makes fine-grained memory management more
difficult Improper use of Resources folders will increase application
startup time and the length of builds
As the number of Resources
folders increases, management of the Assets within those folders
becomes very difficult
The Resources system degrades a project's
ability to deliver custom content to specific platforms and
eliminates the possibility of incremental content upgrades
AssetBundle Variants are Unity's primary tool for adjusting content
on a per-device basis
Check this answer for a solution how to load audio files:
public void LoadSong()
{
StartCoroutine(LoadSongCoroutine());
}
IEnumerator LoadSongCoroutine()
{
string url = string.Format("file://{0}", path);
WWW www = new WWW(url);
yield return www;
song.clip = www.GetAudioClip(false, false);
songName = song.clip.name;
length = song.clip.length;
}

How To Load A Local File as an AssetBundle with WWW

So I have been trying to make it possible for users to load a .obj file and read it as an AssetBundle, but I can't figure it out.
I have figured out how to get the path of the file, but I can't load it as an asset bundle, it just returns null.
Here is my code :
WWW bundleRequest = new WWW(#"file://" + pathName);
while (!bundleRequest.isDone)
{
yield return null;
}
AssetBundle bundle = null;
if (bundleRequest.bytesDownloaded > 0)
{
AssetBundleCreateRequest myRequest = AssetBundle.LoadFromMemoryAsync(bundleRequest.bytes);
while (!myRequest.isDone)
{
Debug.Log("loading....");
yield return null;
}
if (myRequest.assetBundle != null)
{
bundle = myRequest.assetBundle;
GameObject model = null;
if (bundle != null)
{
AssetBundleRequest newRequest = bundle.LoadAssetAsync<GameObject>("Test");
while (!newRequest.isDone)
{
Debug.Log("loading ASSET....");
yield return null;
}
model = (GameObject)newRequest.asset;
bundle.Unload(false);
}
}
else
{
Debug.LogError("COULDN'T DOWNLOAD ASSET BUNDLE FROM URL");
}
}
else
{
Debug.LogError("COULDN'T DOWNLOAD ASSET BUNDLE FROM URL");
}
pathName here is: "C:\\Users\\mySuperCoolName\\OneDrive\\Documents\\Fun\\Programming\\Ungoing projects\\ThiefCop\\Unity Mobile\\Assets\\Prefabs\\TestOBJ.obj". Everything seems to work until AssetBundleCreateRequest when AssetBundle.LoadFromMemoryAsync() is called, where myRequest.assetBundle == null even if the file was downloaded correctly.
I also get an error which probably is linked with my problem : I have searched for what it meant but I couldn't find...
It is really hard to explain what I mean, but I really hope you can find an answer to this, I've been searching for hours and between us, I don't understand much of File loadind and Reading... Don't hesitate to ask if you didn't understand my bad english... Thank you in advance :)
https://docs.unity3d.com/ScriptReference/BuildPipeline.BuildAssetBundles.html
To be short :
Import your object in Unity
Give it a AssetBundle name (click on it then on bottom of the inspector view)
Call this function
for load obj files directly to you project, you need todo code what unpacks object file and then converts it to the Unity engine.
in assert store look at the Runtime OBJ Importer,
this should be right direction for your issue.
https://assetstore.unity.com/packages/tools/modeling/runtime-obj-importer-49547

Unity Asset Bundles

I am creating an editor for a game using Unity game engine and I want to allow a designer to make new spells. What I am trying to achieve is that a designer will download a Unity particle system (or GameObject), save it on the computer and will give the game full path to it. Then the game should load the object and use it as a particle for a new spell.
I need to be able to load a Unity GameObject (particle system) during runtime from a folder. I tried it with Asset Bundles, but I don’t know how to use Asset Bundles when I only want to download the file from the computer (local file system) not from the server. Asset bundles work perfectly in Debug but not when I build and run it (It tries to connect to some server instead).
I use the default script for Asset Bundles:
public class LoadAssets : MonoBehaviour
public const string AssetBundlesOutputPath = "/AssetBundles/";
public string assetBundleName;
public string assetName;
public GameObject loadedObject;
public bool finished;
public MessageWindow messWindow;
// Use this for initialization
//originally was start here might create problems
public IEnumerator Begin ()
{
messWindow.changeMessageAndShow("Asset " + assetName + " is loading.","Loading");
yield return StartCoroutine(Initialize() );
// Load asset.
yield return StartCoroutine(InstantiateGameObjectAsync (assetBundleName, assetName) );
finished=true;
messWindow.Close();
}
public GameObject LoadObject()
{
StartCoroutine(Begin());
//TODO wait for end of couroutine
return loadedObject;
}
// Initialize the downloading url and AssetBundleManifest object.
protected IEnumerator Initialize()
{
// Don't destroy this gameObject as we depend on it to run the loading script.
DontDestroyOnLoad(gameObject);
// With this code, when in-editor or using a development builds: Always use the AssetBundle Server
// (This is very dependent on the production workflow of the project.
// Another approach would be to make this configurable in the standalone player.)
#if DEVELOPMENT_BUILD || UNITY_EDITOR
AssetBundleManager.SetDevelopmentAssetBundleServer ();
#else
// Use the following code if AssetBundles are embedded in the project for example via StreamingAssets folder etc:
// Or customize the URL based on your deployment or configuration
//AssetBundleManager.SetSourceAssetBundleURL("http://www.MyWebsite/MyAssetBundles");
#endif
AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath + "/");
// Initialize AssetBundleManifest which loads the AssetBundleManifest object.
var request = AssetBundleManager.Initialize();
if (request != null)
yield return StartCoroutine(request);
}
protected IEnumerator InstantiateGameObjectAsync (string assetBundleName, string assetName)
{
// This is simply to get the elapsed time for this phase of AssetLoading.
float startTime = Time.realtimeSinceStartup;
// Load asset from assetBundle.
AssetBundleLoadAssetOperation request = AssetBundleManager.LoadAssetAsync(assetBundleName, assetName, typeof(GameObject) );
if (request == null)
yield break;
yield return StartCoroutine(request);
// Get the asset.
GameObject prefab = request.GetAsset<GameObject> ();
if (prefab != null)
{
loadedObject = GameObject.Instantiate(prefab);
DontDestroyOnLoad(loadedObject);
}
// Calculate and display the elapsed time.
float elapsedTime = Time.realtimeSinceStartup - startTime;
Debug.Log(assetName + (prefab == null ? " was not" : " was")+ " loaded successfully in " + elapsedTime + " seconds" );
}
}
Thank you so much.
To load Asset bundle from local system you need Local AssetBundle Server. When Local Asset Server is enabled, AssetBundles must be built and placed in a folder explicitly called AssetBundles in the root of the Project, which is on the same level as the Assets folder.
The built AssetBundles will be available to the Editor and all builds running locally that can reach the Editor on the local network.
Use Assets>AssetBundles>Local AssetBundle Server to enable the Local AssetBundle Server.
Reference to asset bundles and asset bundle manager.

Cannot load cached AssetBundle. A file of the same name is already loaded from another AssetBundle

I am trying to load a scene but I am getting the error from title and I simply don't know why because I call Unload(false) on AssetBundle. Can someone help me? Thanks.
void Start() {
...
StartCoroutine (DownloadAndCache());
...
}
IEnumerator DownloadAndCache (){
// Wait for the Caching system to be ready
while (!Caching.ready)
yield return null;
// Load the AssetBundle file from Cache if it exists with the same version or download and store it in the cache
using(WWW www = WWW.LoadFromCacheOrDownload (BundleURL, version)){
yield return www;
if (www.error != null)
throw new Exception("WWW download had an error:" + www.error);
AssetBundle bundle = www.assetBundle;
bundle.LoadAll();
AsyncOperation async = Application.LoadLevelAsync("main");
Debug.Log (async.progress);
yield return async;
bundle.Unload(false);
}
}
if you do not want to use Unload() function clear the cache after asset bundle is used:-
Caching.CleanCache();
And every things will work fine but you have to download the asset bundle every time as you clearing out the cache after bundle use.
Or you can do this way
First of all call DoNotDestroyOnLoad() (for keeping the reference through out) function in Start() function and make a static variable to store a reference of the asset bundle when you use WWW.LoadFromCacheOrDownload to download the asset,assign the reference to the static variable and unload the asset after its use. if you don not unload the asset bundle and use WWW.LoadFromCacheOrDownload again the same error will be thrown as you have stated.
Suppose if you loaded a scene using asset bundle then before quitting the scene unload the asset bundle reference stored in that static variable. That is why static variable is used so that we can access it from any script and unload it whenever we want also be careful about version.
class LoadScene:MonoBehaviour{
***public static AssetBundle refrenceOfAsset;***
private AssetBundle assetBundle;
void Start(){
***DoNotDestroyOnLoad(gameObject);*** }
protected IEnumerator LoadTheScene()
{
if (!Caching.IsVersionCached(url, version)){
WWW www = WWW.LoadFromCacheOrDownload(url, version);
yeild return www; assetBundle = www.assetBundle;
***refrenceOfAsset = assetBundle;***
www.Dispose();
// Do what ever you want to do with the asset bundle but do not for get to unload it
refrenceOfAsset.Unload(true);
}
}
else{
Debug.Log("Asset Already Cached...");
if(refrenceOfAsset!=null)
refrenceOfAsset.Unload(true);
}
}
or
or you can visit unity http://docs.unity3d.com/Manual/keepingtrackofloadedassetbundles.html

Categories

Resources