Unity3D Android: как динамически загружать изображения и звуки (из файла) из папки после сборки?

Изменить: поскольку он был отмечен как дубликат Использование папки ресурсов в Unity, мне нужно говорят, что это другое. Поскольку я ЗНАЮ, как использовать папку ресурсов, и если бы вы прочитали весь текст, вы бы увидели, что я уже использую ее, как сказано в сообщении. У меня проблемы с выполнением этого, когда сборка уже завершена, поскольку файл .apk не позволяет мне получить доступ к папке впоследствии, если я не создал экземпляры всех изображений заранее. ПОЖАЛУЙСТА, прочтите его еще раз и не помечайте как дубликат, особенно если проблема уже указана в заголовке как другая ... Спасибо!

Оригинал:

Я новичок в Stack Overflow, поэтому надеюсь, что дам вам всю необходимую информацию.

Я разрабатываю серьезную игру в Unity3D, где мне нужно создать экземпляры различного количества сборных плиток и динамически изменять их спрайты и звуки при изменении сцены. Я объясню больше после кода.

Вот код, который отлично работает в редакторе:

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; }
}

Поэтому в редакторе Unity я просто делаю это, получая путь, по которому находится моя сцена (мне нужно, чтобы она была такой, а не в отдельной папке сцен, потому что мне нужно несколько сцен с тем же именем, но с разным содержимым и вся сцена должна редактироваться людьми, которые не знают, как использовать единство, поэтому я хочу, чтобы они просто скопировали-вставили папку и заменили контент, чтобы им не пришлось возиться с кодом или даже открывать единство ).

После этого я использую функцию GetLettersFromFile, чтобы получить несколько букв (которые будут постоянно меняться, поэтому мне нужно, чтобы они были динамическими).

Затем у меня есть много сцен с разными именами, а в тех, что с именем «Слова» или «Контейнер», у меня есть два .csv-файла в папке, где они находятся, со всеми именами спрайтов / звуков (одно и то же) , который необходимо создать.

Например. Я мог бы вставить 100 изображений и 100 звуков, но записать только 10 из них в этот список (csv), поэтому только 10 из них нужно создать как префаб-тайл.

Вот пример кода, который я использую для связывания плиток:

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;
}

Как видите, я хочу, чтобы их можно было перетаскивать, и если я начну их перетаскивать, они будут воспроизводить звук, который у них был однажды.

Итак, в общем:

  • Я читаю csv-файл и сохраняю содержимое в виде строки []
  • Я просматриваю строку [] и создаю экземпляр плитки-префаба и устанавливаю его спрайт на изображение из папки, например, название "mother.png" и его аудиоклип к клипу с названием "mother.wav"

И теперь мне нужно сделать то же самое для Android, но я просто не могу понять, как это сделать. Я прочитал то, что казалось миллионом сообщений, и попробовал www и streamingassests, но это не сработало, поэтому я подумал, может быть, я просто делаю это неправильно, и хотел попросить вас, ребята, о помощи. Я не хотел публиковать неправильный код, потому что все это просто сбивает с толку.

Надеюсь, у вас достаточно информации. Не стесняйтесь спрашивать, если что-то еще не ясно.

Большое тебе спасибо!

Бест, Нина

Скрининг расположения всех изображений для одного уровня-примера: расположение спрайтов / звуков для уровня words_easy_MN

все файлы в указанном каталоге

Пример того, как это должно выглядеть позже на Android

См. Пример: посередине две плитки. После чтения содержимого из файлов letter.txt и M.csv алгоритм устанавливает звук правой плитки на «M», а для левой - на «blume» (один пример из списка). Правый тайл также получает изображение «голубого» (= цветка).

У меня уже все в папке. Мне нужно, чтобы плитка создавалась автоматически из списка ..


person Nin4ikP    schedule 30.01.2018    source источник
comment
Как вы упомянули, есть AssetBundle, а также папка Resources. Я предлагаю вам использовать Resources.Load. Когда все заработает, переключитесь на AssetBundle. Прочтите дубликат, чтобы узнать, как использовать папку «Ресурсы». Если вы все еще не можете заставить его работать, измените свой вопрос и покажите новый код, который не работает.   -  person Programmer    schedule 30.01.2018
comment
Здравствуйте, проблема не в Resource.Load, а в том, что я не могу найти никакого решения для загрузки данных после сборки apk! Поскольку я читаю содержимое файла ВО ВРЕМЯ игры и потому что он не может найти нужный контент с этими именами, опять же, потому что нет возможности использовать Resource.Load ВО ВРЕМЯ игры с ДИНАМИЧЕСКИМ именем файла, которое, как я сказал, должно быть редактируемым и не жестко запрограммированным ...   -  person Nin4ikP    schedule 30.01.2018
comment
Нет, нет - вот в чем проблема :( Но они в той папке, куда читаются из файла во время игры ... Извините, что все так запутано ..   -  person Nin4ikP    schedule 30.01.2018
comment
Я использую папку (папка проекта), в которой есть все необходимое для сцены .. но после сборки apk, думаю, все будет по-другому ... Там есть текстовый файл с именем letter.txt. letter.txt извлечения, например M и N. после этого все слова в M.csv и N.csv извлекаются в две строки [] s. Затем я использую функцию InitTiles, которая ищет файлы .png и .wav с именами из строки [] s. Поэтому он должен загружать их из той же папки, где находится сцена.   -  person Nin4ikP    schedule 30.01.2018
comment
да (отредактировал мой комментарий выше)   -  person Nin4ikP    schedule 30.01.2018
comment
Да вроде как. Я хочу загрузить только те, которые указаны в csv. И когда они загружены, я хочу создать экземпляры префабов с загруженными звуками / изображениями (см. Пример)   -  person Nin4ikP    schedule 30.01.2018
comment
Изначально это был дубликат. К сожалению, я не могу снова пометить его как дубликат. Решил подготовить ответ по вашему делу, но это дубликат. Мой ответ показывает, как читать ваши текстовые, звуковые и графические файлы. Если вы их знаете, вы можете использовать их для исправления кода. Весь ваш код нужно переписать. Дайте мне знать, если у вас возникнут вопросы   -  person Programmer    schedule 30.01.2018


Ответы (1)


Вам нужно переписать свой код. Практически все из них. К сожалению, я не могу сделать это за вас в этом ответе, но объясню, что не так и как их исправить.

Папка "Ресурсы" является особой, и к ней нельзя получить доступ с помощью функций File.XXX. Для этого предназначена функция Resources.Load. Это единственный способ читать из папки "Ресурсы".

Как показано на снимке экрана, файл, который вы хотите прочитать, находится в папке "Resources/levels/words_diff_MN/".

Допустим, вы хотите загрузить файл "letter.txt" из пути "Resources/levels/words_diff_MN/", удалить "ресурсы" из пути. Также удалите расширение файла. Последний путь для чтения - levels/words_diff_MN/letters"

Resources.Load и TextAsset используются для загрузки текстовых файлов:

Чтобы загрузить файл "letter.txt" по пути "Resources/levels/words_diff_MN/":

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

Чтобы загрузить звуковой файл M.mp3 / ogg:

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

Чтобы загрузить файл изображения blume:

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

Если изображение blume помечено как мультиспрайт, загрузите его как массив:

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

Вы можете найти полный список примеров для других типов файлов здесь.

person Programmer    schedule 30.01.2018
comment
На самом деле я должен извиниться. Ты был прав все время. Я пробовал все с Resource.Load, но упустил из виду, что не использовал TextAsset. Я пробовал AssetsPath и другие, но не TextAsset. Теперь все работает! Ты замечательный! Спасибо! - person Nin4ikP; 30.01.2018
comment
@ Nin4ikP, если этот ответ решил вашу проблему, выберите его как правильный ответ, чтобы помочь будущим пользователям, которые могут столкнуться с теми же проблемами, что и вы. - person AresCaelum; 30.01.2018
comment
@ Nin4ikP Нет проблем. Я сдался после прочтения вашего удаленного комментария. Рад, что вы действительно решили попробовать и все заработало. - person Programmer; 31.01.2018