ゲーム開発
Unity
UnrealEngine
C++
Blender
Houdini
ゲーム数学
ゲームAI
グラフィックス
サウンド
アニメーション
GBDK
制作日記
IT関連
ツール開発
フロントエンド関連
サーバサイド関連
WordPress関連
ソフトウェア設計
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
ラーメン日記
四コマ漫画
その他
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • Blender
    • Houdini
    • ゲーム数学
    • ゲームAI
    • グラフィックス
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • IT関連
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • WordPress関連
    • ソフトウェア設計
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • ラーメン日記
    • 四コマ漫画
      • その他
        • おすすめアイテム
        • おもしろコラム
      1. ホーム
      2. 20211212_01

      【Unity】UnityWebRequestを使ってCRUD機能を実装する【Ruby on Rails】

      UnityRuby on Railsサーバサイド関連
      2021-12-12

      マイケル
      マイケル
      みなさんこんにちは!
      マイケルです!
      エレキベア
      エレキベア
      こんにちクマ〜〜〜
      マイケル
      マイケル
      今日は UnityWebRequest の使い方について見ていくよ!
      エレキベア
      エレキベア
      UnityWebRequestとは何クマ??
      マイケル
      マイケル
      UnityからWebサーバと通信するために使われる機能だよ!
      今回はこれを使って下記のような
      CRUD機能(作成、検索、更新、削除)を作ってみます!
      ↑今回作るもの
      エレキベア
      エレキベア
      WEBアプリとかで最初に作ったりするやつクマね
      マイケル
      マイケル
      コードはUnity側だけになるけどGitHubにあげているので
      参考にお使いください!

      GitHub(masarito617) – unity-web-request-sample

      マイケル
      マイケル
      それではやっていこう!
      エレキベア
      エレキベア
      楽しみクマ〜〜〜〜

      参考書籍

      マイケル
      マイケル
      今回は特に書籍は使わずに、
      下記の公式リファレンスを参考にしています!

      UnityWebRequest – Unityスクリプトリファレンス

      マイケル
      マイケル
      WEBAPIについては下記書籍がおすすめです!
      少し古いけどWebの歴史から知ることができるので読み物としても面白いですね。

      Webを支える技術 ―― HTTP,URI,HTML,そしてREST WEB+DB PRESS plus

      エレキベア
      エレキベア
      これは定番クマ〜〜〜

      UnityWebRequestの使い方

      マイケル
      マイケル
      まずは簡単な使い方についてみていこう!
      基本的には下記のようにリクエストを作成してレスポンスを受け取ります。
      SendWebRequest は非同期のためコルーチン内で実行する必要があります。
      //リクエスト作成
      UnityWebRequest request = UnityWebRequest.Get(【URL】);
      
      // リクエスト送信
      yield return request.SendWebRequest();
      
      // レスポンスを受け取る
      request.downloadHandler.text
      ↑基本的な使い方
      エレキベア
      エレキベア
      シンプルクマね
      マイケル
      マイケル
      リクエストに関しては GET/POST/PUT/DELETE
      それぞれ作成することができます。
      この時、POSTに関してはWWWFormを使用して値を送ることができます。
      UnityWebRequest.Get(【URL】);
      UnityWebRequest.Post(【URL】, 【データ】);
      UnityWebRequest.Put(【URL】, 【データ】);
      UnityWebRequest.Delete(【URL】);
      ↑リクエストの作成
      // formデータの作成
      WWWForm form = new WWWForm();
      form.AddField("name1", "value1");
      form.AddField("name2", "value2");
      
      // リクエスト作成
      UnityWebRequest request = UnityWebRequest.Post(【URL】, form);
      ↑WWWFormを使用したリクエスト作成
      エレキベア
      エレキベア
      値を送るのもこれなら簡単クマね
      マイケル
      マイケル
      最後にContent-Typeについてですが、
      UnityWebRequestはデフォルトでapplication/x-www-form-urlencodedで送信されます。
      application/jsonで送信したい場合には、下記のようにUploadHandlerRaw、DownloadHandlerBufferを自身でnewして送る必要があるようです。
      // byte配列に変換
      var bodyRaw = Encoding.UTF8.GetBytes(【JSON】);
      
      // リクエスト作成
      var request = new UnityWebRequest(【URL】, "POST");
      request.uploadHandler = new UploadHandlerRaw(bodyRaw);
      request.downloadHandler = new DownloadHandlerBuffer();
      request.SetRequestHeader("Content-Type", "application/json");
      ↑application/jsonでの送信
      エレキベア
      エレキベア
      JSON形式で送りたい場合もよくありそうクマね
      マイケル
      マイケル
      それじゃ使い方はこの辺にしておいて、さっそく使っていこう!

      CRUD機能の実装

      APIサーバの作成

      マイケル
      マイケル
      送信先が必要なので、簡単なAPIサーバを準備します。
      ここではRuby on Railsを使って、下記のBookテーブルを操作するだけの機能を実装しました。

      Bookテーブル構成

      本ID 名前 価格
      id : integer name : string price : integer
      エレキベア
      エレキベア
      かなりシンプルなテーブルクマね
      マイケル
      マイケル
      APIサーバの作成について詳細は省きますが、
      参考までにコマンドを載せておきます!
      # api_testプロジェクト作成
      rails new api_test --api --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-action-cable --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-action-cable
      cd api_test
      
      # Bookリソースの作成
      bin/rails g resource book name:string price:integer
      
      # テーブル作成
      bin/rails db:migrate
      ↑APIサーバの作成
      マイケル
      マイケル
      この状態で起動してhttp://127.0.0.1:3000にアクセスすると、
      お馴染みのWelcome画面が表示されます!
      # サーバ起動
      bin/rails server
      エレキベア
      エレキベア
      これだけでサーバ立てられるのは便利クマね〜〜〜
      マイケル
      マイケル
      resourceコマンドで作成したため、ルーティングは下記のように設定されています。
      これらの処理をcontrollerクラスに記述しましょう!
      GET    /books     books#index
      POST   /books     books#create
      PUT    /books/:id books#update
      DELETE /books/:id books#destroy
      ↑routes情報
      class BooksController < ApplicationController
        # 全てのBook情報を返す
        def index
          @books = Book.all()
          render json: {
            books: @books
          }
        end
      
        # Bookレコードを作成する
        def create
          @book = Book.new()
          @book.name = params[:name]
          @book.price = params[:price]
          if @book.save
            render json: @book
          end
        end
      
        # 指定IDのBookを更新する
        def update
          @book = Book.find(params[:id])
          @book.name = params[:name]
          @book.price = params[:price]
          if @book.save
            render json: @book
          end
        end
      
        # 指定IDのBookを破棄する
        def destroy
          @book = Book.find(params[:id])
          @book.destroy
        end
      end
      ↑各処理の記述
      マイケル
      マイケル
      これでサーバ側の最低限の準備はできました!
      あとは、http://127.0.0.1:3000/books にUnity側からリクエストするようにしていきましょう。
      エレキベア
      エレキベア
      楽しみクマ〜〜〜〜

      Unity側の実装

      UI構成
      マイケル
      マイケル
      UIの構成についてですが、
      今回は下記のような本アイテムのプレハブをScrollViewの中に生成する
      といった構成にしました。
      ↑本アイテムの構成
      ↑ScrollViewの中に作成する
      マイケル
      マイケル
      プレハブには下記のようなスクリプトをアタッチしておきます。
      事前に各UIを割り当てて置くことでGetComponentする数を減らすことができます。
      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      using UnityEngine.UI;
      
      /// <summary>
      /// 本アイテムクラス
      /// </summary>
      public class BookItemContent : MonoBehaviour
      {
          public Text idText;
          public InputField nameInput;
          public InputField priceInput;
          public Button updateButton;
          public Button deleteButton;
      }
      
      ↑本アイテムスクリプトの作成
      エレキベア
      エレキベア
      いちいち各フィールドをGetComponentしていたら効率が悪いクマね
      本リストの表示
      マイケル
      マイケル
      まずは本リストの表示機能から!
      下記のように、起動時とリロードボタン押下時に取得して表示するようにします。
      ↑本リストの表示
      エレキベア
      エレキベア
      GETリクエストを送信して
      レスポンスから本アイテムを生成するクマね
      マイケル
      マイケル
      まず本リスト全体を管理するスクリプトを作成し、共通で使用する送信処理を用意しました。
      下記のように
      リクエストとコールバックを渡して、レスポンスが返ってきたらコールバックを実行する
      といった処理にします。
          /// <summary>
          /// リクエストを送る
          /// </summary>
          /// <param name="request"></param>
          /// <param name="callback"></param>
          private IEnumerator SendRequest(UnityWebRequest request, Action<string> callback = null)
          {
              // リクエストを送る
              yield return request.SendWebRequest();
              
              // レスポンスを出力
              if (request.result == UnityWebRequest.Result.ConnectionError 
                  || request.result == UnityWebRequest.Result.ProtocolError)
              {
                  Debug.Log(request.error);
              }
              else
              {
                  // コールバックを実行
                  if (callback != null)
                  {
                      callback(request.downloadHandler?.text);
                  }
              }
          }
      ↑送信処理の作成
      マイケル
      マイケル
      そしてこの送信処理をGetBookList()内から呼び出します!
      using System;
      using System.Collections;
      using System.Collections.Generic;
      using System.Text;
      using UnityEngine;
      using UnityEngine.Networking;
      
      /// <summary>
      /// 本リスト管理クラス
      /// </summary>
      public class BookListManager : MonoBehaviour
      {
          [SerializeField] private GameObject bookScrollRect; // 本リストスクロールエリア
          [SerializeField] private GameObject bookItemPrefab; // 本アイテムPrefab
          
          /// <summary>
          /// 開始処理
          /// </summary>
          private void Start()
          {
              // 本情報を取得
              GetBookList();
          }
      
          /// <summary>
          /// 全ての本アイテムを取得する
          /// READ
          /// </summary>
          private void GetBookList()
          {
              // リクエストを送る
              var url = "http://127.0.0.1:3000/books";
              var request = UnityWebRequest.Get(url);
              StartCoroutine(SendRequest(request, response =>
              {
                  var schema = JsonUtility.FromJson<BookSchemaArray>(response);
                  foreach (var bookSchema in schema.books)
                  {
                      // 取得した本アイテムをリストに追加
                      var obj = Instantiate(bookItemPrefab, bookScrollRect.transform);
                      var bookItemContent = obj.GetComponent<BookItemContent>();
                      bookItemContent.idText.text = bookSchema.id.ToString();
                      bookItemContent.nameInput.text = bookSchema.name;
                      bookItemContent.priceInput.text = bookSchema.price.ToString();
                      bookItemContent.updateButton.onClick.AddListener(() => { PushUpdateButton(bookItemContent); });
                      bookItemContent.deleteButton.onClick.AddListener(() => { PushDeleteButton(bookSchema.id); });
                  }
              }));
          }
      
      ・・・略・・・
      
          /// <summary>
          /// 本リストをクリアする
          /// </summary>
          private void ClearBookList()
          {
              foreach (Transform child in bookScrollRect.gameObject.transform)
              {
                  Destroy(child.transform.gameObject);
              }
          }
      
          /// <summary>
          /// JSON型定義
          /// </summary>
          [Serializable]
          private class BookSchema
          {
              public int id;
              public string name;
              public int price;
          }
          [Serializable]
          private class BookSchemaArray
          {
              public BookSchema[] books;
          }
      
          // ---------- 各ボタン押下処理 ----------
          public void PushReloadButton()
          {
              ClearBookList();
              GetBookList();
          }
      
      ・・・略・・・
      
      }
      
      ↑取得処理の実装
      マイケル
      マイケル
      コールバック内で返ってきたJSONをclassに変換してUIに設定しています。
      JsonUtility.FromJsonを使って変換することができますが、classには[Serializable]を付ける必要があることには注意です。
      エレキベア
      エレキベア
      これで表示機能はできたクマね
      本アイテムの追加
      マイケル
      マイケル
      次にアイテムの追加について!
      入力された内容をPOSTリクエストで送信しますが、
      ここではapplication/jsonを使用した送信処理を実装してみました。
      ↑本アイテムの追加
          /// <summary>
          /// 本アイテムを追加する
          /// CREATE
          /// </summary>
          /// <param name="bookItemContent">追加する本情報</param>
          private void AddBookItem(BookItemContent bookItemContent)
          {
              // jsonデータの作成
              var schema = new BookSchema
              {
                  name = bookItemContent.nameInput.text,
                  price = int.Parse(bookItemContent.priceInput.text)
              };
              var json = JsonUtility.ToJson(schema);
              
              // byte配列に変換
              var bodyRaw = Encoding.UTF8.GetBytes(json);
              
              // リクエストを送る
              var url = "http://127.0.0.1:3000/books/";
              var request = new UnityWebRequest(url, "POST");
              request.uploadHandler = new UploadHandlerRaw(bodyRaw);
              request.downloadHandler = new DownloadHandlerBuffer();
              request.SetRequestHeader("Content-Type", "application/json");
              StartCoroutine(SendRequest(request, response =>
              {
                  // 本情報を再取得
                  ClearBookList();
                  GetBookList();
              }));
          }
      
      ・・・略・・・
          
          public void PushAddButton(BookItemContent bookItemContent)
          {
              AddBookItem(bookItemContent);
          }
      ↑追加処理の実装
      マイケル
      マイケル
      最小限のため、入力チェック処理が入っていないことには注意です!
      エレキベア
      エレキベア
      (手抜きクマ・・・。)
      本アイテムの更新
      マイケル
      マイケル
      次に本アイテムの更新処理について!
      これもapplication/jsonでPUTリクエストを送信します。
      ↑本アイテムの更新
          /// <summary>
          /// 本アイテムを更新する
          /// UPDATE
          /// </summary>
          /// <param name="bookItemContent">更新する本情報</param>
          private void UpdateBookItem(BookItemContent bookItemContent)
          {
              // jsonデータの作成
              var schema = new BookSchema
              {
                  name = bookItemContent.nameInput.text,
                  price = int.Parse(bookItemContent.priceInput.text)
              };
              var json = JsonUtility.ToJson(schema);
              
              // byte配列に変換
              var bodyRaw = Encoding.UTF8.GetBytes(json);
              
              // リクエストを送る
              var url = "http://127.0.0.1:3000/books/" + bookItemContent.idText.text;
              var request = new UnityWebRequest(url, "PUT");
              request.uploadHandler = new UploadHandlerRaw(bodyRaw);
              request.downloadHandler = new DownloadHandlerBuffer();
              request.SetRequestHeader("Content-Type", "application/json");
              StartCoroutine(SendRequest(request, response =>
              {
                  // 本情報を再取得
                  ClearBookList();
                  GetBookList();
              }));
          }
      
      ・・・略・・・
      
          private void PushUpdateButton(BookItemContent bookItemContent)
          {
              UpdateBookItem(bookItemContent);
          }
      ↑更新処理の実装
      エレキベア
      エレキベア
      追加処理とほぼ同じクマね
      本アイテムの削除
      マイケル
      マイケル
      そして最後に削除処理について!
      これはシンプルでidを付与したURLをDELETE送信するだけになります。
      ↑本アイテムの削除
          /// <summary>
          /// 本アイテムを削除する
          /// DELETE
          /// </summary>
          /// <param name="id">削除する本ID</param>
          private void DeleteBookItem(int id)
          {
              // リクエストを送る
              var url = "http://127.0.0.1:3000/books/" + id;
              var request = UnityWebRequest.Delete(url);
              StartCoroutine(SendRequest(request, response =>
              {
                  // 本情報を再取得
                  ClearBookList();
                  GetBookList();
              }));
          }
      
      ・・・略・・・
      
          private void PushDeleteButton(int id)
          {
              DeleteBookItem(id);
          }
      ↑削除処理の実装
      マイケル
      マイケル
      以上で最低限のCRUD機能の実装完了です!!
      エレキベア
      エレキベア
      楽勝だったクマね
      WWWFormを使用する場合
      マイケル
      マイケル
      ちなみにWWWFormでPOST、PUTリクエストを送る場合には
      下記のようにリクエストを作成します!
      PUTメソッドではWWWFormを使用できないため、
      POSTリクエスト作成後にrequest.methodにPUTを指定するようにします!
      
          /// <summary>
          /// 本アイテムを追加する(WWWForm使用)
          /// CREATE
          /// </summary>
          /// <param name="bookItemContent">追加する本情報</param>
          private void AddBookItemForm(BookItemContent bookItemContent)
          {
              // formデータの作成
              var form = new WWWForm();
              form.AddField("name", bookItemContent.nameInput.text);
              form.AddField("price", int.Parse(bookItemContent.priceInput.text));
              
              // リクエストを送る
              var url = "http://127.0.0.1:3000/books";
              var request = UnityWebRequest.Post(url, form);
              StartCoroutine(SendRequest(request, response =>
              {
                  // 本情報を再取得
                  ClearBookList();
                  GetBookList();
              }));
          }
      
          /// <summary>
          /// 本アイテムを更新する(WWWForm使用)
          /// UPDATE
          /// </summary>
          /// <param name="bookItemContent">更新する本情報</param>
          private void UpdateBookItemForm(BookItemContent bookItemContent)
          {
              // formデータの作成
              var form = new WWWForm();
              form.AddField("name", bookItemContent.nameInput.text);
              form.AddField("price", int.Parse(bookItemContent.priceInput.text));
      
              // リクエストを送る
              var url = "http://127.0.0.1:3000/books/" + bookItemContent.idText.text;
              var request = UnityWebRequest.Post(url, form);
              request.method = "PUT"; // PUTを指定
              StartCoroutine(SendRequest(request, response =>
              {
                  // 本情報を再取得
                  ClearBookList();
                  GetBookList();
              }));
          }
      ↑WWWFormを使用した送信
      マイケル
      マイケル
      というのも、htmlのformでもPUT、DELETE送信がサポートされていないためだと思われます。
      htmlから送る場合にもhiddenパラメータとして_methodを指定して送るのが定番となっているため、同じようなイメージで使用するといいと思います。
      エレキベア
      エレキベア
      とりあえずWWWFormを使う時にはPOST送信クマね

      おわりに

      マイケル
      マイケル
      というわけで今回はUnityでCRUD機能を実装してみました!
      どうだったかな?
      エレキベア
      エレキベア
      クライアントサーバ連携がめんどくさそうだったクマが、
      やってみたら思っていたよりも簡単に出来てびっくりしたクマ
      マイケル
      マイケル
      通信処理はソシャゲを作る際には避けて通れないので
      どんどん触って慣れていこう!
      マイケル
      マイケル
      それでは今日はこの辺で!
      アデュー!!!
      エレキベア
      エレキベア
      クマ〜〜〜〜〜

      【Unity】UnityWebRequestを使ってCRUD機能を実装する【Ruby on Rails】〜完〜


      UnityRuby on Railsサーバサイド関連
      2021-12-12

      関連記事
      【Unity】Timeline × Excelでスライドショーを効率よく制作する
      2024-10-31
      【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する
      2024-05-28
      【Unity】GoでのランキングAPI実装とVPSへのデプロイ方法についてまとめる【Go言語】
      2024-04-14
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30
      【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-28
      【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜
      2024-03-28
      【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
      2024-03-27