Multiple Asset Bundle Download Cache Problem - unity3d

I'm using UnityWebRequestAssetBundle.GetAssetBundle(url, bundleData.version, bundleData.crc); system and I can successfully download and use bundleAssets online. But when I want to download multiple bundle assets and save them to use offline for later I have a problem.
I have 2 files for example, 'A' and 'B'.
Case 1: When I download A and go offline I can load A anytime even I close the app. But when I want to download B and come back to A can't load A again because it deletes cache for some reason and tries to download it again.
Case 2: When I download A and B together and go offline, if I load B it loads. But if I try A it can't load and needs internet connection. After that when I try to load B again I loose package so it needs internet connection again.
So basicly I want to download multiple asset bundles and I want to use them whenever I want. How can I solve this problem? Thanks.
Code example:
using UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(url, bundleData.version, bundleData.crc);
yield return uwr.SendWebRequest();
bundle = DownloadHandlerAssetBundle.GetContent(uwr);
Debug.Log(bundle);
if (bundle==null)
{
Debug.Log("COULDN'T CONNECT TO URL");
callback(null);
}
else
{
Debug.Log("FOUND AT SEARCH!");
// Get downloaded asset bundle
bundle = DownloadHandlerAssetBundle.GetContent(uwr);
Sprite sprite = isDiff ? bundle.LoadAsset<Sprite>(levelText + "Diff") : bundle.LoadAsset<Sprite>(levelText);
callback(sprite);
}

I solved my issue by saving images to persistentFolder.
I write image to disk first by Texture2D.EncodeToJPG();
private void writeImageOnDisk(Sprite sprite, string fileName)
{
Texture2D texture = DeCompress(sprite.texture);
byte[] textureBytes = texture.EncodeToJPG();
File.WriteAllBytes(Application.persistentDataPath + "/" + fileName+".jpg", textureBytes);
Debug.Log("File Written On Disk!");
}
To EncodeToJPG image must be Read/Write enabled before having AssetBundles and you should decompress them first, I found solution from this topic.
private Texture2D DeCompress(Texture2D source)
{
RenderTexture renderTex = RenderTexture.GetTemporary(
source.width,
source.height,
0,
RenderTextureFormat.Default,
RenderTextureReadWrite.Linear);
Graphics.Blit(source, renderTex);
RenderTexture previous = RenderTexture.active;
RenderTexture.active = renderTex;
Texture2D readableText = new Texture2D(source.width, source.height);
readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
readableText.Apply();
RenderTexture.active = previous;
RenderTexture.ReleaseTemporary(renderTex);
return readableText;
}
And finally you can load image with this code:
private Sprite loadImageFromDisk(string fileName)
{
string dataPath = Application.persistentDataPath + "/" + fileName + ".jpg";
if (!File.Exists(dataPath))
{
Debug.LogError("File Does Not Exist!");
return null;
}
byte[] textureBytes = File.ReadAllBytes(dataPath);
Texture2D loadedTexture = new Texture2D(2048,2048,TextureFormat.ARGB32, false);
loadedTexture.LoadImage(textureBytes);
Sprite spr = Sprite.Create(loadedTexture, new Rect(0f, 0f, loadedTexture.width, loadedTexture.height), new Vector2(.5f,.5f), 2048); // 2048 is pixel per unit if you have a custom pixelPerUnit value you should set it here. Or you will see only some part of pixels of image.
spr.name = fileName;
return spr;
}
I hope this will work for you guys. This is how I store my images after downloading assetbundles and how I call them in game.

Related

Alternative to AssetDatabase.LoadAssetAtPath for any path?

Currently I am using the following piece of code to load Textures from image files.
Texture my_pic = (Texture) AssetDatabase.LoadAssetAtPath(path, typeof(Texture));
Unfortunately this method doesn't work if the target path isn't in the Asset/ folder. I was wondering how I would load an image, given some absolute path of the form
/Users/Alan/SomeFolder/SomePic.png
(note that I am currently writing a custom Unity editor plugin by extending EditorWindow if that matters)
AssetDatabase is an Editor-only class.
Furthermore, it can only read assets in the /Assets directory (you know, the ones that are known to the asset database).
If you want to read any file on the file system, you need to use the System.IO classes.
Here is some code that will open a unity file dialog, and load the selected texture into a material of the attached object.
string path = EditorUtility.OpenFilePanel("Load an image", "", "png");
if (string.IsNullOrEmpty(path)) {
return;
}
// Load the images bytes from file (this is a synchronous call!)
byte[] bytes = null;
try {
bytes = System.IO.File.ReadAllBytes(path);
} catch (System.Exception e) {
Debug.LogError(e.Message);
return;
}
// Load the bytes into a Unity Texture2D
Texture2D _tex = new Texture2D(2,2);
_tex.LoadImage(bytes);
// Apply this texture to the object
Renderer r = (target as Component).gameObject.GetComponent<Renderer>();
if (r != null) {
r.material.SetTexture("_MainTex", _tex);
}
The last part, just for demonstration, will work only on a script derived from Editor because it uses target to find the attached renderer. It's up to you to decide what to do with the texture in your EditorWindow script.
Also, remember to explicitly call DestroyImmediate on your texture when you no longer need it, as you may end up with a memory leak in your editor code.
You can use UnityWebRequestTexture
var www = UnityWebRequestTexture.GetTexture("file:///Users/Alan/SomeFolder/SomePic.png");
www.SendWebRequest();
while(!www.isDone)
continue;
var texture = DownloadHandlerTexture.GetContent(www);

Imaged loaded via new WWW doesn't display in a build, but it does in the player

