i am trying to get this really easy setup to work, and it works on my computer but not on Android.
After trying it for the whole day im now asking since i cant find a single example on the internet.
The setup:
I have a lot of *.png files in a folder. Since the problem is with android i put them into a "StreamingAssets" folder
I have a RawImage called "Overlay"
I have a Dropdown called "Dropdown"
What should happen:
1. The Dropdown_script checks the StreamingAssets folder for all *.png files and adds every Filename to the dropdown options menu.
2. Once the user chooses one of the dropdown values my "Overlay" image changes its texture to the according file.
Pretty easy on PC ... impossible on android(with unity3d) as it seems.
My Dropdown Script:
public class DDScript : MonoBehaviour {
void Start () {
GameObject obj = GameObject.Find("Dropdown");
//Get all filenames
var info = new DirectoryInfo(Application.streamingAssetsPath);
var fileInfo = info.GetFiles("*.png");
//Clear the DropDown Menu Items from the Inspector
obj.GetComponent<Dropdown>().options.Clear();
//Make the first chosen item the first filename
obj.GetComponent<Dropdown>().captionText.text = fileInfo[0].Name.Substring(0, fileInfo[0].Name.IndexOf("."));
foreach (var file in fileInfo)
{
Dropdown.OptionData list = new Dropdown.OptionData(file.Name.Substring(0, file.Name.IndexOf(".")));
obj.GetComponent<Dropdown>().options.Add(list);
}
}
Its not working. It is working on my Computer but it is just not working on Android. I know that under Android everything gets packed into a jar file and there is nothing like a folderstructure. StreamingAssets should still have the files but i can understand that this part maybe cant work. Which would not be that bad, since it just finds the names and puts it into an array, i could do that within the program by hand. Since i know from beforehand how much and which files will be in the folder and it wont change ever.
This is my Change Texture Method
It is invoked by the Dropdown OnValueChange so the "newOverlay" is simply the int from the Dropdown. For debugging purposes i set the filepath to a specific file. later it will change depending on newOverlay
public IENumerator SetOverlay(int newOverlay)
{
GameObject obj = GameObject.Find("Overlay");
img = (RawImage)obj.GetComponent<RawImage>();
string filePath = System.IO.Path.Combine(Application.streamingAssetsPath, "testfile.png");
WWW www = new WWW(filePath);
yield return www;
img.texture = www.texture;
}
And i do not understand why this is not working!
The Documentation reads:
" This means that if you do not use Unity’s WWW class to retrieve the file then you will need to use additional software"
So using www is the right way. But it is not working!
No Texture gets loaded. i dont get the red questionmark, my image is just plain white. Please can someone help me here? Maybe i just cant see the forest due to all of the trees or the day was too long.
I really just cant believe that it is this hard to load a bunch of images that are within my apk file wiht unity :(
Thank you very much in advance
Edit: Solution (somehow)
First of all the problem was that System.IO.Path.Combine adds a back-slash between file and folder, but not between the folders themself!!!!
public void SetOverlay(int newOverlay)
{
GameObject obj = GameObject.Find("Overlay");
img = (RawImage)obj.GetComponent<RawImage>();
string filePath = Application.streamingAssetsPath + "/testfile.png";
WWW www = new WWW(filePath);
while (!www.isDone)
{}
img.texture = www.texture;
GameObject.Find("DebugText").GetComponent<Text>().text = filePath;
}
Havent found a solution to the Filename problem yet but im glad that this stupid "bug" is resolved
Have you tried putting the png files into the drawable folder and accessing them with BitmapFactory.decodeResources()?
Related
Im currently trying to save some values in a text file in Processing Android (APDE). I want to later use this in another context, so it's important to use a complete file path. From Processing documentation for loadStrings():
... Alternatively, the file maybe be loaded from anywhere on the local
computer using an absolute path (something that starts with / on Unix
and Linux, or a drive letter on Windows)
So it must be possible.
I already searched for a answer, but never found something for Processing.
So my code is:
String[] saveData;
int score;
void setup(){
saveData=loadStrings("/storage/emulated/0/dataP/hi.txt");
score=parseInt(saveData[0]);
fullScreen();
frameRate(60);
noStroke();
noSmooth();
textAlign(CENTER);
textSize(height/20);
}
void draw(){
background(0);
fill(255) ;
text(score, width/2,height/2);
}
void mousePressed(){
score--;
saveData[0]=str(score);
println(saveData[0]);
saveStrings("/storage/emulated/0/hi.txt" ,saveData);
}
and I get the following error:
java.lang.IllegalArgumentException: File
/storage/emulated/0/dataP/hi.txt contains a path separator
I believe the confusion stems from the fact that loadStrings() method works differently for Java mode and Android mode. In Java mode, it is definitely possible to give loadStrings() an absolute Path with included separators, but in Android mode, loadStrings() will only work if you only specify a name without any separator (assumes by default to be looking into the data folder). Therefore, having any separator inside loadStrings() will throw the error.
One simple workaround you can try is to first create a separate path variable:
String path = "/storage/emulated/0/dataP/hi.txt";
And then give that as parameter to the loadStrings() method:
saveData = loadStrings(path);
If you were to use an SD card for storage, for example, you could do something like:
String SDCARD = Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(SDCARD + File.separator + "mytext.txt");
String[] s = loadStrings(file.getPath());
As explained in the link in the comment I posted, loadStrings() and saveStrings() does not take absolute path as argument. What it means is that it can only access files with path "name.txt" and not "folder/name.txt". You have to do it using a FileInputStream and FileOutputStream if you must use absolute path. There are many examples of both these files on StackOverflow.
So in my project I have an image object that loads various different sprites throughout the game. In order to implement this, I have a folder with a bunch of .pngs that the game accesses. In order for this to work in a standalone build I put these images in the following path:
StreamingAssets/Question Images
However, in Android I am getting an error because, as the manual says:
"On Android, the files are contained within a compressed .jar file (which is essentially the same format as standard zip-compressed files). This means that if you do not use Unity’s WWW class to retrieve the file, you need to use additional software to see inside the .jar archive and obtain the file.
The thing is I have no idea how to go about implementing this, any ideas?
From Application.streamingAssetsPath
It is not possible to access the StreamingAssets folder on WebGL and Android platforms. No file access is available on WebGL. Android uses a compressed .apk file. These platforms return a URL. Use the UnityWebRequest class to access the Assets.
Example using UnityWebRequestTexture
private void Start()
{
StartCoroutine(GetTexture());
}
private IEnumerator GetTexture()
{
// in general I would always avoid to have spaces in file-paths
var path = Path.Combine(Application.streamingAssetsPath, "QuestionImages", "exampleImage.png");
using(var www = UnityWebRequestTexture.GetTexture(path))
{
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError)
{
Debug.LogErrorFormat(this, "Unable to load texture due to {0} - {1}", www.responseCode, www.error);
}
else
{
Texture myTexture = ((DownloadHandlerTexture)www.downloadHandler).texture;
}
}
}
Note: Typed on smartphone so no warrenty but I hope the idea gets clear.
Ok, Quick heads up that I am new to android programming.
I have very basic experience with java from uni.
I've recently been messing around with android studio and app building for my Samsung Galaxy Tab.
Sorry if this has been asked before but i couldn't see it.
I have tried to make an app that displays all the names of files on my SD card. Just at the top level. I made a quick interface with just a button and a text view to start (i'm away a single text view wont display a list but had problems but i got to that anyway)
ended up with the following code.
*public void ShowFolder(View view){
TextView test = (TextView)findViewById(R.id.View);
root = new File(Environment.getRootDirectory().toString());
File[] Files;
Files = root.listFiles();
String[] File_List;
File_List = root.list();*
I then ran this using the debugger to see what ended up in both the array lists.
I ran this code three times over but varied the "Environment.getRootDirectory" to "getExternalStorageDirectory()" and "getDataDirectory()".
Only the getRootDirectory version will return anything.In both other instances the debugger shows NULL for both Files and Files_list.
I know they are valid locations because used those commands then outputted to strings which i can manually find on my tablet with a file browser.
So, simply put, why dont all three of these options return file lists?
My first thought it permissions but I was unsure how to confirm this.
(Sorry for the long post)
Don't use toString() to create File. getRootDirectory() already returns File object.
Try something like this:
File root = Environment.getRootDirectory();
String[] rootList = root.list();
File[] rootListFiles = root.listFiles();
Also you need to obtain READ_EXTERNAL_STORAGE permission as you can see here in the docs of getExternalStorageDirectory()
I have some level definitions in xml format (with .txt extension) inside (without any subfolders) my project's rescources folder
I for more scalability, I have a plain text file naming all these level definition XMLs
I read that file using
TextAsset WorldList = (TextAsset)Resources.Load("WorldList");
And then I load the needed world:
TextAsset TA = (TextAsset)Resources.Load(/*"LevelDefs/" +*/ Worlds[worldToload]);
xps.parseXml(TA.text);
loadLevel(levelToLoad);
(you see that I have moved these rescources out of subfolder to reduce the chance of them not loading)
worldToload here is the index number of that world
program works fine on windows but nothing loads on my android test device.
I seem to have problems debugging on device so I only guess something went wrong in loading phase.
any suggestions?
From Unity Documentation:
Most assets in Unity are combined into the project when it is built.
However, it is sometimes useful to place files into the normal
filesystem on the target machine to make them accessible via a
pathname. An example of this is the deployment of a movie file on iOS
devices; the original movie file must be available from a location in
the filesystem to be played by the PlayMovie function.
Any files placed in a folder called StreamingAssets in a Unity project
will be copied verbatim to a particular folder on the target machine.
You can retrieve the folder using the Application.streamingAssetsPath
property. It’s always best to use Application.streamingAssetsPath to
get the location of the StreamingAssets folder, it will always point
to the correct location on the platform where the application is
running.
So below snippet should work:
// Put your file to "YOUR_UNITY_PROJ/Assets/StreamingAssets"
// example: "YOUR_UNITY_PROJ/Assets/StreamingAssets/Your.xml"
if (Application.platform == RuntimePlatform.Android)
{
// Android
string oriPath = System.IO.Path.Combine(Application.streamingAssetsPath, "Your.xml");
// Android only use WWW to read file
WWW reader = new WWW(oriPath);
while ( ! reader.isDone) {}
realPath = Application.persistentDataPath + "/Your"; // no extension ".xml"
var contents = System.IO.File.ReadAll(realPath);
}
else // e.g. iOS
{
dbPath = System.IO.Path.Combine(Application.streamingAssetsPath, "Your.xml");
}
Thanks to David I have resolved this problem.
for more precision I add some small details:
1-As David points out in his answer (and as stated in unity documentations), we should use StreamingAssets if we want to have the same folder structure. One should use Application.streamingAssetsPathto retrieve the path.
However, files are still in the apk package in android's case, so they should be accessed using unity android's www class.
a good practice here is to create a method to read the file (even mandatory if you are going to read more than one file)
here is one such method:
private IEnumerator LoadDataFromResources(string v)
{
WWW fileInfo = new WWW(v);
yield return fileInfo;
if (string.IsNullOrEmpty(fileInfo.error))
{
fileContents = fileInfo.text;
}
else
fileContents = fileInfo.error;
}
This is a coroutine, and to use it we need to call it using
yield return StartCoroutine(LoadDataFromResources(path + "/fileName.ext"));
Yet another remark: we can not write into this file or modify it as it's still inside the apk package.
It is similar to this question, however my problem is that the images are there in Codename ONE simulator (image can be loaded and shows correctly), but when I do an android build and try it on my phone, I get a java.io.FileNotFoundException. According to the comment given by Shai Almog to the question mentioned above, it should work.
What is wrong here? Could it be filenames have to be all lower case or all upper case?
(I am omitting the source here because it seems to be a problem with packaging rather than the code itself.)
UPDATE: I tried changing all filenames to lowercase but to no avail.
Finally I found out that you have to open the files as resources (Thanks to #ChrisStratton for pointing in the right direction whilst the comment was obviously about android SDK development).
This works:
private static Image loadImage(String path) throws IOException {
InputStream in=null;
try {
// works in simulator but not on device:
// in = FileSystemStorage.getInstance().openInputStream(path);
// works on both simulator and device:
in = Display.getInstance().getResourceAsStream(null, path);
return Image.createImage(in);
} finally {
if (in!=null) {
in.close();
}
}
}
You should create Images with EncodedImage.create("/fileName") or better yet add them to the res file where you can use the multi image format: http://www.codenameone.com/how-do-i---fetch-an-image-from-the-resource-file---add-a-multiimage.html
The approach with the input stream is slightly less efficient than what we can do internally.
Files in the src directory are implicitly placed in the assets directory for Android in Codename One so there is no need for an assets directory.