Loading assets from Asset Bundles for Play Asset Delivery in Unity - c#

I am currently maintaining an old project which I would very much like to keep the old code and avoid refactoring of the old code if it's possible. To upload it to google play store, I have used play asset delivery PAD system which is requiring asset bundle system.
I am able to successfully loading asset bundles in an asynchronous way with coroutines following the tutorials.
My question is that; is it possible to access bundled assets without changing original way of accessing such as
UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/Prefabs/Game/Tiles/Tile.prefab", typeof(Tile)) as Tile;
instead of implementing the following code?
var assets = AssetDatabase.FindAssets("t:Sprite",
new[] {"Assets/Images"});
foreach (var guid in assets)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
Sprite loadedSprite = null;
if (imageDictionary.TryGetValue(assetPath, out loadedSprite)
== false)
{
loadedSprite = AssetDatabase.LoadAssetAtPath<Sprite>(
assetPath);
if (loadedSprite != null)
{
imageDictionary.Add(assetPath, loadedSprite);
AddImageToList(assetPath, loadedSprite);
}
}
}
I am currently using 2 asset bundles consists of texture and spine animation folders.I have created 2 asset bundles which are texture folder (110 MB) and spine animation bundle (23 MB) and base asset. When I created the aab file, file size increased to 370 MB.
(I know that I need to split my bundles to match play store requirement of 150 MB for base assets but this is a different issue.)
And also, my educated guess is that, unity is not only adding asset bundles but also adding assets in old fashioned way which is an issue might relate to this question.

I was also facing the same problem.
The initially built AAB file exceeded 150MB and could not be uploaded to the store.
But I found a way to upload.
order
Put the build scene in "File - BuildSetting - Scene In Build".
Check "Build App Bundle" in "File - BuildSetting".
Close BuildSetting and click "Google - Build Android App Bundle"
Build.
(In this process, I did not create an AssetBundle. I used the original program as it was)
Ignore the large size alarm in the middle of the build.
When the build is completed, the size will be larger than 150MB.
Don't worry about the size of the build file and upload it to the Google console.
"Google - Build Android App Bundle" The file built with this goes up.
I hope the problem is resolved.

#Alp, Google's PAD tutorial is quite limited and it only guesses you're trying to access textures, which might not be always the case.
You can actually access the bundles directly, and load them into memory, and if you've packed your prefab as an assetBundle, when you load your assetBundle this way you can just cast it to a GameObject later and then use the prefab as a GameObject to instantiate it or do whatever you need.
There is only one official way of loading asset bundles if you packed them as install-time,
public static IEnumerator LoadAssetBundleFromMemoryAsync(string packName, string assetName, Action<AssetBundle> callback)
{
var packRequest = PlayAssetDelivery.RetrieveAssetPackAsync(packName);
while (!packRequest.IsDone)
{
yield return null;
}
AssetLocation location = packRequest.GetAssetLocation(assetName);
long size = (long)location.Size;
long offset = (long)location.Offset;
using (Stream stream = File.OpenRead(location.Path))
{
byte[] bytes = new byte[size];
stream.Seek(offset, SeekOrigin.Begin);
stream.Read(bytes, 0, bytes.Length);
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(bytes);
yield return request;
callback(request.assetBundle);
}
}
And then, I also figured out an unofficial way of loading asset bundles directly from file. It turns out that the location we get for a given assetBundle is going to be inside the split apk module with the same name as the assetPack.
But if we just replace the split_pack.apk with base.apk, we can actually read it directly from the File without needing to actually pre-load the bundle into memory. Load from memory will put it into memory in advance, while using the LoadFromFile way, you can actually load the bundle to memory when it's needed.
Something like this:
public static IEnumerator LoadAssetBundleFromFileAsync (string packName, string assetName, Action<AssetBundle> callback)
{
var packRequest = PlayAssetDelivery.RetrieveAssetPackAsync(packName);
while (!packRequest.IsDone)
{
yield return null;
}
AssetLocation location = packRequest.GetAssetLocation(assetName);
string path = location.Path.Replace($"split_{packName}.apk", "base.apk");
string basePath = $"{path}!assets/assetpack/{assetName}";
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(basePath);
while (!request.isDone)
{
yield return null;
}
callback(request.assetBundle);
}
You could also maybe only return the 'assetPath' and load it later.
These are only for install-time asset packs.
If you're trying to load any file from a fast-follow or on-demand asset pack, then all you need is the location.Path to load it from, as a raw file.
public static IEnumerator GetAssetLocationFromAssetPack (string packName, string assetName, Action<string> callback)
{
PlayAssetPackRequest packRequest = PlayAssetDelivery.RetrieveAssetPackAsync(packName);
while (!packRequest.IsDone)
{
yield return null;
}
AssetLocation location = packRequest.GetAssetLocation(assetName);
callback(location.Path);
}
I hope it helps, and be wary that any folder or assets contained in your StreamingAssets if also set to be in an assetPack will be added in double, everything contained in StreamingAssets will be in your base.apk (within the AAB), so if you have something from StreamingAssets that will actually be loaded from a pack, delete it from Streaming to avoid an AAB bigger than expected.

Related

How to create .webp image file from url in unity?

I am developing app for Android, iOS.
I should download webp file from url to unity.
private IEnumerator StartWebPExample()
{
var lWebStream = new WWW(#"http://cdn.octo-dev.co.uk/octo/image01.webp");
yield return lWebStream;
Error lError;
Texture2D lTexture2D = Texture2DExt.CreateTexture2DFromWebP(lWebStream.bytes, true, true, out lError);
File.WriteAllBytes(Application.persistentDataPath + fileName, textureBytes);
Debug.Log("File Written On Disk!");
}
I tried for this operation but got this issue.
Assuming you are using this the CreateTexture2DFromWebP for some reason uses
lTexture2D.Apply(lMipmaps, true);
where Texture2D.Apply
makeNoLongerReadable: When set to true, Unity discards the copy of pixel data in CPU-addressable memory after this operation.
=> I would file this as a bug to the devs (I actually did - last maintenance is quite a while ago though - maybe one of the forks is actively being maintained) and for now modify the local source code to include a flag as parameter.

Application not saving png file on phone - unity

I am making a camera app on unity for learning purpose. I followed two tutorial and got my cam running Now here is a code that I found on stack overflow, The issue is its not saving the output on phone.
public void Pic()
{
StartCoroutine(TakePhoto());
}
IEnumerator TakePhoto() // Start this Coroutine on some button click
{
// NOTE - you almost certainly have to do this here:
yield return new WaitForEndOfFrame();
Texture2D photo = new Texture2D(backCam.width, backCam.height);
photo.SetPixels(backCam.GetPixels());
photo.Apply();
//Encode to a PNG
byte[] bytes = photo.EncodeToPNG();
//Write out the PNG. Of course you have to substitute your_path for something sensible
File.WriteAllBytes(Application.dataPath + "photo.png", bytes);
}
I have attached Pic() to a button.
I am just learning these so might be making some stupid mistake.
It needs to be /photo.png, otherwise you are just appending a filename to foldername
You should also consider switching to persistentDataPath and validating it for file io (checking if it exists is a good practice)
For Paths you should always use Path.Combine instead of direct string concatenation
Path.Combine(Application.dataPath, "photo.png")
It is also possible that the folder or file simply don't exist yet so you could check that and create them in that case before writing
var filePath = Path.Combine(Application.dataPath, "photo.png");
if (!Directory.Exists(Application.dataPath))
{
Directory.CreateDirectory(Application.dataPath);
}
if (!File.Exists(filePath))
{
File.Create(filePath);
}
//Write out the PNG. Of course you have to substitute your_path for something sensible
File.WriteAllBytes(filePath, bytes);
You need to add a slash before your file name.
File.WriteAllBytes($"{Application.dataPath}/photo.png", bytes);

Reliable image handling on Azure platform / Dotnet core

I'm struggling a little with images on the Azure platform under dotnet core and I'm hoping someone can make a sensible suggestion.
Simple enough premise: user uploads image, saved in a database as base64 (about to move to Azure storage blob, but that's irrelevant to this). Later on, site owner comes along and clicks a button to get all these images down in a ZIP file. In the old days of .net framework this was easy. Now I seem to be hitting a set of 'yes, but' comments.
Yes, there's system.drawing.image but you can't use that because it's not in dotnet core (until recently).
Yes, you can use CoreCompat but it doesn't work on Azure because in Web Applications there's no support for GDI+.
Yes, even if I could, I'm developing on a Mac so it won't work locally as far as I can see.
I have tried beta4 of ImageSharp without a lot of success. It's random - sometimes it works, sometimes it just throws OutOfMemoryException.
I have tried SkiaSharp but similar results; sometimes it works, sometimes it spits out random error messages.
I'm not doing anything fancy in terms of processing, no resizing or anything. It should be a case of load file to byte array from Convert.FromBase64String, create Zip file entry, ultimately spit out zip file. The ZIP portion is fine, but I need something decent that can do the image work.
Here's a bit of code:
if(!string.IsNullOrEmpty(del.Headshot))
{
var output=SKImage.FromBitmap(SKBitmap.Decode(Convert.FromBase64String(del.Headshot)));
MemoryStream savedFile=new MemoryStream();
output.Encode(SKEncodedImageFormat.Jpeg, 100).SaveTo(savedFile);
string name=$"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + "_Headshot.jpg";
ZipArchiveEntry entry=zip.CreateEntry(name);
using(Stream entryStream=entry.Open())
{
entryStream.Write(savedFile.ToArray(), 0, Convert.ToInt32(savedFile.Length));
}
output.Dispose();
savedFile.Dispose();
}
Can anyone give me a sensible suggestion for a library that can handle images, cross-platform and on Azure, before I pull out what little hair remains!
Thanks
EDIT: The first answer is technically correct, I don't need anything else. However, I might have been a bit wrong when I said I wasn't doing any image manipulation. Because it's all base64 without a filename being stored anywhere, I've no idea what sort of file it is. I'm therefore saving each one as JPEG to ensure that I can always output that file type and extension. Users I guess could be uploading JPG / PNG or even GIF.
Technically you do not need any of those other imaging (unless you are doing more that just zipping the content). Convert the base64 to byte array and pass that to the zip file. No need to save to disk just to read it back again for zipping.
//...
if(!string.IsNullOrEmpty(del.Headshot)) {
var imageBytes = Convert.FromBase64String(del.Headshot);
string name = $"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + "_Headshot.jpg";
ZipArchiveEntry entry = zip.CreateEntry(name);
using(Stream entryStream = entry.Open()) {
entryStream.Write(imageBytes, 0, imageBytes.Length));
}
}
//...
Also using a minor hack for known image types when converted to base64
public static class ImagesUtility {
static IDictionary<string, string> mimeMap =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "IVBOR", "png" },
{ "/9J/4", "jpg" },
//...add others
};
/// <summary>
/// Extract image file extension from base64 string.
/// </summary>
/// <param name="base64String">base64 string.</param>
/// <returns>file extension from string.</returns>
public static string GetFileExtension(string base64String) {
var data = base64String.Substring(0, 5);
var extension = mimeMap[data.ToUpper()];
return extension;
}
}
You could try to determine the file extension from its prefix
if(!string.IsNullOrEmpty(del.Headshot)) {
var imageBytes = Convert.FromBase64String(del.Headshot);
var ext = ImagesUtility.GetFileExtension(del.Headshot) ?? "jpg";
string name = $"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + $"_Headshot.{ext}";
ZipArchiveEntry entry = zip.CreateEntry(name);
using(Stream entryStream = entry.Open()) {
entryStream.Write(imageBytes, 0, imageBytes.Length));
}
}
Now ideally, if you are able to control the type of image uploaded, then you should also validate and do what ever image processing when the data is being saved along with any needed metadata (ie content type). That way when extracting it from storage, you can be confident that it is the correct type and size. That saves you having to worry about that later on.
Aspose.Drawing and Aspose.Imaging can handle images and cross-platform running on .NET Core (I'm one of the developers).

SharpDX - fxc.exe error when compiling hlsl file

I'am starting to learn SharpDX and Holographic template for C#.
While compiling project I have error at compilation of shader file.
var vertexShaderByteCode = ShaderBytecode.CompileFromFile("Content/Shaders/VertexSharderShared.hlsl", "VS", "vs_5_0", ShaderFlags.Debug);
fxc.exe exited with code 1.
In the sample they use some .cso file while loading shader into byte code:
code img
As I don't really understand where that .cso comes from and what Vprt is, I prefer loading hlsl file directly. I'm using SharpDX 3.0.2, SharpDX.Direct3D11 3.0.2 and SharpDX.D3DCompiler 3.0.2.
Thanks!
The CSO files need to be loaded then the relevant shader created. Below is an example of a function I use to load the shader. Please note, that you should hold onto the byte code as well as the vertex shader in this example.
The Helper function is my cross uwp/mfc wrapper, but it basically returns the file in a datastream in memory.
The CSO file is byte code, you need to attach it to the correct Sharpdx.Direct3D11 shader class.
The bytecode is also used to reference the semantic interface (for Vertex buffers only). once you have created that, your draw call can match the data against the GPU register that it is associated to. But the code below at least will load your CSO file in and create the vertexshader.
goodluck.
using (Stream fileStream = Helper.GetFileStream(a_fileName, a_filePath))
{
a_byteCode = new D3DCompiler.ShaderBytecode(fileStream);
if (a_byteCode != null)
{
a_child = new D3D11.VertexShader(a_device, a_byteCode)
{
DebugName = "a_fileName"
};
}
else
{
throw new Exception("Unable to load file: " + a_fileName);
}
}

Make correct URI for WP Game Library

I am trying to create a library with sounds in it, but I cant get the URIs to work, if I use a online uri like
new Uri("http://www.archive.org/download/BrahmsViolinConcerto-Heifetz/03Iii.AllegroGiocosoMaNonTroppoVivace.mp3")
it works fine, so the issue is linking correctly to my folders in my project
My in my WP Game Librarys folder I have \Sounds\letters and in that folder is a sound named a.wma
My Method for loading this is
public void PlayLetter(string letter)
{
try
{
Initialize();
FrameworkDispatcher.Update();
var uri = new Uri(#"/Sounds/letters/" + letter + ".wma", UriKind.Relative);
var song = Song.FromUri("sound", uri);
MediaPlayer.Play(song);
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
}
And I of course give it string "a" as a parameter when it fails
I have also included the sound file in my project like
I just get a
A first chance exception of type 'System.InvalidOperationException' occurred in Microsoft.Xna.Framework.dll
But its an uri problem I am certain as I tried a online URI that worked just fine
Also I am in doubt of 2 things, is MediaPlayer the right thing to use in a game? And can a library play sounds (Or even contain them)
The typical thing in XNA would be to use a SoundEffectInstance:
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.audio.soundeffectinstance.aspx
Unfortunately SoundEffectInstance only works with wav files. If you want to play back longer music files - you can use a MediaElement - but that allows for playback of a single compressed audio file at a time only. Another option might be to play compressed from the MediaLibrary using the MediaPlayer class. You could also save your own compressed audio file in the MediaLibrary to play it from there. See:
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.media.medialibrary.songs.aspx

Categories

Resources