I am having issue with the asset bundle from unity. I have a bundle with multiple assets in it with the same name, but different extension, like bonus.png bonus.prefab.
When I try to instanciate a prefab named bonus, the assetBundle.LoadAssets("bonus") function doesnt return me a gameobject like it usuallly do. This seems to be happening only if i have multiple asset with the same name.
AssetBundleRequest request = assetBundle.LoadAssetAsync(m_ObjectsToPool[i].m_Name, typeof(GameObject));
yield return request;
GameObject prefab = request.asset as GameObject;
if (prefab != null)
{
PoolManager.Instance.AddLoadedPrefabToPool(prefab, m_ObjectsToPool[i].m_Name, null, m_ObjectsToPool[i].m_Amount);
}
But if i call loadallassets(typeof(GameObject)) and then use a for loop, i can find my asset and instanciante it properly. but that is just a dirty way of doing it.
You should be able to load assets of different types with the same name.
What version of Unity are you using?
Your code above looks correct (except for the typo calling it LoadAssetAsync?), and we don't know for sure it is failing to enter the if() statement?
The following worked for me on Unity 4.5.5f1
Added ExportAssetBundles.cs context menu script to my Unity build from Unity Docs
Created something to export
found a JPG dragged it in and named it "marble.jpg"
created a cube, named it "marble" and dragged it into project to create a prefab
created a material, named it "marble", added the jpg and dragged it onto the cube, pressed apply.
Hilighted all 3 and exported asset bundle using "Build Assets From Selection - Track Selection" and saved it to "c:\temp\marble.unity3d"
Added a script to load that asset bundle on start (similiar to yours):
using UnityEngine;
using System.Collections;
public class BundleTest : MonoBehaviour {
void Start () {
StartCoroutine(StartCo());
}
IEnumerator StartCo() {
string url = "file://c:\\temp\\marble.unity3d";
WWW www = new WWW(url);
yield return www;
AssetBundle bundle = www.assetBundle;
AssetBundleRequest request = bundle.LoadAsync ("marble", typeof(GameObject));
yield return request;
Debug.Log ("loaded asset: " + request.asset.ToString());
GameObject prefab = request.asset as GameObject;
GameObject go = (GameObject)Instantiate(prefab);
bundle.Unload(false);
www.Dispose();
}
}
Ran Unity and verified I see the game object correctly.
It used to work when I was on unity 4.6, but I am now using the beta 5.0.17. And no its not a typo, it is really laodassetasync I have, I also tried with loadasset only with the same result. Right now I am using
var allAssets = assetBundle.LoadAllAssets(typeof(GameObject));
for (int i = 0; i < m_ObjectsToPool.Count; i++)
{
bool found = false;
for (int j = 0; j < allAssets.Length; j++)
{
if (allAssets[j].name == m_ObjectsToPool[i].m_Name)
{
PoolManager.Instance.AddLoadedPrefabToPool((GameObject)allAssets[j], m_ObjectsToPool[i].m_Name, null, m_ObjectsToPool[i].m_Amount);
found = true;
break;
}
}
if (!found)
{
Debug.Log(string.Format("{0} object could not be found in assetbundle", m_ObjectsToPool[i].m_Name));
}
}
and it is working, but i dont really like this way of doing it.
Having same issue with Unity 5.1, I've found this solution:
Besides refering asset by base name (no directory, no extension parts of path), you can also use full relative path (including file extension, relative to project root).
Let's say I've got bundle created from directory Assets/Bundle, it contains following files:
Prefabs/Box.prefab
Prefabs/Box2.prefab
Models/Box.fbx
Materials/Box.mat
Textures/Box.png
So the documented way of loading prefab that fails in our cases is
// May return nulll
bundle.LoadAsset<GameObject>("Box")
And this is the not documented way that's working correctly in our case
bundle.LoadAsset<GameObject>("assets/bundle/prefabs/box.prefab")
If you are not sure what is the correct asset name you should use for LoadAsset, you can use following code to list all asset names in bundle
string[] assets = bundle.GetAllAssetNames();
foreach (string asset in assets)
{
Debug.Log(asset);
}
Related
I've a game. I should save the game whenever end user exit.
how to know where to save? because If I save the game to the under C://Users/.. path what happens if user uses Android or IOS? what is the solution for this problem?
You should use Application.persistentDataPath method.
This value is a directory path where you can store data that you want
to be kept between runs. When you publish on iOS and Android,
persistentDataPath points to a public directory on the device. Files
in this location are not erased by app updates. The files can still be
erased by users directly.
When you build the Unity application, a GUID is generated that is
based on the Bundle Identifier. This GUID is part of
persistentDataPath. If you keep the same Bundle Identifier in future
versions, the application keeps accessing the same location on every
update.
using UnityEngine;
public class Info : MonoBehaviour
{
void Start()
{
Debug.Log(Application.persistentDataPath);
}
}
Use application data folder which changes depending on platform.
//Attach this script to a GameObject
//This script outputs the Application’s path to the Console
//Run this on the target device to find the application data path for the platform
using UnityEngine;
public class Example : MonoBehaviour
{
string m_Path;
void Start()
{
//Get the path of the Game data folder
m_Path = Application.dataPath;
//Output the Game data path to the console
Debug.Log("dataPath : " + m_Path);
}
}
Read more about it here at source
There is structure or class and on it base will be better to creating objects and writing its in file.
[System.Serialisable]
public class AAA{
public string Name = "";
public int iNum = 0;
public double dNum = 0.0;
public float fNum = 0f;
int[] array;
// Here may be any types of datas and arrays and lists too.
}
AAA aaa = new AAA(); // Creating object which will be serialis of datas.
// Initialisation our object:
aaa.Name = "sdfsf";
aaa.iNum = 100;
string path = "Record.txt"
#if UNITY_ANDROID && !UNITY_EDITOR
path = Path.Combine(Application.persistentDataPath, path);
#else
path = Path.Combine(Application.dataPath, path);
#endif
// Writing datas in file - format JSON:
string toJson = JsonUtility.ToJson(aaa, true);
File.WriteAllText(path, toJson);
// reading datas from file with JSON architecture and serialis.. it in object:
string fromjson = File.ReadAllText(path);
AAA result = JsonUtility.FromJson<AAA>(fromjson);
I'll start with the errors themselves. The first is : Assertion failed on expression: 'm_UncompressedBlocksOffsets[blockInd] <= from + dstPos && m_UncompressedBlocksOffsets[blockInd] + uncompressedBlockSize >= from + dstPos' UnityEngine.AssetBundle:LoadFromFile(String)
Followed by : Failed to decompress data for the AssetBundle 'D:/Unity/projects/PQv0.10/Assets/AssetBundles/spritesbundle'. UnityEngine.AssetBundle:LoadFromFile(String)
I've always just used the resources folder for my projects but my current project has outgrown what that method can accommodate. So I'm trying to offload assets (in this case 5000 or so sprites)into an assetbundle and load them as needed at runtime. I figured I could just get a reference to the assetbundle and then call assetbundle.LoadAsset(assetname), substituting my old Resources.Load calls. Here's my attempt at making the assetbundle reference that keeps erroring:
AssetBundle sprites;
void loadSprites()
{
sprites = AssetBundle.LoadFromFile(Application.dataPath + "/AssetBundles/spritesbundle"); // " + bundleName);
if (!sprites) {
Debug.Log("failed to load assetbundle");
}
}
For due diligence, here's also the code I used to create that assetbundle. It simple searches my project for png's and packs them into the assetbundle:
public static void SaveSpecificAssets()
{
AssetBundleBuild build = new AssetBundleBuild();
build.assetBundleName = "spritesBundle";
string[] files = AssetDatabase.GetAllAssetPaths();
List<string> validFiles = new List<string>();
foreach (string file in files)
{
if (file.EndsWith(".png")) validFiles.Add(file);
}
build.assetNames = validFiles.ToArray();
BuildPipeline.BuildAssetBundles("Assets/AssetBundles", new AssetBundleBuild[1] { build }, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
}
The assetbundle builds without errors and appears in the directory where it should be. I have no indication that it's faulty other than the errors I get when trying to load it. The manifest it generates contains appropriate items so I think it's working as intended at least.If I'm missing any relevant information, please point it out and I'll try to provide it.
this is the test coroutine I used in place of loadSprites() for testing if it made a difference:
IEnumerator testLoader()
{
WWW www = WWW.LoadFromCacheOrDownload("file:///" + Application.dataPath + "/AssetBundles/spritesbundle", 1);
yield return www;
sprites = www.assetBundle;
}
Try this, for all the PNG's in your project folder, select them all and add them to an AssetBundle via the Editor, Then just build using
BuildPipeline.BuildAssetBundles("Assets/AssetBundles",BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
Then try to load the bundles and see if it shows an error.
Also, look into the Manifest to see if all the files you intended are listed
I need to stream or download and play an audio getted from an URL in Unity3D, running on iOS.
The Audio comes from a text-to-audio service and I need to play it on Unity:
http://api.ispeech.org/api/rest?apikey=...&action=convert&voice=eurspanishfemale&text=hola+que+tal
I've been googling all the morning and not found a valid solution...
There is a code snippet in the Unity3D documentation (WWW-audioClip,WWW.GetAudioClip), but is not working, I have debugged and the error says it couldn't open the file.
using UnityEngine;
using System.Collections;
public class AudioURLScript : MonoBehaviour {
public string url = "http://api.ispeech.org/api/rest?apikey=...&action=convert&voice=eurspanishfemale&text=hola+que+tal";
public AudioSource source;
void Start() {
WWW www = new WWW("file://"+url);
source = GetComponent<AudioSource>();
source.clip = www.GetAudioClip(false,true);
}
void Update() {
if (!source.isPlaying && source.clip.isReadyToPlay)
source.Play();
}
}
Thanks
SOLUTION
This is my working solution right now.
void Start(){
StartCoroutine(DownloadAndPlay("http://api.ispeech.org/api/rest?apikey=...&action=convert&voice=eurspanishfemale&text=Hola+que+tal"));
}
IEnumerator DownloadAndPlay(string url)
{
WWW www = new WWW(url);
yield return www;
AudioSource audio = GetComponent<AudioSource>();
audio.clip = www.GetAudioClip(false, true,AudioType.MPEG);
audio.Play();
}
You don't mention the platform you are on, so I'm going to assume Windows.
Unity Windows runtime only supports WAV or OGG. The link to the audio service file you provided is downloading as a MP2 Audio File (common in broadcasting). Unity will not be able to play that (or MP3).
For reference, Android and iOS platforms do support MP3 (but not MP2).
So, you're first issue is to make sure your audio source is in compatible format.
The code sample is incorrect for 3 reasons;
the URL is https: (which tells unity to download from the internet) but then you pre-pend file: to it (which tells unity to load from the local file system). So you should pick one or the other.
it wouldn't work if you picked https because that particular link (I know it's just an example) but it requires a user to be logged in to the service (and using cookies to know that), so it doesn't send you an audio file, it sends you a HTML page telling the user to login or register.
As #fafase says, the WWW must be placed within a co-routine, so that it can download over multiple frames.
OK, here's what I suggest.
If you can know the audio files a head of time, download them and transcode to OGG (if windows) or MP3 (if mobile) and upload them to your own server (say Amazon S3, or a $10 a month unlimited website).
Then, use this code to download and play it:
StartCoroutine(DownloadAndPlay("http://myserver.com/audio/test.ogg"));
IEnumerator DownloadAndPlay(string url)
{
WWW www = new WWW(url);
yield return www;
AudioSource audio = GetComponent<AudioSource>();
audio.clip = www.GetAudioClip(false, false);
audio.Play();
}
A WWW object is a wrapper for a HTTP request, containing the creation of the connection, the transfer of the data and the closing of the connection (and some extra actions).
This does not happen in one frame and requires a coroutine.
void Start() {
StartCoroutine(GetAudio(url));
}
private IEnumerator GetAudio(string url)
{
WWW www = new WWW("file://"+url);
yield return www;
if(string.IsNullOrEmpty(www.error) == false)
{
Debug.Log("Did not work");
yield break;
}
source = GetComponent<AudioSource>();
source.clip = www.GetAudioClip(false,true);
}
The WWW package got deprecated and Unity3D recommended using the UnityWebRequest package for API communications.
Try the following code snippet to download audio from a URL and play it using the AudioSource gameObject.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class DownloadAndPlay : MonoBehaviour
{
public AudioSource audioSource;
// Start is called before the first frame update
void Start()
{
string URL = "http://api.ispeech.org/api/rest?apikey=...&action=convert&voice=eurspanishfemale&text=hola+que+tal";
StartCoroutine(DownloadAudio(URL));
}
// Update is called once per frame
void Update()
{
}
IEnumerator DownloadAudio(string URL) {
using (UnityWebRequest www_audio = UnityWebRequestMultimedia.GetAudioClip(URL, AudioType.MPEG)) {
yield return www_audio.SendWebRequest();
if (www_audio.isNetworkError) {
Debug.Log(www_audio.error);
} else {
AudioClip clip = DownloadHandlerAudioClip.GetContent(www_audio);
audioSource.clip = clip;
audioSource.Play();
}
}
}
}
For more: https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.html
I'm adding a custom create action for a ScriptableObject like this:
[MenuItem("Assets/Create/FlowEngine/Character")]
private static void CreateCharacter()
{
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
string assetPath = path + "/New Character.asset";
FlowEngineCharacterData character = ScriptableObject.CreateInstance<FlowEngineCharacterData>();
AssetDatabase.CreateAsset(character, assetPath);
AssetDatabase.SaveAssets();
Selection.activeObject = character;
}
This works exactly as expected, but there is one difference between this and all of the built in Assets. When you create those it automatically starts in rename 'mode'.
The only thing I can find to do with renaming the assets is a method which directly renames the asset rather than starting the UI rename operation.
Is there any way I can replicate this behavior?
I've found an answer for this tucked away in one of the other namespaces:
[MenuItem("Assets/Create/FlowEngine/Character")]
private static void CreateCharacter()
{
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
string assetPath = path + "/New Character.asset";
FlowEngineCharacterData character = ScriptableObject.CreateInstance<FlowEngineCharacterData>();
ProjectWindowUtil.CreateAsset(character, assetPath);
}
Note that is ProjectWindowUtil rather than AssetDatabase. Using that starts the create operation off in 'rename mode'.
I'm using Unity 5 and C# and I would like to load a movie from anywhere on disk at run-time rather than from the Resources folder.
This is possible to do with regular image textures using the .LoadImage (bytes) method in Texture2D type for example, but there does not appear to be an equivalent for MovieTexture.
The code shows how to do it from the resources folder, but how could i modify it to load from disk?
Thanks
public void loadVideo (GameObject container, string video)
{
// Load MovieTexture from Resources Folder
MovieTexture mat = (MovieTexture)Resources.Load(Path.GetFileNameWithoutExtension(video), typeof(MovieTexture));
MeshRenderer ren = container.GetComponent<MeshRenderer>();
ren.material.mainTexture = mat;
mat.Play();
}
You can. Use the WWW class to to this.
IEnumerator loadAndPlay(){
WWW diskMovieDir = new WWW("file:///C:/yourvideoDir/video.mp4");
//Wait for file finish loading
while(!diskMovieDir.isDone){
yield return null;
}
//Save the loaded movie from WWW to movetexture
MovieTexture movieToPlay = diskMovieDir.movie;
//Hook the movie texture to the current renderer
MeshRenderer ren = container.GetComponent<MeshRenderer>();
ren.material.mainTexture = movieToPlay ;
movieToPlay.play();
}
Now you can call it by starting coroutine.
StartCoroutine(loadAndPlay());
I did not compile this so there might be a compile error but you can easily fix it if there is one. This should also work without problems if it compiles. Note that the url must start with file:///. If that didnt work, try file://