1

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.

E.g. 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 e.g. 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..

Nin4ikP
  • 135
  • 1
  • 13
  • There is AssetBundle as you mentioned and also the Resources folder. I suggest you use `Resources.Load`. Once you get it working, switch to AssetBundle. Read the duplicate for how to use the Resources folder. If you can't still get it to work, modify your question and show the new code that's not working. – Programmer Jan 30 '18 at 13:21
  • Hello, the problem is not with Resource.Load but with the fact that I cannot find any solution to load the data after apk was build! Because I read the content from the file DURING the game and because it cannot find the right content with these names, again because there is no option to use Resource.Load DURING the game with a DYNAMIC filename, which, as I said, needs to be editable and not hardcoded... – Nin4ikP Jan 30 '18 at 13:29
  • No, they aren't - that's the problem :( But they are in the folder, where they are read from the file during the game... I'm sorry that it's all so confusing.. – Nin4ikP Jan 30 '18 at 13:45
  • I use a folder (project folder), where everything needed for the scene is in.. but after apk was build it will be different I think... There is this text-file named letters.txt. letters.txt extracts e.g. M and N. afterwards all words within M.csv and N.csv are extracted in two string[]s. Then I use the function InitTiles, which searches for the .png and .wav -files with the names from the string[]s. So it should load them from the same folder, where the scene is. – Nin4ikP Jan 30 '18 at 13:51
  • yes (edited my comment above) – Nin4ikP Jan 30 '18 at 13:56
  • Yes, kind of. I want to load only the ones in the csv mentioned ones. And when they are loaded I want to instantiate prefabs with the loaded sounds/images (see example) – Nin4ikP Jan 30 '18 at 14:11
  • It was a duplicate originally. Unfortunately, I cannot mark it as a duplicate again. I have decided to prepare answer for your case but it is a duplicate. My answer shows how to read your text, sound and image files. If you know these, you can use it to fix your code. Your whole code needs to be re-written. Let me know if you have any question – Programmer Jan 30 '18 at 14:31

1 Answers1

0

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.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Actually I must excuse myself. You were right the whole time. I tried everything with Resource.Load, but I overlooked that I did not use TextAsset. I tried AssetsPath and other but not TextAsset. Now everything works! You are great! Thank you! – Nin4ikP Jan 30 '18 at 15:12
  • 1
    @Nin4ikP if this answered solved your problem please select it as the correct answer to help future users who may run into the same problems you had. – AresCaelum Jan 30 '18 at 16:29
  • @Nin4ikP No problem. I gave up after reading your deleted comment. Glad you actually decided to try it and got it working. – Programmer Jan 31 '18 at 16:50