Unity(ver:2019.4.28f1)で、UnityWebRequest を使って HTTP(POST) 通信します。今回作成するHTTP通信スクリプト「ApiHttpTest.cs」の仕様を次に示します。

  • UnityWebRequest で HTTP(POST)通信
  • リクエスト、レスポンスは JSON 形式
  • 基底クラスを使った複数の通信 API の作成

スクリプトテンプレートの作成

プロジェクト「HttpTest」を作成し、Unityのスクリプトテンプレートを次のように作成します。

作成したスクリプト・クラスのフォルダ構成を次に示します。

HTTP通信スクリプトの作成

つぎのようにHTTP通信スクリプト「ApiHttpTest.cs」を作成します。作成したHTTP通信スクリプト「ApiHttpTest.cs」は、HierarchyのMainCameraにドラッグ&ドロップします。

  • 14-15行目で SoldierとWeaponを生成します。
  • 25行目の「sendApiSoldier」では Soldier、58行目の「sendApiWeapon」ではWeaponと通信します。それぞれのSendメソッド 内で JSON 形式にシリアライズされ、UnityWebRequest で HTTP(POST) 通信されます。通信完了をコールバックで受け取り、レスポンス構造体へデシリアライズします。
  • 25行目の「sendApiSoldier」では、「userId = “usersoldier”」「password = “abc123″」をjson形式で送信し、サーバから「’name’=>’tomosoft’」「 ‘age’=>2」をjson形式で受信します。
  • 58行目の「sendApiWeapon」では、「userId = “userweapon”」「valuesreq = { 1, 10, 100, 1000 }」をjson形式で送信し、サーバから「’count’=>5 」「‘valuesres’=> [1,2,3,4,5]」をjson形式で受信します。

ApiHttpTest.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ApiHttpTest : MonoBehaviour
{
    private Api.Soldier soldier;
    private Api.Weapon weapon;
    private Web.ApiBase.Result  result;

    private void Start()
    {
        // 通信用クラス生成
        soldier = new Api.Soldier();
        weapon = new Api.Weapon();

        // API 通信
        sendApiSoldier();
        sendApiWeapon();
    }

    /// <summary>
    /// API Soldier 通信
    /// </summary>
    private void sendApiSoldier()
    {
        // エンドポイントの設定
        soldier.EndPoint = Api.Soldier.Name;
        // リクエストパラメータを設定
        soldier.request.userId = "usersoldier";
        soldier.request.password = "abc123";

        // 通信
        soldier.Send<Api.Soldier.Request>(ref soldier.request, result => {

            // リザルト
            if(result.isSucceeded)  // 成功
            {
 
                // レスポンスを展開
                soldier.response = soldier.Response<Api.Soldier.Response>();

                // 内容確認
                Debug.Log("Soldier Succeed!!");
                Debug.Log("  name : " + soldier.response.name);
                Debug.Log("  age : " + soldier.response.age);
            }
            else                    // 失敗
            {
                Debug.Log("Soldier Failed : " + result.error);
            }
        });
    }

    /// <summary>
    /// API Weapon 通信
    /// </summary>
    private void sendApiWeapon()
    {
        // エンドポイントの設定
        weapon.EndPoint = Api.Weapon.Name;
        // リクエストパラメータを設定
        weapon.request.userId = "userweapon";
        weapon.request.valuesreq = new List<int>() { 1, 10, 100, 1000 };

        // 通信
        weapon.Send<Api.Weapon.Request>(ref weapon.request, result => {

            // リザルト
            if (result.isSucceeded)  // 成功
            {
                // レスポンスを展開
                weapon.response = weapon.Response<Api.Weapon.Response>();

                // 内容確認
                Debug.Log("Weapon Succeed!!");
                Debug.Log("  count : " + weapon.response.count);
                foreach(var v in weapon.response.valuesres)
                {
                    Debug.Log("  val : " + v);
                }
            }
            else                    // 失敗
            {
                Debug.Log("Weapon Failed : " + result.error);
            }
        });
    }

}

soldierクラスの作成

