How to load asset with same name, but placed in different folders from assetbundle? - unity3d

I am using unity5.3.3, I would like to know how should I get the asset from an asset bundle, whose names are same but are kept in different folder. My AssetBundle Folder is set in the following manner:
MyAssets -> this Folder is packed as an AssetBundle
-ThemeOne(folder)
- Logo.png
-ThemerTwo(folder)
- Logo.Png
When I do AssetBundle.LoadAssetAsync("Logo"). I end getting the logo in the first(ThemeOne) folder. So how do I access the file in the other folder?
I have just created a sample project so that you can check it out. Check the Folder Assets\AssetBundleSample\SampleAssets\Theme and the script LoadAssets

You can put the ThemeOne(folder) and ThemeTwo(folder) in Resources Folder under assets and than use something like this
(AudioClip)(Resources.Load ("Sounds/" + "myaudioclip", typeof(AudioClip)) as AudioClip)
similarly load your png giving folder name first as i have given sounds and than name of file as i have given myaudioclip .
cast as texture or something as you want

As unity official docs provided
public string BundleURL;
public string AssetName;
IEnumerator Start() {
// Download the file from the URL. It will not be saved in the Cache
using (WWW www = new WWW(BundleURL)) {
yield return www;
if (www.error != null)
throw new Exception("WWW download had an error:" + www.error);
AssetBundle bundle = www.assetBundle;
if (AssetName == "")
Instantiate(bundle.mainAsset);
else
Instantiate(bundle.LoadAsset(AssetName));
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
} // memory is freed from the web stream (www.Dispose() gets called implicitly)
}
BundleURL will use the path where your actual assets is located. You can also use Application.dataPath in conjunction with ur specified path string.

Seems to be a bug. Still present in Unity version 5.3.3.
See: http://answers.unity3d.com/questions/1083400/asset-bundle-cant-have-multiple-files-with-the-sam.html

I'm not really sure if this is possible, you should read Explicit naming of assets which is FYI obsolete as on Unity 5.6. This allows you to explicitly name assets that you are including in the bundle. Alternatively, you could try,
bundle.LoadAsset("/Assets/ThemerTwo/Logo.png")
But generally, this is a bad practice to have assets of the same naming in the same bundle.

You can use the method LoadAssetAtPath() to specify the full path instead of just the asset name.
Example:
LoadAssetAtPath("Assets/Texture/Logo.jpg", typeof(Texture2D));
Documentation here: http://docs.unity3d.com/ScriptReference/AssetDatabase.LoadAssetAtPath.html

Related

Get AssetBundle manifest file from server

I am new in Unity and and I start to use AssetBundle for my project to download them from server. I use UnityWebRequestAssetBundle to download the assets because WWW is obsolete. Everything is works fine I can download the assets from the server put it into the persistentDataPath and I can load them to the scene without any problem. But then I realised that there may be a chance when the model is get updated so I also need to update it in the app. I know there is a manifest file for every asset in the assetbundle so when I download the asset I also download the manifest file. I do everything as the Unity documentation but I always get this error:
unable to read header from archive file: /manifest/location
The manifest download method looks like this:
IEnumerator GetManifest(string animal)
{
using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle("http://weppage.com/" + asset + ".manifest"))
{
uwr.downloadHandler = new DownloadHandlerFile(path + "/" + asset + ".manifest");
yield return uwr.SendWebRequest();
if ( uwr.isNetworkError || uwr.isHttpError )
{
Debug.Log(uwr.error);
}
}
}
Then there is the check method after download:
AssetBundle manifestBundle = AssetBundle.LoadFromFile(Path.Combine(path, asset));
AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("asset.manifest");
I'm totally lost. I go through the google and I can't get it work. I don't get why it isn't work. I load the asset the same way without any problem and Unity docs said it have to work the same way. From Unity docs: Loading the manifest itself is done exactly the same as any other Asset from an AssetBundle
The file you need to load in AssetBundle.LoadFromFile is a file that is generated in the same folder you stored your AssetBundle. This file gets the same name as the folder is stored in. For example, if you generated an AssetBundle called maps in a folder called bundles, a file bundles is going to be generated along with maps. This way, in AssetBundle.LoadFromFile you would have to load the file bundles. In any case you have to load any .manifest file. Your code would be like this:
IEnumerator GetManifest(string animal)
{
using (UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle("http://weppage.com/" + asset))
{
uwr.downloadHandler = new DownloadHandlerFile(path + "/" + asset);
yield return uwr.SendWebRequest();
if ( uwr.isNetworkError || uwr.isHttpError )
{
Debug.Log(uwr.error);
}
}
}
And your check method after download:
AssetBundle manifestBundle = AssetBundle.LoadFromFile(Path.Combine(path, "bundles")); //the name of the example file I gave you
AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

Iterating and reading text files from folder on Android

I made a word game app for android in Unity, where the player has to find a word from a category previously loaded to the game.
The way I load the categories is:
There is a folder named Categories, inside Assets, I run through the folder and read each text file as a category.
The categories are stored in a dictionary where the key is name the of the file and the value is every line of the file as an array element.
It worked well on the PC however no luck on android. Tried changing the path to
"public string categoriesDirectoryPath = Application.persistentDataPath +"Categories";" still does not work.
Original path was "Assets/Categories"
Code for initiating the dictionary with the file values is (Happens on GameManager's Awake()):
private Dictionary<string, string[]> createCategories(string directoryPath)
{
Dictionary<string, string[]> categories = new Dictionary<string, string[]>();
string[] categoryPaths = Directory.GetFiles(directoryPath);
foreach (string path in categoryPaths)
{
if (!path.EndsWith("meta")) {
Debug.Log(path);
string categoryName = Path.GetFileNameWithoutExtension(path);
Debug.Log(categoryName);
string[] categoryData = File.ReadAllLines(path).ToArray();
categories.Add(categoryName, categoryData);
}
}
return categories;
}
Is there a way of iterating the folder and reading the text files that were in Assets/Categories after building the APK?
Is there a way of iterating the folder and reading the text files that
were in Assets/Categories after building the APK?
No.
If you want to access from the project, in a build, you have two options:
1.Put the file a folder named "Resources" then use the Resources to read the file and copy it to the Application.persistentDataPath path. By copying it to Application.persistentDataPath, you'll be able to modify it. Anything in the "Resources" is read only.
2.Put the file in the StreamingAssets folder then use UnityWebRequest, WWW or the System.IO.File API to read it. Atfer this, you can copy it to the Application.persistentDataPath.
Here is a post with code examples on how to do both of these.
3.AssetBundle(Recommended due to performance and loading reaons).
You can build the file as AssetBundle then put them in the StreamingAssets folder and use the AssetBundle API to read it.
Here is a complete example for building and reading AssetBundle data.

Application.Capturescreenshot(fileName) save to specific path

i am using in my unity project this method that saves the camera viewport to the file system:
Application.Capturescreenshot(fileName)
it is work great but i want it to be saved under a specific path.
for example : Application.Capturescreenshot(c:/screenshots/filename);
how can i managed this?
thnaks!
You could use the absolute file path easily by passing it as a string. In your case, using this statement should work just fine:
Application.CaptureScreenshot("C:/screenshots/filename");
Note that it will give you an error if screeshots folder does not exist. Hence, if you are uncertain of that, you could modify the code accordingly:
// File path
string folderPath = "C:/screenshots/";
string fileName = "filename";
// Create the folder beforehand if not exists
if(!System.IO.Directory.Exists(folderPath))
System.IO.Directory.CreateDirectory(folderPath);
// Capture and store the screenshot
Application.CaptureScreenshot(folderPath + fileName);
Lastly, if you want to use a relative path instead of an absolute one, Unity provides dataPath and persistentDataPath variables to access the data folder path of the project. So, if you want to store the screenshots inside the data folder, you could change the folderPath above accordingly:
string folderPath = Application.dataPath + "/screenshots/";
var nameScreenshot = "Screenshot_" + DateTime.Now.ToString("dd-mm-yyyy-hh-mm-ss") + ".png";
ScreenCapture.CaptureScreenshot(nameScreenshot);
Try this !

Image not showing immediately after uploading in sails.js

In my application ,I have stored uploaded images to folder ./assets/uploads. I am using easyimage and imagemagick for storing the images.
In my application, after uploading the images, it should show the new uploaded image. But it is not displayed even if the page is refreshed. But when i do sails lift , the image is shown.
How to show image immediately after uploading the image? Thanks a lot!
It's a totally normal situation, because of the way Sails works with the assets.
The thing is that upon sails lift the assets are being copied (including directory structure and symlinks) from ./assets folder to ./.tmp/public, which becomes publicly accessible.
So, in order to show your images immediately after upload, you, basically, need to upload them not to ./assets/uploads but to ./.tmp/public/uploads.
The only problem now is that the ./.tmp folder is being rewritten each time your application restarts, and storing uploads in ./tmp/... would make them erased after every sails lift. The solution here would be storing uploads in, for example, ./uploads and having a symlink ./assets/uploads pointing to ../uploads.
Though this question is pretty old but I would like to add a solution which I just implemented.
Today I spend almost 4 hours trying all those solutions out there. But none helped. I hope this solution will save someone else's time.
WHY images are not available immediately after uploading to any custom directory?
Because according to the default Sails setup, you can not access assets directly from the assets directory. Instead you have to access the existing assets that is brought to .tmp/public directory by Grunt at time of sails lift ing
THE Problems
(Available but Volatile) If you upload a file (say image) anywhere inside .tmp/public
directory, your file (image) is going to erase at next sails lift
(Unavailability) If you upload a file in any other custom directory- say: ./assets/images, the uploaded file will not be available immediately but at next sails lift it will be available. Which doesn't makes sense because - cant restart server each time files gets uploaded in production.
MY SOLUTION (say I want to upload my images in ./assets/images dir)
Upload the file say image.ext in ./tmp/public/images/image.ext (available and volatile)
On upload completion make a copy of the file image.ext to ./assets/images/*file.ext (future-proof)
CODE
var uploadToDir = '../public/images';
req.file("incoming_file").upload({
saveAs:function(file, cb) {
cb(null,uploadToDir+'/'+file.filename);
}
},function whenDone(err,files){
if (err) return res.serverError(err);
if( files.length > 0 ){
var ImagesDirArr = __dirname.split('/'); // path to this controller
ImagesDirArr.pop();
ImagesDirArr.pop();
var path = ImagesDirArr.join('/'); // path to root of the project
var _src = files[0].fd // path of the uploaded file
// the destination path
var _dest = path+'/assets/images/'+files[0].filename
// not preferred but fastest way of copying file
fs.createReadStream(_src).pipe(fs.createWriteStream(_dest));
return res.json({msg:"File saved", data: files});
}
});
I dont like this solution at all but yet it saved more of my time and it works perfectly in both dev and prod ENV.
Thanks
Sails uses grunt to handle asset syncing. By default, the grunt-watch task ignores empty folders, but as long as there's at least one file in a folder, it will always sync it. So the quickest solution here, if you're intent on using the default static middleware to server your uploaded files, is to just make sure there's always at least one file in your assets/uploads folder when you do sails lift. As long as that's the case, the uploads folder will always be synced to your .tmp/public folder, and anything that's uploaded to it subsequently will be automatically copied over and available immediately.
Of course, this will cause all of your uploaded files to be copied into .tmp/public every time your lift Sails, which you probably don't want. To solve this, you can use the symlink trick #bredikhin posted in his answer.
Try to do this:
npm install grunt-sync --save-dev --save-exact
uncomment the line: // grunt.loadNpmTasks('grunt-sync');
usually it is near to the end of the file /tasks/config/sync.js.
lift the App again
Back to the Original answer
I was using node version 10.15.0, and I faced same problem. I solved this by updating to current version of node(12.4.0) and also updated npm and all the node modules. After this, I fixed the vulnerabilities(just run 'npm audit fix') and the grunt error that was coming while uploading the images to assets/images folder was fixed.
Try out this implementation
create a helper to sync the file
example of the filesync helper
// import in file
const fs = require('fs')
module.exports = {
friendlyName: 'Upload sync',
description: '',
inputs: {
filename:{
type:'string'
}
},
exits: {
success: {
description: 'All done.',
},
},
fn: async function ({
filename
}) {
var uploadLocation = sails.config.custom.profilePicDirectory + filename;
var tempLocation = sails.config.custom.tempProfilePicDirectory + filename;
//Copy the file to the temp folder so that it becomes available immediately
await fs.createReadStream(uploadLocation).pipe(fs.createWriteStream(tempLocation));
// TODO
return;
}
};
now call this helper to sync your files to the .temp folder
const fileName = result[0].fd.split("\\").reverse()[0];
//Sync to the .temp folder
await await sails.helpers.uploadSync(fileName);
reference to save in env
profilePicDirectory:path.join(path.resolve(),"assets/images/uploads/profilePictures/")
tempProfilePicDirectory:path.join(path.resolve(),".tmp/public/images/uploads/profilePictures/"),
also can try
process.cwd()+filepath

Change tinymce imagemanager upload folder

I'm using tinymce and imagemanager to upload images.
Any image that I upload is saved in this path: tiny_mce/plugins/imagemanager/files/
It is posiable to change this path to: uploads/ (outside from the tiny_mce folder) ?
And it is posiable to change the path to uploads/test/ or tiny_mce/plugins/imagemanager/files/test/ - That if test not exist is will create it?
Thanks
Sorry, this seems to be not possible at the moment.
I think you can't resolve it with tinyMCE settings. But you can try to implement your own plugin for this.
http://www.tinymce.com/wiki.php/MCImageManager:Creating_a_plugin
You can try to override method OnAfterUpload that executed after upload files.
You has acces to paths of new files and can make some manipulations. Like this:
public override bool OnAfterUpload(ManagerEngine man, string cmd, NameValueCollection input)
{
foreach (string path in man.RootPaths)
{
string fileName = input["name"];
// create new folder if necessary. Copy files etc
}
return true;
}