Using UnityWebRequest to Download Resources in Unity

by SlideFactory

“Learn how to efficiently use the UnityWebRequest class to download many different resources from the web.”

Overview

In this post, we’ll cover different ways that we can make network requests using Unity’s UnityWebRequest class. Nowadays in app development, more data is coming from websites and APIs. Using this data allows us to take our games and apps to the next level. We can enable features like leader boards, user logins, and cloud data backups. But to do this, we need to know how to interact with web servers. Let’s dive in!

If you’re coming from the video you can find the NetworkingManager.cs script in the scripts section below.

Using UnityWebRequest.Get

Firstly, we’ll cover a simple example of how to request data from a web server. For brevity, we assume that you are familiar with web servers and making HTTP requests. If not, there are a lot of great resources available. Check out this article on codecademy.

Scene Setup

To start, let’s set up our scene in Unity. Open a fresh project and select the Game tab to preview our UI. Click Create > UI > Button. This will create a Canvas element holding your button. Now, let’s position our button. Add an empty GameObject under your Canvas named “Button Row”. Select it and then hit Add Component in the inspector. Search for “Horizontal Layout Group” and add it to your Button Row. Next, on your Button Row’s Rect Transform, click the Anchor Presets box. Hold down shift + alt and click top left to anchor your Button Row to the top left of the UI. Drag your Button under your Button Row and it should position itself in the top left of the screen.

The reason we added the Horizontal Layout Group is so that we can easily add more buttons in a row when we need to make more requests. Next, let’s dig into our coding out the network request.

Coding a GET UnityWebRequest

Firstly, create a new C# script and name it NetworkManager.cs. We’ll come back to this script during the tutorial to add more request types. Once you open it up, we’re going to add a simple GetRequest method. This might look different than what you’re used to, but I’ll explain why we use this approach.

using System.Collections;
using System;
using UnityEngine;
using UnityEngine.Networking;

public class NetworkManager : MonoBehaviour
{
    IEnumerator GetRequest(string url, Action<UnityWebRequest> callback)
    {
        using (UnityWebRequest request = UnityWebRequest.Get(url))
        {
            // Send the request and wait for a response
            yield return request.SendWebRequest();
            callback(request);
        }
    }
}

This method doesn’t seem to do much, however, it let’s us flexibly make GET requests and pass the response from the server to a callback that we can process in other functions. Next, we’ll see this in action.

// ... NetworkManager.cs
public void GetPosts()
{
    StartCoroutine(GetRequest("https://jsonplaceholder.typicode.com/posts", (UnityWebRequest req) =>
    {
        if (req.isNetworkError || req.isHttpError)
        {
            Debug.Log($"{req.error}: {req.downloadHandler.text}");
        } else
        {
            Debug.Log(req.downloadHandler.text);
        }
    }));
}

Now, go back into the Unity Editor. Add an empty GameObject, name it “Network Manager” and add your NetworkManager.cs script onto it. Then, select your Button and add a new click handler. Drag your Network Manager onto it and select the GetPosts method to run. Hit Play, click your button, and you should see a debug message of 100 fake placeholder posts from the jsonplaceholder website.

Parsing JSON Requests into C# Objects

If we were requesting images, or binary files, our job would be mostly done (We’ll cover those cases in a bit). But most of the time, working with web requests involves parsing JSON. Now that we have a JSON string, let’s look at how to get it into a C# object that we can use.

To do this, we’ll need to install the Json.NET package. The easiest way to do this is through the Asset Store. Simply search for json .net and download the “JSON .NET For Unity” asset and import it into your project. Now, we are able to serialize JSON strings returned from servers into objects to use in our code. We’ll do this by Deserializing the server response using Json.NET.

Let’s start by creating a Post class that each of our returned posts will populate.

public class Post
{
    public int userId;
    public int id;
    public string title;
    public string body;
}

Second, we’ll deserialize our server response into a list of Post Objects that we can use.

