ガンズターン 公式サイト

楽しいことに、まじめです。 ——ガンズターンアプリ研究所公式サイト

Unity備忘録 #5 HTTP非同期通信を行う汎用的なクラスを作る

Pocket

0. Unityの勉強会に参加してきました

お疲れ様です。ガンズターンのRyosukeです。
先日初めて、Unity関係の勉強会(Unityもくもく会@大宮)に参加してきまして、いろいろなUnityユーザの方々とお知り合いになることができました。
ためになるお話もたくさん聞かせて頂き、ポジティブな刺激を受けるとともに、また一つUnityの理解を進めることができたと思います。

主催してくださったのは、こちらのブログで初心者向けにUnity5の解説をしてくださっているhiyotamaさんです。

「Unity5(C#)初心者・入門者向けチュートリアル ひよこのたまご」
http://hiyotama.hatenablog.com

ブロック崩しの作り方から始まり、さまざまな初心者のつまずきやすいポイントを大変丁寧に解説してくださっています。
わたしも、たくさんお世話になっています。(端末へのデータ保存の仕方とか)

本当に、一人で家でコーディングするよりも何倍も有意義な時間でした。
hiyotamaさん、初心者のわたしの変な質問にも丁寧にお答えいただきありがとうございました! m(_ _)m
またぜひ、機会がありましたら参加させていただきたく思います。

てなわけで、このテンションが下がらないうちに、先送りにしていたUnityにおけるHttp通信のことでもまとめておこうと思います。

……といっても、まとめるほど難しいことはないうえに、すでにわかりやすくまとめてくださっているサイトがいくつもありますので、そちらを参考にするのもよいかと……。(なんて身も蓋もないことを……苦笑)

んでは、始めますね。

1. わたしが参考にしたサイト

この記事を書くにあたり、わたしが参考にしたサイトは以下の通りです。
お世話になりました。

Lonely Mobiler さん「Unity から GET/POST でサーバにアクセスする」 http://loumo.jp/wp/archive/20140218000116/

cloned.log さん「UnityのWWW、WWWFormのハマりどころ」
http://d.hatena.ne.jp/cloned/20140609

Unity公式スクリプトリファレンス「WWW」
http://docs.unity3d.com/jp/current/ScriptReference/WWW.htm

同上 「WWWForm」
http://docs.unity3d.com/jp/current/ScriptReference/WWWForm.html

上記サイトを読むことで、この記事の先を読む必要がなくなります。やったね!(身も蓋もないk以下略)

2. とくに押さえておきたいところ

UnityにおけるHTTP通信において、押さえておきたいところは大体以下の通りです。

  • GETもPOSTも「WWW」クラスを用いて行う。
  • GETをしたい時は「WWW www = new WWW(url);」の一行でOK。
    →非同期処理にしたい場合はコルーチンの中でこれを行う。
  • 接続の結果、取得されたデータは、そのデータの形式に応じて「WWW」クラスのインスタンスのプロパティに自動的に設定される。
    →例えばテキストを取得した場合は「www.text」に設定される。
  • POSTをしたい時は、まず「WWWForm」クラスのインスタンス(例えばform)を作成して、サーバに送信するデータをその中に「AddField()」して格納する。
  • それから「WWW www = new WWW(url, form);」の一行でPOST開始。
  • 接続の後はGETと同じでWWWクラスのインスタンスのプロパティを参照して接続結果を得る。
  • SSL通信については、公式には対応してない模様。残念……。(iPhoneだけできる?)

最初「?」となりやすいのは、明示的に「POST」通信をさせるメソッドがないこと。
これ、WWWクラスを作成する時に、WWWFormが与えられていれば「POST」に、与えられていなかったら「GET」に自動的になるそうです。

そのため、「送信データ」のないPOST通信をしたい時にも、以下のようにダミーのWWWFormを作る必要があります。(cloned.logさんの記事より)

WWWForm form = new WWWForm();
form.AddField("dummy", "dummy");

WWW www = new WWW(url, form);

……なんでこんな作りにしたんだろう?
その方が、Web関係にあまり詳しくない人でも理解しやすいということなのかな。

3. (おまけ)自作の汎用クラスでもさらします

さて。
上記の勘所を押さえれば、多少プログラムの経験がある人ならなんにも迷う部分はないと思います。
それぐらい、UnityのHTTP通信は簡単。(ただ接続するだけなら)

あまりにも簡単すぎてすぐに記事が終わってしまうので、ご参考までにわたしが作ったHTTP通信用の汎用クラスでもさらそうかと思います。
(いささか冗長なコードになってしまってますが、逆にその方がわかりやすいかと思ってこのままにしてます……という苦しい言い逃れ。笑)

非同期のURL接続、結果取得完了時のコールバック処理等、Web連携で必要な仕組みは大体実装されてると思いますので、使おうと思えば便利に使えるのではないでしょうか。

具体的な使用方法はコードのあとで。

using UnityEngine;
using System.Collections;

public class HttpController : MonoBehaviour 
{
    // デリゲートの定義(接続完了後のコールバック呼び出し用)
    // tagは、ハンドラがなんのHTTP通信の完了を受け取ったか判断するのに用いる
    // 任意の文字列。必要なければ無視してください。
    
    // GET(tagなし)
    public delegate void OnCompletionGet(WWW www);
    public delegate void OnErrorGet(WWW www);
    
    // POST(tagなし)
    public delegate void OnCompletionPost(WWW www);
    public delegate void OnErrorPost(WWW www);
    
    // GET(tagあり)
    public delegate void OnCompletionGetWithTag(WWW www, string tag);
    public delegate void OnErrorGetWithTag(WWW www, string tag);
    
    // POST(tagあり)
    public delegate void OnCompletionPostWithTag(WWW www, string tag);
    public delegate void OnErrorPostWithTag(WWW www, string tag);

    // GET(tagなし)の開始処理
    public void StartGet (OnCompletionGet completion,
                          OnErrorGet errorHandler,
                          string url)
    {
        StartCoroutine(HttpGetCoroutine(
            completion,
            errorHandler,
            url
        ));
    }
    
    // GET(tagなし)を実際に処理してるコルーチン
    private IEnumerator HttpGetCoroutine (
        OnCompletionGet completion,
        OnErrorGet errorHandler,
        string url)
    {
        WWW www = new WWW(url);
        yield return www;

        if(www.error == null)
        {
            //接続成功
            completion(www);
        }
        else
        {
            //エラー処理
            errorHandler(www);
        }
    }

    // GET(tagあり)の開始処理
    public void StartGet (OnCompletionGetWithTag completion,
                          OnErrorGetWithTag errorHandler,
                          string url, string tag)
    {
        StartCoroutine(HttpGetCoroutine(
            completion,
            errorHandler,
            url, tag
        ));
    }
        
    // GET(tagあり)を実際に処理してるコルーチン
    private IEnumerator HttpGetCoroutine (
        OnCompletionGetWithTag completion,
        OnErrorGetWithTag errorHandler,
        string url, string tag)
    {
        WWW www = new WWW(url);
        yield return www;

        if(www.error == null)
        {
            //接続成功
            completion(www, tag);
        }
        else
        {
            //エラー処理
            errorHandler(www, tag);
        }
    }

    // POST(tagなし)の開始処理
    public void StartPost (
        OnCompletionPost completion,
        OnErrorPost errorHandler,
        string url, WWWForm form)
    {
        StartCoroutine(HttpPostCoroutine(
            completion,
            errorHandler,
            url, form));
    }
    
    // POST(tagなし)を実際に処理してるコルーチン
    private IEnumerator HttpPostCoroutine (
        OnCompletionPost completion,
        OnErrorPost errorHandler,
        string url, WWWForm form)
    {
        WWW www = new WWW(url, form);
        yield return www;

        if(www.error == null)
        {
            //接続成功
            completion(www);
        }
        else
        {
            //エラー処理
            errorHandler(www);
        }
    }

    // POST(tagあり)の開始処理
    public void StartPost (
        OnCompletionPostWithTag completion,
        OnErrorPostWithTag errorHandler,
        string url, WWWForm form, string tag)
    {
        StartCoroutine(HttpPostCoroutine(
            completion,
            errorHandler,
            url, form, tag));
    }

    // POST(tagあり)を実際に処理してるコルーチン
    private IEnumerator HttpPostCoroutine (
        OnCompletionPostWithTag completion,
        OnErrorPostWithTag errorHandler,
        string url, WWWForm form, string tag)
    {
        WWW www = new WWW(url, form);
        yield return www;

        if(www.error == null)
        {
            //接続成功
            completion(www, tag);
        }
        else
        {
            //エラー処理
            errorHandler(www, tag);
        }
    }
}

4.「HttpContoller」の使い方

① Http接続をしたいシーンに空のGameObjectを追加して「HttpController」という名前に変更します。

② ①で作った「HttpController」に「HttpController」のスクリプトをくっつけます。

③ 以下のようなスクリプト「HttpTest.cs」を作成して、適当なGameObjectに貼り付けます。

using UnityEngine;
using System.Collections;

public class HttpTest : MonoBehaviour 
{
    public HttpController httpController;
    
    public void Start ()
    {
        HttpGetTest();
        HttpPostTest();
    }
    
    //GETのテスト
    public void HttpGetTest ()
    {
        string url = "(接続したいURL)";
        string tag = "httpGetTest";
        httpController.StartGet(OnComplete, OnError, url, tag);
    }
        
    //POSTのテスト
    public void HttpPostTest ()
    {
        string url = "(接続したいURL)";
        
        WWWForm form = new WWWForm();
        form.AddField("dummy", "dummy");
        
        string tag = "httpPostTest";
        httpController.StartPost(OnComplete, OnError, url, form, tag);
    }
    
    //接続成功(GET、POST共通)
    private void OnComplete(WWW www, string tag)
    {
        Debug.Log("Get Complete ! tag : " + tag 
                         + ", data : " + www.text);
    }

    //エラー処理(GET、POST共通)
    private void OnError(WWW www, string tag)
    {
        Debug.Log("Http Error ! tag : " + tag 
                         + ", error : " + www.error);
    }
}

④ くっつけ終えたらそのGameObjectのInspectorに「HttpController」という項目があるはずなので、そこに①で作った「HttpController」をドラッグ&ドロップ。

⑤ エディタ上の「プレイ」ボタンを押す。

⑥ コンソールに接続結果の文字列(HTML等)が表示されたら成功!

なお、この例ではあまり有効活用してませんが、tagを利用すると、いくつもHTTP通信を非同期で走らせて、呼び出されたハンドラ側でtagに応じて処理を変える、みたいな使い方もできます。

かなり冗長なコードになってしまったような気がしますが、ひとまず今日はこのへんで。
(一応、自分のアプリではこのままのコードで今の所うまく動いてます)

ガンズターンのRyosukeでした! m(_ _)m

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

トラックバックURL: http://www.gunsturn.com/2015/08/30/studying_unity_005/trackback/