MrRobot9
MrRobot9

Reputation: 2684

Unity3D: Don't continue the code until a large 3D model file is downloaded

There are around 10 3D models that I need to load from the server on a button click.

public GameObject DownloadFile(String url)
{
    GameObject obj = null;
    Debug.Log("url: " + url);

    string path = GetFilePath(url);
    Debug.Log("Path: " + path);

    if (File.Exists(path))
    {
        Debug.Log("Found file locally, loading...");
        Debug.Log(path);
        obj = LoadModel(path);

        return obj;
    }
    Debug.Log(path);


    StartCoroutine(GetFileRequest(url, (UnityWebRequest req) =>
    {
        if (req.isNetworkError || req.isHttpError)
        {
            // Log any errors that may happen
            Debug.Log($"{req.error} : {req.downloadHandler.text}");
        }
        else
        {
            // Save the model into a new wrapper
            obj = LoadModel(path);

            obj.SetActive(false);

            StopAllCoroutines();

        }
    }));



    return obj;
}


string GetFilePath(string url)
{
    filePath = $"{Application.persistentDataPath}/Files/";
    string[] pieces = url.Split('/');
    string filename = pieces[pieces.Length - 1];

    return $"{filePath}{filename}";
}



IEnumerator GetFileRequest(string url, Action<UnityWebRequest> callback)
{
    using (UnityWebRequest req = UnityWebRequest.Get(url))
    {
        req.downloadHandler = new DownloadHandlerFile(GetFilePath(url));
        yield return req.SendWebRequest();
        callback(req);
    }
}

GameObject LoadModel(string path)
{
    ResetWrapper();
    GameObject model = Importer.LoadFromFile(path);



    return model;
}

This is my code right now. I call the DownloadFile function from my script with a URL as a parameter. The Download function returns a null obj because it takes time to download a huge file. So it returns obj without getting downloaded. How do I not make it return until the download is finished?

Upvotes: 0

Views: 112

Answers (1)

derHugo
derHugo

Reputation: 90649

if you would do what you ask for you would completely freeze your app ... if this is what you want you could of course instead of running a Coroutine simply do

var request = GetFileRequest(url, (UnityWebRequest req) =>
{
    if (req.isNetworkError || req.isHttpError)
    {
        // Log any errors that may happen
        Debug.Log($"{req.error} : {req.downloadHandler.text}");
    }
    else
    {
        // Save the model into a new wrapper
        obj = LoadModel(path);

        obj.SetActive(false);

        StopAllCoroutines();
    }
}

while(request.MoveNext())
{
    // freeze
}

return obj;

In most cases however this is absolutely not what you want ;)


Instead of giving it a return type I would rather solve this via a callback:

public void DownloadFile(String url, Action<GameObject> onResult)
{
    GameObject obj = null;
    Debug.Log("url: " + url);

    string path = GetFilePath(url);
    Debug.Log("Path: " + path);

    if (File.Exists(path))
    {
        Debug.Log("Found file locally, loading...");
        Debug.Log(path);
        obj = LoadModel(path);

        onResult?.Invoke(obj);
    }
    Debug.Log(path);


    StartCoroutine(GetFileRequest(url, (UnityWebRequest req) =>
    {
        if (req.isNetworkError || req.isHttpError)
        {
            // Log any errors that may happen
            Debug.LogError($"{req.error} : {req.downloadHandler.text}");
        }
        else
        {
            // Save the model into a new wrapper
            obj = LoadModel(path);

            obj.SetActive(false);

            StopAllCoroutines();

            onResult?.Invoke(obj);
        }
    }));
}

So instead of e.g. doing something like

var result = DownloadFile(url);

you would rather do

DownloadFile(url, result => {
    // whatever to do with the result object
});

Upvotes: 2

Related Questions