Apllication.dataPath question

So I have my save game files in Application.persistentDataPath and that works fine. A user can save his game and load it back up.

I am working on a tutorial system that uses a specific saved game file that sets up a particular scenario for learning how to play the game.

I move and rename the proper save file and put it in the ‘Assets’ folder. In the editor player, I can access this file with Application.dataPath. However, when I build and run the game on my phone, this file doesn’t publish with the rest of the build in the proper place. The file is clearly just not there.

How do I include this read-only file to the build so it can be accessed on the phone?

You can use streaming assets folder. Unity - Manual: Streaming Assets

I’m not able to get this to work. When I use Application.streamingAssetsPath it works in the edtior player, but on my phone I get this error in Android monitor.

IsolatedStorageException: Could not find a part of the path “/jar:file:/data/app/com.myname.FrontLine-2/base.apk!/assets/tut0.sav”.

Are you sure that you construct the path like this?

path = "jar:file://" + Application.dataPath + "!/assets/tut0.sav";

The exception shows path as “/jar:file:/data…”.

Here is my exact code:

stream = new FileStream(Application.streamingAssetsPath + "/tut" + tutorial.ToString() + ".sav",FileMode.Open,FileAccess.Read);

This works in the editor player, just not on the phone. Do I need to use a platform directive and use different code for each?

Yes, you need to use different code for Android. In editor you get a simple path, so your code works, but on Android it’s not possible to directly access files inside the apk (where files from streaming assets are), so Application.streamingAssetsPath returns the url instead. You have to use WWW to access the file. There is a small code example here Unity - Scripting API: Application.streamingAssetsPath

1 Like

Do you have any better examples on how to use WWW? Is that a coroutine? My file consists of binary data, so presumably I would access www.bytes and maybe deserialize from a MemoryStream instead of a FileStream.

I don’t use the ‘yield’ keyword in my C#, and I am still utterly confused by it. I do use a few coroutines to wait for a few seconds etc. but I would be lying if I said I understood them.

In the example you gave, how would I call the method ‘Example()’? Does it have to be in a loop?

Ok, I got it to work, so I’ll post the code for anyone else who might be interested. It’s an ugly workaround to have to go asynchronous when you’ve designed only for synchronous file access.

Reading from WWW happens in a coroutine, and then you have to poll it in an update loop to see when its done reading.

    public bool LoadTutorial(int tutorial)
    {
        tutorialNumber = tutorial;
        try
        {
#if !UNITY_ANDROID   

            FileStream stream = new FileStream(Application.streamingAssetsPath + "/tut" + tutorial.ToString() + ".sav",FileMode.Open,FileAccess.Read);
            BinaryFormatter bf = new BinaryFormatter();
            currentSaveState = (SaveState)bf.Deserialize(stream);
            currentSaveState.gameStateType = GameManager.GameStateType.GameStateTutorial;
            stream.Close();
            return true;
#endif
#if UNITY_ANDROID
            filePath = Application.streamingAssetsPath + "/tut" + tutorial.ToString() + ".sav";
            www = new WWW(filePath);
            print(filePath);
            StartCoroutine(FillByteArray(www));
        
            return true;
#endif

        }
        catch(UnityException e)
        {
            messageBox.Show(myCanvas, e.Message, null, false);
          
        }

        return false;

    }
    IEnumerator FillByteArray(WWW www)
    {
        yield return www;

        if (www.error != null)
        {
            print("WWW Error: " + www.error);
        }
    }

So for the android code, we are only partially finished. So In my saved game manager I put in an update loop to finish the process like so:

    void Update()
    {
        if (tutorialLoaded)
            return;
        if (www != null && www.isDone)
        {
            print("www.isDone is true and www.bytes length = " + www.bytes.Length);
            MemoryStream stream = new MemoryStream(www.bytes,false);
            BinaryFormatter bf = new BinaryFormatter();
            currentSaveState = (SaveState)bf.Deserialize(stream);
            currentSaveState.gameStateType = GameManager.GameStateType.GameStateTutorial;
            stream.Close();
            tutorialLoaded = true;
            mainMenuState.StartTutorial();
        }
    }

The only problem I have now is that this no longer works in the editor when I have the platform set to android.

1 Like

You can put editor specific code in #if UNITY_EDITOR. Here’s a list of all platform defines Unity - Manual: Conditional compilation

1 Like

It would be nice if WWW had an event to fire when IsDone is true so you don’t have to poll it forever.