HTTP 通信クラス「ApiBase.cs」を基底クラスとして、各API のリクエストとレスポンスを定義した soldierクラス「soldier.cs」を作成します。

  • 10行目で、通信時の URL は共通部分をHTTP 通信クラス「ApiBase.cs」側で定義するので、末尾の API 名を定義します。
  • 15行目,26行目で、リクエストとレスポンス用の構造体を Serializable 属性で作成し、パラメータを定義します。

Api/soldier.cs

using System;

namespace Api
{
    /// <summary>
    /// API Soldier
    /// </summary>
    public class Soldier : Web.ApiBase
    {
        public const string Name = "soldier.php";

        /// <summary>
        /// リクエストパラメータ
        /// </summary>
        [Serializable]
        public struct Request
        {
            public string userId;
            public string password;
        }
        public Request request;

        /// <summary>
        /// レスポンスパラメータ
        /// </summary>
        [Serializable]
        public struct Response
        {
            public string name;
            public int age;
        }
        public Response response;
    }
}

HTTP 通信クラスの作成

HTTP 通信クラス「ApiBase.cs」を次のように作成します。

  • 77行目でUnityWebRequest を POST形式で生成し、リクエストパラメータをjson形式で設定します。詳細については「UnityWebRequest」を参照
  • 58-59行目でパラメータは string のままでは渡せないため、byte配列に変換します。詳細については「JSON Serialization」を参照
  • 64行目で、コルーチンを使ってHTTP(POST)通信を開始します。
  • 87-89行目で、通信結果は isNetworkError と isHttpError がともに false であることで成功と判定します。
  • 96行目でレスポンスはdownloadHandler に Json形式(text)で格納されます。

Utility/ApiBase.cs

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

namespace Web
{
    /// <summary>
    /// HTTP 通信クラス
    /// </summary>
    public class ApiBase
    {
        /// <summary>
        /// HTTP 通信の結果
        /// </summary>
        public struct Result
        {
            public bool     isSucceeded;    // true で成功
            public string   error;          // 失敗時のエラー内容

            /// <summary>
            /// 成功
            /// </summary>
            public void Suceeded()
            {
                isSucceeded = true;
                error = "";
            }

            /// <summary>
            /// 失敗
            /// </summary>
            /// <param name="error"></param>
            public void Failed(string error)
            {
                isSucceeded = false;
                this.error = error;
            }
        }

        // ベースURL
        private const string BaseUrl = "http://localhost/httptest/";  // 適切なパスを設定してください

        // レスポンス(JSON)
        private string resJson;
        // エンドポイント
        public string EndPoint { get; set; }

        /// <summary>
        /// リクエスト(オブジェクト)を JSON に変換して HTTP(POST)通信を行う
        /// </summary>
        /// <typeparam name="T">リクエスト型</typeparam>
        /// <param name="request">リクエストのオブジェクト</param>
        /// <param name="cb">コールバック</param>    
        public void Send<T>(ref T request, Action<Result> cb)
        {
            // リクエストオブジェクトを JSON に変換(byte配列)
            string reqJson = JsonUtility.ToJson(request);
            byte[] postData = System.Text.Encoding.UTF8.GetBytes(reqJson);

            // HTTP(POST)通信
            var url = BaseUrl + EndPoint;
            Debug.Log("Send URL : " + url);
            CoroutineHandler.StartStaticCoroutine(onSend(url, postData, cb));
        }

        /// <summary>
        /// HTTP(POST)通信の実行
        /// </summary>
        /// <param name="url">接続する URL</param>
        /// <param name="postData">POST するデータ</param>
        /// <param name="cb">コールバック</param>
        /// <returns>コルーチン</returns>
        private IEnumerator onSend(string url, byte[] postData, Action<Result> cb)
        {
            // HTTP(POST)の情報を設定
            var req = new UnityWebRequest(url, "POST");
            req.uploadHandler = (UploadHandler)new UploadHandlerRaw(postData);
            req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
            req.SetRequestHeader("Content-Type", "application/json");

            // API 通信(完了待ち)
            yield return req.SendWebRequest();

            // 通信結果
            Result result = new Result();
            if (req.isNetworkError ||
                 req.isHttpError)  // 失敗
            {
                Debug.Log("Network error:" + req.error);
                result.Failed(req.error);
            }
            else                    // 成功
            {
                Debug.Log("Succeeded:" + req.downloadHandler.text);
                resJson = req.downloadHandler.text;
                result.Suceeded();
            }
            cb(result);
        }

        /// <summary>
        /// レスポンス(JSON)からオブジェクトを生成して返す
        /// </summary>
        /// <typeparam name="T">レスポンス型</typeparam>
        /// <returns>レスポンスのオブジェクト</returns>
        public T Response<T>()
        {
            return JsonUtility.FromJson<T>(resJson);
        }

    }
}

コルーチンクラスの作成

HTTP 通信クラス「ApiBase.cs」は MonoBehaviour を継承していないので、次のようにコルーチンクラス「CoroutineHandler.cs」を作成します。

Utility/CoroutineHandler.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// MonoBehaviour を継承せずにコルーチンを使うためのクラス
/// 参考:https://qiita.com/Teach/items/2fa2b4fa4334a0a3e34d
/// </summary>
public class CoroutineHandler : MonoBehaviour
{
    // インスタンス
    static protected CoroutineHandler Instance;

    // インスタンス取得(DontDestroy な Object)
    static public CoroutineHandler instance
    {
        get
        {
            if (Instance == null)
            {
                GameObject obj = new GameObject("CoroutineHandler");
                DontDestroyOnLoad(obj);
                Instance = obj.AddComponent<CoroutineHandler>();
            }
            return Instance;
        }
    }

    /// <summary>
    /// オブジェクトが非アクティブまたは削除される際の処理(解放)
    /// </summary>
    public void OnDisable()
    {
        if(Instance)
        {
            Destroy(Instance.gameObject);
            Instance = null;
        }
    }

    /// <summary>
    /// コルーチンの起動
    /// </summary>
    /// <param name="coroutine">起動させるコルーチン</param>
    /// <returns>コルーチン</returns>
    static public Coroutine StartStaticCoroutine(IEnumerator coroutine)
    {
        return instance.StartCoroutine(coroutine);
    }

}

サーバ側スクリプトの作成

xampp環境下で次のphpアプリ「soldier.php」「weapon.php」を動作させ、UnityからのアクセスでJson形式で受け取ったデータにデータを追加して転送します。

  • 2行目で追加するデータ「’name’=>’tomosoft’, ‘age’=>2」を定義します。

httptest/soldier.php

<?php
$value_array = ['name'=>'tomosoft', 'age'=>2, 'value3'=>3, 'value4'=>4, 'value5'=>5];
 
try {
    //print_r(getallheaders() );
    //echo "body".file_get_contents('php://input');
    
    $post_body = file_get_contents('php://input');
    $obj = json_decode($post_body,true);
    if ($obj === NULL) return;

	//配列をJSON形式に変換
	$jsonstr =  json_encode($obj + $value_array);
	echo $jsonstr;
	
} catch (PDOException $e){
    var_dump($e->getMessage());
}
?>
  • 2行目で追加するデータ「’count’=>5, ‘valuesres’=> [1,2,3,4,5]」を定義します。

httptest/weapon.php

<?php
$value_array = ['count'=>5, 'valuesres'=> [1,2,3,4,5], 'value3'=>3, 'value4'=>4, 'value5'=>5];
 
try {
    //print_r(getallheaders() );
    //echo "body".file_get_contents('php://input');
    
    $post_body = file_get_contents('php://input');
    $obj = json_decode($post_body, true);
    if ($obj === NULL) return;

	//配列をJSON形式に変換
	$jsonstr =  json_encode($obj + $value_array);
	echo $jsonstr;
	
} catch (PDOException $e){
    var_dump($e->getMessage());
}
?>

HTTP通信スクリプトの実行

ゲームをプレーすると、Consoleにサーバから転送された次のデータが表示されます。