I'm loading a .jpeg file in at runtime from disk, it works absolutely fine in the player but when I build the project the texture being loaded in via WWW doesn't display - no errors. It suggests a format issue, but like I said it renders as expected in the player:
Unit doc: https://docs.unity3d.com/ScriptReference/WWW.LoadImageIntoTexture.html
private IEnumerator SetMaterialImage(string path)
{
Texture2D tex;
tex = new Texture2D(4, 4, TextureFormat.RGB24, false, true);
WWW www = new WWW(path);
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log("ERROR REDNERING IMAGE --- " + www.error);
}
while (!www.isDone) yield return null;
if (www.isDone)
{
Debug.Log("WWW,ISDONE = TRUE");
Shader shader = Shader.Find("Standard");
www.LoadImageIntoTexture(tex);
GetComponent<Renderer>().material.mainTexture = tex;
GetComponent<Renderer>().material.shader = shader;
}
}
Edit: please don't suggest the Resources folder - this particular application must load from disk
I was unable to locate any Unity documentation that explains why this scenario differed between Player and Build, however I was able to confirm that:
GetComponent<Renderer>().material.mainTexture = tex;
Renderer>().material.shader = shader;
Should have been declared in the opposite order:
Renderer>().material.shader = shader;
GetComponent<Renderer>().material.mainTexture = tex;
Edit: Credit #Programmer 'The reason that code work in the Editor is because the Editor remembers which Shader or Texture is assigned to a material. So, when you change the shader, it uses the old texture'

Unity texture from disk has low resolution

I am trying to load a texture(and create a sprite from it eventually) from disk but sprite renders as low resolution image.
What I am doing:
-> Download the image from url. Once the image is downloaded, I save the texture as png to disk so that next time it doesn't requires a download.
WWW www = new WWW(url);
yield return www;
if (www.isDone)
{
if (string.IsNullOrEmpty(www.error))
{
Sprite img = Sprite.Create(www.texture, new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0, 0));
reward.RewardSprite = img;
byte[] bytes = www.texture.EncodeToPNG();
FileManager.SaveRewardImage(reward.rewardId, bytes);
}
else
{
Debug.Log(www.error);
}
}
-> Load from disk
string path = string.Format("Cache\\Venue\\{0}", nameWithoutExtension);
return Resources.Load<Texture2D>(path);
The first time when the texture loads from url, its resolution seems fine(because its the original one). When it loads from cache, it attenuates to a lower one.
Can someone tell me what am I missing, or even if there is way around it?
Thanks in advance.
You can overload your texture in your sprite creation in the same way that you do with the rectangle and specify in TextureFormat the format you need:
Sprite img = Sprite.Create(
new Texture2D (www.texture.width, www.texture.height, TextureFormat format, bool mipmap),
new Rect(0, 0, www.texture.width, www.texture.height), new Vector2(0, 0));

Save image fetched from server in unity 3d

I get an image from my server and I set it as a texture to a Gameobject. But what I also want to do is to save it in my project as a png. Any idea how am I supposed to do that ? I am new to unity.
Here is how I fetch my image and set it as a texture:
WWW wB = new WWW ("http://xxx.xxxx.xxxx");
yield return wB;
cube.GetComponent<Renderer>().material.mainTexture = wB.texture;
I would like to save wb.Texture as a png.
void SaveTextureToFile(Texture2D texture, string fileName)
{
var bytes=texture.EncodeToPNG();
var file = new File.Open(Application.dataPath + "/"+fileName, FileMode.Create);
var binary= new BinaryWriter(file);
binary.Write(bytes);
file.Close();
}
SaveTextureToFile(wB.texture, "abc.png");
Note that Application.dataPath can be replaced with Application.persistentDataPath.
WWW wB = new WWW ("http://xxx.xxxx.xxxx");
yield return wB;
byte[] bytes = wb.Texture.EncodeToPNG();
Now u could save bytes into a png file in your desired path.

Add remote image as texture during loop

I am very new to Unity3d.
I have a JSON array that contains the parameters of the prefabs I want to create at runtime.
I want to display images that are stored on my server in the scene.
I have a prefab "iAsset" that has a plane (mesh filter) and I want to load the image files as the texture of the plane.
I am able to Instatiate the prefabs however the prefab is showing up as a white square. This is my code:
for(var i = 0; i < bookData.Assets.Count; i++){
GameObject newAsset = null;
newAsset = (GameObject)Instantiate(iasset, new Vector3(2*i, 0, 0), Quaternion.identity);
if(!imageAssetRequested )
{
remoteImageAsset = new WWW(((BookAssets)bookData.Assets[i]).AssetContent);
imageAssetRequested = true;
}
if(remoteImageAsset.progress >=1)
{
if(remoteImageAsset.error == null)
{
loadingRemoteAsset = false;
newAsset.renderer.material.mainTexture = remoteImageAsset.texture;
}
}
}
the urls to the images on my server is retrieved from the JSON array:
((BookAssets)bookData.Assets[i]).AssetContent);
The code builds without any errors, I would very much appreciate any help to display the remote images.
You are not waiting for your downloads to complete.
The WWW class is asynchronous, and will commence the download. However, you need to either poll it (using the code you do have above) at a later time, or use a yield WWW in a CoRoutine that will block your execution (within that CoRoutine) until the download finishes (either successfully or due to failure).
Refer to Unity Documentation for WWW
Note however, that page sample code is wrong, and Start is not a CoRoutine / IEnumarator. Your code would look something like :
void Start()
{
... your code above ...
StartCoroutine(DownloadImage(bookData.Assets[i]).AssetContent, newAsset.renderer.material.mainTexture));
}
IEnumerator DownloadImage(string url, Texture tex)
{
WWW www = new WWW(url);
yield return www;
tex.LoadImage(www.bytes)
}