Search Unity

Question Use unitywebrequest without coroutine

Discussion in 'Scripting' started by chengwang2077, Jul 21, 2020.

  1. chengwang2077

    chengwang2077

    Joined:
    Nov 23, 2019
    Posts:
    131
    In order to use unitywebrequest, my code is full of coroutines and delegates. These grammars make my code very complicated. UniRx allows me to use network requests in a concise syntax, but it does not support UnityWebRequest. What should I do?
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    You are not required to use coroutines with UnityWebRequest. You can do something like this if you really wanted to:

    Code (CSharp):
    1. UnityWebRequestAsyncOperation asyncOperation;
    2.  
    3. void Start() {
    4.   asyncOperation = UnityWebRequest.Get("myurl.com").SendWebRequest();
    5. }
    6.  
    7. void Update() {
    8.   if (asyncOperation.isDone) {
    9.     // Done!
    10.     string result = asyncOperation.webRequest.downloadHandler.text;
    11.   }
    12. }
     
    Hikiko66 and chengwang2077 like this.
  3. AnthonySharp

    AnthonySharp

    Joined:
    Apr 11, 2017
    Posts:
    88
    EDIT: forgive the awful code formatting

    I've never used UniRx before but I'm working on a project right now that uses a bunch of networking so...

    Because networking code inevitably involves sending and requesting lots of different kinds of data and then doing all sorts of different things to that data, it's somewhat unavoidable that it will end up being quite complicated in terms of how those networks requests are handled. But there are a few ways to simplify things.

    As PraetorBlue says, you don't have to use coroutines. I hadn't thought about using the Update() function for networking calls, which is actually quite clever. But I think the only issue is that once you chuck in your error handling etc, you might find it turns out to be just as complicated and untidy as using coroutines anyway.

    Your second option is to simply to use synchronous networking calls. This would usually be considered bad practice (and will actually cause your code to freeze if there is no network connection etc and you don't handle that properly), but the upshot is that it is very easy to manage IF you can get away with it. It's also a good solution for networking calls that don't require a value to be returned (because you can just send it and forget about it, rather than having to wait around for a response). For example you can create a template function:

    Code (CSharp):
    1. public static void DoPostRequest(string url, string[] fields, string[] values)
    2.     {
    3.         WWWForm form = new WWWForm();
    4.  
    5.         for (int i = 0; i < fields.Length; i++)
    6.         {
    7.             form.AddField(fields[i], values[i]);
    8.         }
    9.  
    10.         UnityWebRequestAsyncOperation wr = UnityWebRequest.Post(url, form).SendWebRequest();
    11.     }
    It then becomes very easy to call it however you like:

    Code (CSharp):
    1. public static void BlockUser(string blockwhom)
    2.     {
    3.         DoPostRequest("https://www.myrandomserver.co.uk/blocks/blockuser.php",
    4.             new string[] { "blockwhom" },
    5.             new string[] { blockwhom } );
    6.     }
    Even asynchronous calls don't necessarily have to be complicated:

    Code (CSharp):
    1. IEnumerator DownloadImageAndApplyToRawImage(string imageid, RawImage therawimage)
    2.     {
    3.         UnityWebRequest www = UnityWebRequestTexture.GetTexture("https://www.myserver.com/downloadimage.php?imageid=" + imageid);        
    4.        
    5.         yield return www.SendWebRequest();
    6.  
    7.         if (www.isNetworkError || www.isHttpError) Debug.Log("Network error: " + www.error);
    8.         else if(therawimage) therawimage.texture = ((DownloadHandlerTexture)www.downloadHandler).texture;
    9.         // garbage collection ?
    10.     }
    After that you can call them just like any other function.

    I was taking a look at possibly using the async operator to simplify things but supposedly that is not something that is straightforward to implement with UnityWebRequest because it is not something that comes as standard with Unity (https://forum.unity.com/threads/do-...-and-unitywebrequest-with-async-await.589870/). Not even sure how helpful it would be anyhow, since I've never used it.

    If you want to get really serious, you could even create some kind of download/upload handler class that stores retrieved networking data. But, again, whether that would result in "simpler" code is debatable.

    Perhaps someone with more experience than I might have more of an idea about what might be useful for you.
     
    chengwang2077 likes this.
  4. crevelop

    crevelop

    Joined:
    Nov 9, 2010
    Posts:
    13
    Hey guys,

    You might find my UnityWebRequest async tutorial useful.



    I'm doing just that, using UnityWebRequest within an async function instead of coroutines.

    The tutorial expands into deserializing JSON, using dependency injection and other perks.

    I hope it helps, cheers :)
     
    Hikiko66, x1alphaz1 and JDeyby like this.
  5. x1alphaz1

    x1alphaz1

    Joined:
    Dec 19, 2020
    Posts:
    23

    the cleanest implementation i have ever seen so far... i wish u do more videos like these
    thanks
     
  6. mike25z

    mike25z

    Joined:
    Jun 10, 2018
    Posts:
    1
    I am following this same Tutorial, but my machine is locking up because of the while statement. What am I doing wrong?
    The URL is valid and returns if I step through with the debugger, but this doesn't work otherwise.


    Code (CSharp):
    1.  
    2. public async Task<T> Get<T>(string value,string url)
    3.     {
    4.         url = $"{url}&query={value}&timezone_module=1";
    5.         var request = UnityWebRequest.Get(url);
    6.         request.SetRequestHeader("Content-Type", "json/application");
    7.         var operation = request.SendWebRequest();
    8.  
    9.         while(!operation.isDone)
    10.         {
    11.             await Task.Yield();
    12.         }
    13.  
    14.         var jsonResonse = request.downloadHandler.text;
    15.         if (request.result != UnityWebRequest.Result.Success)
    16.         {
    17.             Debug.Log($"Failed: {request.error}");
    18.         }
    19.  
    20.         try
    21.         {
    22.             var result = JsonConvert.DeserializeObject<T>(jsonResonse);
    23.             Debug.Log($"Success: {request.downloadHandler.text}");
    24.             return result;
    25.         }
    26.         catch(Exception ex)
    27.         {
    28.             Debug.LogError($"Could not parse response {jsonResonse}.{ex}");
    29.             return default;
    30.         }
    31.                  
    32.     }
    33.  
    34. public void SetLocation(string locationInfo)
    35.     {
    36.         _location = apiClient.Get<Location>(locationInfo,apiClient.LocationURL).Result;
    37.         Debug.Log(_location.Data[0].Name.ToString());
    38.     }
    39.