もふもふ技術部

IT技術系mofmofメディア

Unity(C#script)で外部のサーバーとHTTP通信を行う

UnityにおけるHTTP通信

内部で完結する簡単なゲームなどであればHTTP通信を行う必要性はあまりないことも多いですが、一定規模のシステムやランキング機能を実装したい場合はHTTP通信を行って外部のサーバーとデータのやり取りが必要になってくる場合があります。

UnityでHTTP通信をしたい場合、以下のような選択肢が存在するようです。

  1. WWWクラスを用いた通信
  2. UnityWebRequestを用いた通信
  3. HttpWebRequestを用いた通信
  4. その他(プラグインとか。アセットストア等)

それぞれ当然ながら特徴が存在しており、結論から言うとパフォーマンスなどの考慮も今回は必要ないので、スタンダードっぽいUnityWebRequestを使用して通信していきます。

ざっと調べてみた感じだと以下のような特徴がありました。

WWW

  • かなり昔から提供されている
  • コード量が少ない
  • コスパはあまりよくない

という感じでした。コード部分もWWWを使用している部分を抜粋すると下記のようになります。

IEnumerator TestConnect(){
        var www = new WWW (targetUrl);
        yeild return www;
        Debug.Log(www.responseHeaders)
    }

実際には通信が成功したかどうかの分岐などが必要ですが、このように使うことができます。

パット見もわかりやすく、これを使用しようかとも思いましたが、Visual Studioでこれを使用しようとすると古いからUnityWebRequest使っておけよというinfoがでてきたので、WWWの利用は見送りました。

UnityWebRequest

今回はこちらを採用しました。 WWWに代わる次世代のAPIとして5.4から正式にUnityに導入されています。WWWと比べてAPIのカスタマイズが柔軟にできるようになり、パフォーマンスの問題に直面しない限りはとりあえずこれを使っておいてもよさそうな気がしました。

多分規模が大きくなったり、大量の通信が必要になったになる場合に、

https://docs.unity3d.com/jp/540/ScriptReference/Networking.DownloadHandler.ReceiveData.html

にあるようなメインスレッドでコールバックが呼び出されることがネックになりそうな感じでしょうか。

実際にコードを書くとこんな感じになりました。

 IEnumerator GetData()
    {
        UnityWebRequest req = UnityWebRequest.Get(dataUrl);
        yield return req.SendWebRequest();

        if (req.isNetworkError || req.isHttpError)
        {
            Debug.Log(req.error);
        }
        else if (req.responseCode == 200)
        {
            Debug.Log(req.downloadHandler.text);
        }
    }

HttpWebRequest

これはC#標準のAPIです。Unityで使うこともできますがAndroid端末などで実機起動した場合に証明書のエラーがでるらしいです。 使おうとした場合はこんな感じで動きました。

var request = WebRequest.Create(dataUrl);
        request.Method = "Get";
        WebResponse response = null;
        try
        {
            response = request.GetResponse();
        }
        catch
        {
            response = null;
        }

        if (response != null)
        {
            Stream st = response.GetResponseStream();
            StreamReader sr = new StreamReader(st, Encoding.GetEncoding("UTF-8"));
            string txt = sr.ReadToEnd();
            sr.Close();
            st.Close();
        }

Webをメインにしているエンジニアからすると結構なじみがある構文をしており、安心感がありますね。

ただ先ほどの二種類よりもコードが長い。後は、iosだった場合も通信の無限リトライや最初に通信した接続から変更されない(途中でwifiにしたときにその後の通信がwifiに切り替わらない)などのエラー報告があるみたいでした。

通信してみる

基本的なUnityでC#を動かす方法についてはこちらを参照してください。

といっても、実際の通信の部分のコードは上記のUnityWebRequestにあるので、ここではUnityの通信のハマりポイントをすこし解説する程度です。

コルーチン

https://docs.unity3d.com/ja/2018.4/Manual/Coroutines.html 上を読めばほとんどわかります。

Unityの関数は呼び出すと、その関数のアクションは1つのフレーム内ですべて行われます。 以下は公式の例を借りたコードですが、対象のオブジェクトのアルファ値を少しづづ透過させるための関数です。

void Fade() 
{
    for (float f = 1f; f >= 0; f -= 0.1f) 
    {
        Color c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
    }
}

上記の関数は、例えばstart()関数に置いたとして呼び出したとしても、1ループ実行後にUnity側に再度レンダリングされるわけではなく、すべて実行された後にレンダリング処理が行われます。

結果として、この関数を実行すると即時に透明になり、意図した挙動になりません。

対処法としては、update関数にフレーム毎にアルファ値をマイナスしていくような処理を実行すれば、意図した挙動になります。

ただし、Unityでは基本的にこういった状況に対応するためにコルーチンという機能を利用することがよくあります。

コルーチンを実行することで実行を停止してUnity側に制御を戻し、その次のフレームで再実行することができます。

Http通信でも、コルーチンを使用しない場合、通信のタイミングで結果が返ってこないのでコルーチンを使用することになります。

コルーチンを実行するのはStartCoroutine関数を使います。 start関数に

   StartCoroutine(”ここに関数”);

コルーチンの再開コード部分は yield returnの部分になります。また、戻り値はIEnumeratorになるので、その宣言がしてあればコルーチンであると判断できるでしょう。

実際にAPIサーバーに向けてurlを設定して、getリクエストを送ってみます。 apidata

Debug.LogでJsonっぽいStringのデータが取得できました。

データが取れればあとはそのデータを欲しい形に成形したり、表示したりといろいろなことができるようになりますね。 サーバーを別建てするのが面倒だという人は、適当なAPIを提供してくれているサービスを使ってみるといいと思います。