public void GetPosts()
{
    StartCoroutine(GetRequest("/posts", (UnityWebRequest req) =>
    {
        if (req.isNetworkError || req.isHttpError)
        {
            Debug.Log($"{req.error}: {req.downloadHandler.text}");
        } else
        {
            Post[] posts = JsonConvert.DeserializeObject<Post[]>(req.downloadHandler.text);

            foreach(Post post in posts)
            {
                Debug.Log(post.title);
            }
        }
    }));
}

Save your NetworkManager.cs file and go into Play mode. When you click you’re button you should now see 100 debug messages displaying each post’s title.

Using UnityWebRequest for Sounds, Images

Knowing how to retrieve and parse JSON responses is important skill for most app developers today, however, you’ll likely need to grab sounds, images, or regular files from web servers at some point. Each section below will outline how to do exactly that with Unity.

Downloading Images

Downloading an image is very similar to the request we’ve already made. The one key difference is that we use a UnityWebRequestTexture object, then we use a DownloadHandlerTexture helper class to extract the Texture2D.

// Inside NetworkingManager.cs
using UnityEngine.UI

// ...

public Image image;

// ...

public void DownloadImage(string url)
{
    StartCoroutine(ImageRequest(url, (UnityWebRequest req) =>
    {
        if (req.isNetworkError || req.isHttpError)
        {
            Debug.Log($"{req.error}: {req.downloadHandler.text}");
        } else
        {
            // Get the texture out using a helper downloadhandler
            Texture2D texture = DownloadHandlerTexture.GetContent(req);
            // Save it into the Image UI's sprite
            image.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
        }
    }));
}

IEnumerator ImageRequest(string url, Action<UnityWebRequest> callback)
{
    using (UnityWebRequest req = UnityWebRequestTexture.GetTexture(url))
    {
        yield return req.SendWebRequest();
        callback(req);
    }
}

Updating the Scene

Now that we have the ability to download images, let’s add some logic in our Unity scene to handle it. In your scene, under your Button Row, duplicate a button and name it “Download Image”, then change its text to the same. Afterwards, click on the button object and add a new click listener, then drag in the Network Manager and select the DownloadImage method to run on click. You will need to input the image URL on the click handler to pass to the DownloadImage method. I’m going to use a placeholder image service to download 200 x 200 image, https://i.picsum.photos/id/1072/200/200.jpg.

After you have this set up, click on your Canvas item and go to Create -> UI -> Image. Make the image’s height and width 200. Next, drag the image onto your Network Manager in the Image reference field we set up above. Now, click Play and click your newly created button. The image should update with the one you chose to download.

Using UnityWebRequest to Download Sounds

By now, you’re probably comfortable with downloading items from servers. Sounds are very similar to downloading images, however, we use the UnityWebRequestMultimedia class and GetAudioClip method instead. That is paired with the DownloadHandlerAudioClip class to get the AudioClip from the request:

// In NetworkManager.cs

// ...
public AudioSource audioSource;

// ...
public void DownloadSound(string url)
{
    StartCoroutine(SoundRequest(url, (UnityWebRequest req) =>
    {
        if (req.isNetworkError || req.isHttpError)
        {
            Debug.Log($"{req.error}: {req.downloadHandler.text}");
        }
        else
        {
            // Get the sound out using a helper class
            AudioClip clip = DownloadHandlerAudioClip.GetContent(req);
            // Load the clip into our audio source and play
            audioSource.Stop();
            audioSource.clip = clip;
            audioSource.Play();
        }
    }));
}

IEnumerator SoundRequest(string url, Action<UnityWebRequest> callback)
{
    // Note, we try to download an OGGVORBIS (ogg) file because Windows doesn't support
    // MPEG readily. If you're on a mac, you can try MPEG (mp3)
    using (UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.OGGVORBIS))
    {
        yield return req.SendWebRequest();
        callback(req);
    }
}

