简体   繁体   English

Unity3D Android:构建后如何从文件夹动态加载图像和声音(从文件)?

[英]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. 编辑:由于它被标记为在Unity使用资源文件夹的副本,所以我需要说它是不同的。 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. 在构建完成后,这样做很麻烦,因为如果没有事先实例化所有图像,.apk文件将不允许我随后访问该文件夹。 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. 我是Stack Overflow的新手,所以希望我能为您提供所需的所有信息。

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. 我正在Unity3D中开发“严肃游戏”,我需要实例化各种预制砖并在场景变化时动态地更改其精灵和声音。 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). 因此,在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). 之后,我使用功能GetLettersFromFile来获取几个字母(将在整个过程中更改它,这就是为什么我需要它是动态的)。

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. 然后,我有许多名称不同的场景,在那些名称为“ Words”或“ Container”的场景中,我在文件夹中有两个.csv文件,它们都带有精灵/声音的所有名称(同名) ,需要实例化。

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. 例如,我可以插入100个图像和100个声音,但在此列表(csv)中仅写入10个图像和声音,因此仅需要将其中10个实例化为预制砖。

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[] 我读取了一个csv文件,并将内容另存为字符串[]
  • 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" 我遍历string []并实例化tile-prefab,并将其精灵设置为名称为“ mother.png”的文件夹中的图像,并将其音频片段设置为名称为“ mother.wav”的剪辑

And now I need to do the same for Android, but I just cannot figure out how. 现在我需要为Android做同样的事情,但是我只是想不出办法。 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. 我读了大概一百万篇的文章,并尝试了www和streamingassests,但是它没有用,所以我认为也许我做错了,所以我想问问男女。 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 一个示例级别的所有图像的位置的摘要: 级别words_easy_MN的精灵/声音的位置

all files within the mentioned directory 提及目录中的所有文件

Example how it should look like later on Android 示例稍后在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). 在从letters.txt和M.csv中读取内容之后,该算法会将右图块的声音设置为“ M”,将左图块的声音设置为“ blume”(列表中的一个示例)。 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. 资源文件夹是特殊的, 不能使用File.XXX函数访问。 The Resources.Load function is a special API dedicated for this. Resources.Load函数是专用于此的特殊API。 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. 如屏幕快照所示,您要读取的文件来自"Resources/levels/words_diff_MN/"文件夹文件夹。

Let's say you want to load the "letters.txt" file from the "Resources/levels/words_diff_MN/" path, remove "Resources" from the path. 假设您要从"Resources/levels/words_diff_MN/"路径中加载“ letters.txt”文件,并从该路径中删除 “资源”。 Also remove the file extension. 同时删除文件扩展名。 The final path to read from is levels/words_diff_MN/letters" 读取的最终路径是“ levels/words_diff_MN/letters"

Resources.Load and TextAsset are used to load text files: Resources.LoadTextAsset用于加载文本文件:

To load the "letters.txt" file from the "Resources/levels/words_diff_MN/" path: 要从"Resources/levels/words_diff_MN/"路径加载“ letters.txt”文件:

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

To load the M.mp3/ogg sound file: 加载M.mp3 / ogg声音文件:

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 . 您可以在此处找到其他文件类型的示例的完整列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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