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にサーバから転送された次のデータが表示されます。