You’ll notice the subtle differences with helper classes, but it’s very similar to loading images! One thing to note, we need to specify the type of audio file to the GetAudioClip method. AudioType.OGGVORBIS is just one of many you can try. We chose it because Windows doesn’t readily support the MPEG option, or mp3.

Configuring the Scene

Finally, let’s jump into our scene and configure it to download and play our sound. Follow the same process as for the image. Duplicate a button, rename it and change the text to “Download Sound.” Update the click function to be “DownloadSound” and enter a sample URL for an ogg file that you can download. We’re using https://file-examples.com/wp-content/uploads/2017/11/file_example_OOG_1MG.ogg to test. Lastly, click on your NetworkManager and add an AudioSource component. Then drag the NetworkManager GameObject into the NetworkManager.cs script’s audioSource reference. Hit Play and test!

Conclusion

In conclusion, UnityWebRequest gives us a lot of options when downloading files and data from the internet. These are just some of the helper classes, check out the Unity docs for a lot more. In Part 2, we’ll look at downloading 3D model files using the DownloadHandlerFile, how to cache them locally to speed up subsequent downloads, and how to load them into Unity at run time. See you then!

Page 2 is now available

You can find it on this page.

Scripts

// NetworkingManager.cs
using System.Collections;
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using Newtonsoft.Json;

public class NetworkManager : MonoBehaviour
{
    public Image image;
    public AudioSource audioSource;

    public void GetPosts()
    {
        StartCoroutine(GetRequest("https://jsonplaceholder.typicode.com/posts", (UnityWebRequest req) =>
        {
            if (req.isNetworkError || req.isHttpError)
            {
                Debug.Log($"{req.error}: {req.downloadHandler.text}");
            } else
            {
                Post[] posts = JsonConvert.DeserializeObject<Post[]>(req.downloadHandler.text);

                foreach(Post post in posts)
                {
                    Debug.Log(post.title);
                }
            }
        }));
    }

    IEnumerator GetRequest(string endpoint, Action<UnityWebRequest> callback)
    {
        using (UnityWebRequest request = UnityWebRequest.Get(endpoint))
        {
            // Send the request and wait for a response
            yield return request.SendWebRequest();

            callback(request);
        }
    }

    public void DownloadImage(string url)
    {
        StartCoroutine(ImageRequest(url, (UnityWebRequest req) =>
        {
            if (req.isNetworkError || req.isHttpError)
            {
                Debug.Log($"{req.error}: {req.downloadHandler.text}");
            } else
            {
                // Get the texture out using a helper downloadhandler
                Texture2D texture = DownloadHandlerTexture.GetContent(req);
                // Save it into the Image UI's sprite
                image.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
            }
        }));
    }

    IEnumerator ImageRequest(string url, Action<UnityWebRequest> callback)
    {
        using (UnityWebRequest req = UnityWebRequestTexture.GetTexture(url))
        {
            yield return req.SendWebRequest();
            callback(req);
        }
    }

    public void DownloadSound(string url)
    {
        StartCoroutine(SoundRequest(url, (UnityWebRequest req) =>
        {
            if (req.isNetworkError || req.isHttpError)
            {
                Debug.Log($"{req.error}: {req.downloadHandler.text}");
            }
            else
            {
                // Get the sound out using a helper downloadhandler
                AudioClip clip = DownloadHandlerAudioClip.GetContent(req);
                // Load the clip into our audio source and play
                audioSource.Stop();
                audioSource.clip = clip;
                audioSource.Play();
            }
        }));
    }

    IEnumerator SoundRequest(string url, Action<UnityWebRequest> callback)
    {
        using (UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.OGGVORBIS))
        {
            yield return req.SendWebRequest();
            callback(req);
        }
    }
}

// Data Classes
public class Post
{
    public int userId;
    public int id;
    public string title;
    public string body;
}
Share this article:

You might also like: