简体   繁体   中英

Unity3D Android: How to load images and sounds dynamically (from file) from folder after the build?

Edit: As it was marked as a duplicate of Using Resources Folder in Unity I need to say that it IS different. Since I KNOW how to use the Resource Folder and if you would have read the whole text, you would have seen that I'm already using it like it is said in the post. I' getting trouble with doing it when the build is already done, since the .apk file doesn't let me access the folder afterwards, if I did not instantiate all images beforehand. So PLEASE read it again and don't mark it as a duplicate especially when the problem is stated as a different one already in the titel... Thanks!

Original:

I'm new to Stack Overflow so I hope, I give you all informations you need.

I'm developing a Serious Game in Unity3D, where I need to instantiate a various number of prefab-tiles and change the sprites and sounds of it dynamically when the scene is changed. I'll explain more after the code.

Here the code, which works perfectly in the Editor:

using UnityEngine;
using UnityEngine.SceneManagement;

#if UNITY_EDITOR
using UnityEditor;
#endif

using System;
using System.IO;

public class SourceManager : MonoBehaviour
{
private string path;
private string filePath;

private string wrongSoundDir;
private string rightSoundDir;

private string letter;
private string levelsFile = "LevelsToLoad.csv";
private string[] listOfCounterletters;
private string[] wordsOne;
private string[] wordsTwo;
private string letterOne;
private string letterTwo;
private string text;
private string text2;

private void Awake()
{
    Debug.Log("SourceManager active.");

    path = GetPath();
    if (SceneManager.GetActiveScene().buildIndex != 0)
    {
        filePath = GetFilePath(path);
        GetLettersFromFile(out letterOne, out letterTwo);
        letter = letterOne;
        if (SceneManager.GetActiveScene().name == "Words" || SceneManager.GetActiveScene().name == "Container")
        {
            GetWordsFromFile(letter, out wordsOne);
            GetWordsFromFile(letterTwo, out wordsTwo);
        }
        SetRightWrongSounds();
    }
}

private string GetPath()
{
    // Returns the path of the active scene
    Debug.Log("Getting path...");
    return SceneManager.GetActiveScene().path.Remove(SceneManager.GetActiveScene().path.LastIndexOf('/') + 1);
}

private string GetFilePath(string path)
{
    // Returns the filepath of the active scene (removes "Assets/Resources/" part)
    Debug.Log("Getting filePath...");
    return path.Replace("Assets/Resources/", "");
}


public void SetRightWrongSounds()
{
    // sets right and wrong sounds
    wrongSoundDir = "mainMusic/general/wrong";
    rightSoundDir = "mainMusic/general/right";
}

private void GetLettersFromFile(out string posL, out string negL)
{
    if (path != null)
    {
        text = File.ReadAllText(path + "letters.txt");
        listOfCounterletters = text.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
        posL = listOfCounterletters[0];
        negL = listOfCounterletters[1];
    }
    else
    {
        Debug.LogError("Path not set (yet). Cannot set pos and neg letters.");
        posL = null;
        negL = null;
    }
}

private void GetWordsFromFile(string letter, out string[] words)
{
    if (letter != null)
    {
        text = File.ReadAllText(path + letter + ".csv");
        words = text.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
    }
    else
    {
        Debug.LogError("Letter not set (yet). Cannot set words list.");
        words = null;
    }
}

public void ChangeToScene(int number)
{
    if (number < SceneManager.sceneCountInBuildSettings)
    {
        if (SceneManager.GetActiveScene() != SceneManager.GetSceneByBuildIndex(number))
            SceneManager.LoadScene(number);
        else
            Debug.LogError("Scene already active.");
    }
    else
    {
        Debug.LogError("Scenenumber is not within the Build Settings.");
    }
}

public string GetLetterOne() { return letterOne; }
public string GetLetterTwo() { return letterTwo; }
public string[] GetWordsOne() { return wordsOne; }
public string[] GetWordsTwo() { return wordsTwo; }
public string GetDirectory() { return path; }
public string GetFileDirectory() { return filePath; }

public string GetWrongSoundDir() { return wrongSoundDir; }
public string GetRightSoundDir() { return rightSoundDir; }
}

So in the Unity Editor I simply do it by getting the path, where my scene is located (I need it to be like this and not in a separate scenes-folder, because I need several scenes with the same name but different content and the whole scene must be edited from people, who don't know how to use unity, so I want them to simply copy-paste a folder and replace the content, so they don't have to mess around with the code or even opening unity).

Afterwards I use the function GetLettersFromFile to get several letters (which will be changed the whole time, which is why I need it to be dynamic).

Then I have many scenes with different names and in the ones with the name "Words" or "Container" I have two .csv-files in the folder, where they are in, with all the names of sprites/sounds(same name), which need to be instantiated.

Eg I could insert 100 images and 100 sounds, but only write 10 of them in this list (csv), so only 10 of them need to be instantiated as a prefab-tile.

Here a sample of the code I use to intsantiate the tiles:

files_dir = sourceManager.GetComponent<SourceManager>().GetFileDirectory();

GameObject[] InitTiles(string myletter, string[] text)
{
    GameObject[] newObj = new GameObject[text.Length - 1];
    for (int i = 0; i < text.Length - 1; i++)
    {
        Debug.Log(files_dir + text[i]);
        zVal = -20.0f + ((i + 2) / 20);
        clone = (GameObject)Instantiate(tile, new Vector3(UnityEngine.Random.Range(-180, 180), UnityEngine.Random.Range(-50, 50), zVal), Quaternion.identity);
        clone.gameObject.GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>(files_dir + text[i]);
        clone.gameObject.GetComponent<AudioSource>().clip = Resources.Load<AudioClip>(files_dir + text[i]);
        clone.gameObject.GetComponent<Dragable>().dragable = true;
        clone.gameObject.GetComponent<Dragable>().letter = myletter;
        newObj[i] = clone;
    }
    return newObj;
}

As you can see I want them to be dragable and if I start to drag them, they shall play the sound they got once.

So all in all:

  • I read a csv-file and save the content as a string[]
  • I go through the string[] and instantiate a tile-prefab and set its sprite to an image from the folder with eg the name "mother.png" and its audioclip to a clip with the name "mother.wav"

And now I need to do the same for Android, but I just cannot figure out how. I read what felt like a million posts and tried www and streamingassests but it doesn't work so I thought maybe I'm just doing it wrong and I wanted to ask you guys and girls for help. I didn't want to post the wrong code, because it all just gets confusing.

I hope you have enough information. Please feel free to ask, if something's not clear yet.

Thank you very much!

Best, Nina

Screeshot of the location of all images for one example-level: location of sprites/sounds for the level words_easy_MN

all files within the mentioned directory

Example how it should look like later on Android

See example: in the middle there are two tiles. After reading the content from letters.txt and M.csv the algorithm sets the sound of the right tile to "M" and of the left one to "blume" (one example from the list). The right tile also gets the image of the "blume" (=a flower).

I already have everything in the folder. I need the tile to instantiate automatically from the list..

You need to re-write your code. Almost all of them. Unfortunately, I can't do that for you in this answer but will explain what's wrong and how to fix them.

The Resources folder is special and cannot be accessed with the File.XXX functions. The Resources.Load function is a special API dedicated for this. It's the only way to read from the Resources folder.

As shown in your screenshot, the file you want to read is from the "Resources/levels/words_diff_MN/" folder folder.

Let's say you want to load the "letters.txt" file from the "Resources/levels/words_diff_MN/" path, remove "Resources" from the path. Also remove the file extension. The final path to read from is levels/words_diff_MN/letters"

Resources.Load and TextAsset are used to load text files:

To load the "letters.txt" file from the "Resources/levels/words_diff_MN/" path:

TextAsset txtAsset = (TextAsset)Resources.Load("levels/words_diff_MN/letters")", typeof(TextAsset));
string textFile = txtAsset.text;

To load the M.mp3/ogg sound file:

AudioClip audio = Resources.Load("levels/words_diff_MN/M", typeof(AudioClip)) as AudioClip;

To load the blume image file:

Sprite sprite = Resources.Load("levels/words_diff_MN/blume", typeof(Sprite)) as Sprite;

If the blume image is marked as multi sprite then load it as an array:

Sprite[] sprite = Resources.LoadAll<Sprite>("levels/words_diff_MN/blume") as Sprite[];

You can find complete list of examples for other file types here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM