1
\$\begingroup\$

I have an issue with my texture loader, everything works fine but once the function runs Unity freezes until the files are loaded into memory. These two are the functions i use to load the textures and add them to an array.

public void LoadTextureArray(string _dirname)
    {
        var fileNames = Directory.GetFiles(Application.persistentDataPath + "/" + _dirname + "/", "*.jpg");
        source = new Texture2D[fileNames.Length];

        Array.Sort(fileNames, (s1, s2) => Path.GetFileName(s1).CompareTo(Path.GetFileName(s2)));

        int index = 0;
        foreach (var fileName in fileNames)
        {
            source[index] = LoadTextureFromPath(fileName);
            source[index].name = fileName;
            index++;
        }
    }

public Texture2D LoadTextureFromPath(string path)
    {
        if (string.IsNullOrEmpty(path))
        {
            Debug.LogError("Texture path is null or empty");
            return null;
        }

        if (!File.Exists(path))
        {
            Debug.LogError($"File not found: {path}");
            return null;
        }

        byte[] bytes = File.ReadAllBytes(path);
        Texture2D texture = new Texture2D(512, 512, TextureFormat.RGB24 , false);
        texture.LoadImage(bytes);
        Debug.Log("success");
        return texture;
    }

Now i understand i need to make it async in order for it to work on the background but honestly i have no idea how to proceed, i did read a bit on various forums but most of what i found used WWW and even though i did try some parts i was not successful.

Edit: Ok so for starters i cannot understand which of those two functions i need to make asynchronous. i found a thread which contained this code

Texture2D _content;
Material _material;

// Call this with StartCoroutine(LoadTexture(path));
IEnumerator LoadTexture(string path)
{
    // Start the asynchronous load.
    var www = new WWW("file://" + path);

    // Let the game continue running until the texture has been loaded.
    yield return www;

    // Replace current content with loaded texture.
    if (_content != null)
        Destroy(_content);
    _content = www.texture;

    // Instantiate a copy of the material, 
    // and hang onto it so we can clean up when finished with it
    if (_material == null)
        _material = GetComponent<Renderer>().material;

    _material.mainTexture = www.texture;
}

void OnDestroy()
{
    // Clean up created instances
    // (these aren't immediately garbage-collected otherwise)
    if(_material != null)
         Destroy(_material);
    if(_content != null)
         Destroy(_content);
}

but i could not make it work by replacing it with this

IEnumerator LoadTexture(string _dirname)
    {
        var fileNames = Directory.GetFiles(Application.persistentDataPath + "/" + _dirname + "/", "*.jpg");
        // Start the asynchronous load.
        var www = new WWW(fileNames.ToString());

        source = new Texture2D[fileNames.Length];

        Array.Sort(fileNames, (s1, s2) => Path.GetFileName(s1).CompareTo(Path.GetFileName(s2)));

        //Debug.Log(string.Join("\n", fileNames));

        int index = 0;
        foreach (var fileName in fileNames)
        {
            // Let the game continue running until the texture has been loaded.
            yield return www;
            source[index] = LoadTextureFromPath(fileName);
            source[index].name = fileName;
            index++;
        }
    }

and even though this works it still freezes the game abit and it takes more time to load the textures than when the game freezes, maybe i should be making the texture into a asynchronous event but i do not know how to.

Edit: by placing the yield in the foreach loop it seems to be working but is this the way it should be done?

Edit: ok so after some trial and error i think i have gotten to a decent point. this part here works in the way that it loads everything in the background without freezing the game and it loads them as fast as i would like but is it correct?

code is this

    private IEnumerator GetTextureFromPath(string _dirname)
    {
        var fileNames = Directory.GetFiles(Application.persistentDataPath + "/" + _dirname + "/", "*.jpg");
        source = new Texture2D[fileNames.Length];

        Array.Sort(fileNames, (s1, s2) => Path.GetFileName(s1).CompareTo(Path.GetFileName(s2)));

        int index = 0;

        foreach (var fileName in fileNames)
        {
            UnityWebRequest webRequest = UnityWebRequestTexture.GetTexture(fileName);

            yield return webRequest.SendWebRequest();
            while (!webRequest.isDone)
            {
                yield return null;
            }

            Debug.Log("Ends server comm");
            if (webRequest.isNetworkError || webRequest.isHttpError)
            {
                Debug.Log(webRequest.error);
            }
            else
            {
                source[index] = ((DownloadHandlerTexture)webRequest.downloadHandler).texture;
                source[index].name = fileName;
                index++;
            }
        }

    }
```
\$\endgroup\$
4
  • \$\begingroup\$ What specifically did you not understand from the resources you've found so far on asynchronous loading? If you don't tell us that, we might try to explain it the same way you've already seen it explained in the past, which apparently hasn't helped you. The more precisely you can describe the specific problem you have applying your research to date, the better we can target answers to solve that problem. \$\endgroup\$
    – DMGregory
    May 26, 2021 at 19:43
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$
    – DMGregory
    May 26, 2021 at 20:20
  • \$\begingroup\$ If the code achieves your goals, what would make it "incorrect"? \$\endgroup\$
    – DMGregory
    May 26, 2021 at 22:02
  • \$\begingroup\$ just to make sure by someone that knows more that this is indeed an async texture loader ! and thank you for the help! \$\endgroup\$
    – stratos la
    May 26, 2021 at 22:04

1 Answer 1

0
\$\begingroup\$

You don't need both

yield return webRequest.SendWebRequest();

and

while (!webRequest.isDone)
{
    yield return null;
}

You can choose one or the other. The first says "call me back when the load is 100% finished", the other says "call me back every frame, and I'll keep looping until it's finished" (useful if you wanted to put code inside that while loop that updates a progress bar or somesuch, which you're not doing here).

Otherwise, this looks fine.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .