0

I am new to Unity and Android development, but please do not mark this as a duplicate - I've looked all over the stacks for an answer to this, but similar topics and questions haven't yielded any working solutions, or are wanting on details, or outdated, or seem not to fit my specific need.

Ok, I have a quiz app built from following part 1 & 2 of this tutorial. That link contains all the source files anyone might need for reference, and everything works fine on ios and in the unity editor, fyi.

The trouble is with the loadGameData function from the DataController script in Android. Again, everything works fine in iOS and the unity editor, but when I make an Android sdk, the quiz is blank and the console says the data couldn't be loaded. Here is how the function is currently written (full file in tutorial link):

    private void LoadGameData ()
{
    string filePath = Path.Combine (Application.streamingAssetsPath, gameDataFileName);


    if (File.Exists (result)) 
    {
        string dataAsJson = File.ReadAllText (result);
    GameData loadedData = JsonUtility.FromJson<GameData> (dataAsJson);

    allRoundData = loadedData.allRoundData;

    }  // #if(File.Exists...

    else 
    {
    Debug.LogError ("Cannot load game data!");
    }  // #else

} // #LoadGameData

If you check the same tutorial on youtube, you'll see lots of people have noted the same problem with the Android build and have been left unanswered. Same goes with unity forums - that's one reason why I don't think this question is a duplicate and could be helpful to others in a similar situation.

I've found that Android has always been sorta tricky with this and that there used to different ways of accessing a file based on platform, but these days "Application.streamingAssetsPath" should find the streaming assets directory on any platform, even Android.

What I've also learned is that in android, even if the path is correct, the file is compressed and will only return a url. So the url needs to be converted using unity's WWW class. I get that, but as of yet, I haven't been able to re-write my loadGameData function to work properly and load the allRoundData array.

Here's an example of some things I've tried:

IEnumerator androidData() 
{
    string filePath = Path.Combine (Application.streamingAssetsPath, gameDataFileName);
    if (filePath.Contains("://")) 
    {
        WWW www = new WWW(filePath);
        yield return www;
        result = www.text;
    } // #if(filePath.Contains
} // #androidData


private void LoadGameData ()

{

    androidData();


    if (File.Exists (result)) 
    {

        string dataAsJson = File.ReadAllText (result);
    GameData loadedData = JsonUtility.FromJson<GameData> (dataAsJson);

    allRoundData = loadedData.allRoundData;

    }  // #if(File.Exists...
    else 
    {
    Debug.LogError ("Cannot load game data!");
    }  // #else
} // #LoadGameData

I know I'm close, and this is probably simple -- but I just can't seem to get to the finish line on this. Can some one help me figure out how to write the loadGameData function so it will load this allRoundData array on android?

An example code would be awesome and much appreciated, not just by me, but I'm sure many others would appreciate it also - Thank you!

UPDATE: Based on the first answer, I've tested some code that works on the unity editor, but crashes in Android. In the Unity editor, I get the "file already exists" message. Here is the code I've tested: Already had: private string gameDataFileName = "data.json"; I added the copyFile call above loadGameDate in Start() and wrote the copy file and loadGameData functions like so ..

int copyFileToPersistentDataPath(string gameDataFileName)
{
    string persistentPath = Application.persistentDataPath + "/" + gameDataFileName;

    try
    {
        //Copy only if gameDataFileName does not exist  
        if (!System.IO.File.Exists(persistentPath))
        {
            string path = System.IO.Path.Combine(Application.streamingAssetsPath, gameDataFileName);
            WWW www = new WWW(path);
            while (!www.isDone) { }
            System.IO.File.WriteAllBytes(persistentPath, www.bytes);
            Debug.Log(gameDataFileName + " Successfully Copied File to " + persistentPath);
            return 1;
        }
        else
        {
            Debug.Log(gameDataFileName + " File already exist here. There is no need to copy it again");
            return 0;
        }
    }
    catch (Exception e)
    {
        Debug.Log(gameDataFileName + " Failed To Copy File. Reason: " + e.Message);
        return -1;
    }
}



private void LoadGameData ()
{


    string tempPath = Path.Combine(Application.persistentDataPath, gameDataFileName);

    string dataAsJson = File.ReadAllText(tempPath);
    GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
    allRoundData = loadedData.allRoundData;
    } // #LoadGameData

This works with or without the call to copy the file in the editor, but crashes either way in Android.

user3147770
  • 203
  • 4
  • 16
  • All you want to do is to save and load game data? – Programmer Feb 07 '17 at 16:04
  • Pretty much ... end goal is to populate that allRoundData array with the question and answer info from the json file, works in ios and the editor with that first piece of code I pasted, but not in Android b/c of the file being compressed in the sdk. I get the 'why' just not the 'how' ... #noobProblems :-) – user3147770 Feb 07 '17 at 16:12
  • In that case, use `Application.persistentDataPath`. Check the duplicated question for how to save and load game data. – Programmer Feb 07 '17 at 16:16
  • Surely there is a simple answer since the code already works and loads the allRoundData arry in ios and the unity editor? It's in a script that is tied to a persistent game object already. Just doesn't work in Android -- editing the question to be more specific so it isn't a dupe. Thank you for your answers and info - I'm still reading over it - it just seems like it should be a simple thing I'm coding wrong for Android since it works otherwise, ya know? – user3147770 Feb 07 '17 at 16:37
  • I realized that. That's why I removed the duplicate even before your comment. Take a look at my answer. I will be away for hours and cannot reply to your question at this moment. I will when I return. – Programmer Feb 07 '17 at 16:49
  • Thank you -- reading all your wonderful info now in the meantime -- super thanks! – user3147770 Feb 07 '17 at 16:53

2 Answers2

1

I ended up putting the files in a Resources folder and going the resources.load a json file into a text asset and throw that into a string to parse route. I now have two different load functions, one that works in ios etc. and one that works in android. Here is the android function (the resourcesGameDataFile does not have the .json extension):

public void LoadDataForAndroid()
{

    TextAsset dataFile = Resources.Load(resourcesGameDataFile) as TextAsset;
    string dataAsJson = dataFile.ToString();
    GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
    allRoundData = loadedData.allRoundData;
    Debug.Log ("Android data loaded with" + resourcesGameDataFile);
} // #LoadDataForAndroid

And this works in the unity editor and in Bluestacks (android simulator).

user3147770
  • 203
  • 4
  • 16
0

As for loading and saving games, this is a duplicate. I marked and remove this as a duplicate because the answer in the duplicated questions did not explain how to read from the StreamingAssets folder. It only talked about saving and loading data.

Make sure that Write Permission is set to External (SDCard).

enter image description here

The first thing to do is to copy the file from StreamingAssets to the persistentDataPath location.

I've found reading data from Application.streamingAssetsPath problematic but I use two methods to solve this.

int copyFileToPersistentDataPath(string fileNameWithExtensionName)
{
    string persistentPath = Application.persistentDataPath + "/" + fileNameWithExtensionName;

    try
    {
        //Copy only if fileNameWithExtensionName does not exist  
        if (!System.IO.File.Exists(persistentPath))
        {
            string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileNameWithExtensionName);
            WWW www = new WWW(path);
            while (!www.isDone) { }
            System.IO.File.WriteAllBytes(persistentPath, www.bytes);
            Debug.Log(fileNameWithExtensionName + " Successfully Copied File to " + persistentPath);
            return 1;
        }
        else
        {
            Debug.Log(fileNameWithExtensionName + " File already exist here. There is no need to copy it again");
            return 0;
        }
    }
    catch (Exception e)
    {
        Debug.Log(fileNameWithExtensionName + " Failed To Copy File. Reason: " + e.Message);
        return -1;
    }
}

If that does not work for you, use the method with WebClient below:

void copyFileToPersistentDataPath(string fileNameWithExtensionName)
{
    string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileNameWithExtensionName);
    string persistentPath = Application.persistentDataPath + "/" + fileNameWithExtensionName;

    Debug.Log("Dir: " + persistentPath);
    WebClient webClient = new WebClient();
    webClient.Proxy = null;

    webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadComplete);
    webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnUpdateDownloadProgress);

    Uri uri = new Uri(path);
    webClient.DownloadFileAsync(uri, persistentPath);
}

void OnDownloadComplete(object sender, AsyncCompletedEventArgs e)
{
    Debug.Log("Finished Downloading: " + e.Error.Message);
}

void OnUpdateDownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
    Debug.Log("Uploading Progreess: " + e.ProgressPercentage);
}

File Copy Usage:

copyFileToPersistentDataPath("yourFileName.txt");

After copying the file, you can then read and convert it to Json like this:

string fileNameWithExtensionName = "questionfile.txt";
string tempPath = Path.Combine(Application.persistentDataPath, fileNameWithExtensionName);

string dataAsJson = File.ReadAllText(fileNameWithExtensionName);
GameData loadedData = JsonUtility.FromJson<GameData>(dataAsJson);
Community
  • 1
  • 1
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thank you - I've gotten farther using an adaptation of this - at least, it now works again in unity, but it still doesn't work on Android. – user3147770 Feb 07 '17 at 23:15
  • I really want to know which part does not work. The copying of the data or the reading of it from the persistentDataPath? – Programmer Feb 07 '17 at 23:16
  • Not sure - I have a weird new error about "OVRPlugin.bundle/Contents/MacOS/OVRPlugin, error:" that has nothing to do with anything. No idea where that came from. I can get it to work in the unity editor & saw the message "file already exists" - so I tested without calling copyfile function & it still worked in unity - but nothing works in Android - actually it just crashes now without loading the empty quiz screen. I'll add an update so you can see the code I've tried. – user3147770 Feb 07 '17 at 23:20
  • You can use Android Monitor from Android Studio to see the Debug.Log messages then you will see where it crashes. Since I don't have your device, you have to do this yourself. – Programmer Feb 07 '17 at 23:24
  • That would be useful ... but I built this in unity & am not yet sure how to open the project in android studio. I can only export the apk file at the moment. But I did post how I adapted your copy file function in that update - in case you might notice some glaring noob errors. Thanks again for all your help! – user3147770 Feb 07 '17 at 23:40
  • You do **not** need to open your Project in Android Studio. Simply open Android Studio then open the Android Monitor. When you build and open your app in your Android, it will show you all the `Debug.Log` stuff in the Android Monitor. You must know how to do this in order to properly Debug your project on Android. – Programmer Feb 07 '17 at 23:44
  • Thanks! Got the monitor all set up now - of course, I have no idea what I'm looking at in the log ... but it's a start. Thank you for all your help! – user3147770 Feb 08 '17 at 04:13