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

      【Unity】第二回 ソーシャルゲームを作る 〜ユーザ登録処理の実装〜【AWS × Laravel】

      UnityLaravelサーバサイド関連AWSソーシャルゲーム
      2020-11-23

      マイケル
      マイケル
      みなさんこんにちは!
      マイケルです!!
      エレキベア
      エレキベア
      クマ〜〜〜〜!!
      マイケル
      マイケル
      Laravelも勉強したことだし、
      ソシャゲ開発の続きを進めるぞーー!!
      エレキベア
      エレキベア
      そういえばソシャゲの環境構築をやっていたクマね
      マイケル
      マイケル
      その通り!
      今回は UnityとLaravelを連携させたユーザ登録処理 を実装していくぞ!
      エレキベア
      エレキベア
      これは楽しみクマ

      参考書籍

      マイケル
      マイケル
      前回に引き続き、こちらの参考書を参考に進めていきます!

      エレキベア
      エレキベア
      書いてある内容がなかなか難しいクマね
      マイケル
      マイケル
      俺もLaravel勉強してやっと内容がわかってきたところだよ・・・。

      実装イメージ

      マイケル
      マイケル
      実装イメージはこんな感じだ!


      ① クライアントからサーバへユーザ登録リクエストを送る。
      ② サーバ側のDB登録を実施する。
      ③ サーバからクライアントへ登録結果レスポンスを送る。
      ④ クライアント側のDB登録を実施する。

      エレキベア
      エレキベア
      サーバ側もクライアント側も登録するクマね
      マイケル
      マイケル
      使うたびにDBアクセスするわけにもいかないから
      ローカルにも保存しておくわけだね
      マイケル
      マイケル
      実際に動かしてみるとこのようになります!

      ↑登録するユーザ名を入力して登録する。

      ↑登録完了後、ユーザ名が表示される。

      ↑MySQL登録結果

      ↑SQLite登録結果
      マイケル
      マイケル
      画面の動きは少ないけど、サーバ・クライアント共に
      DB登録できる状態です!
      ここまでできるように実装していきましょう!
      エレキベア
      エレキベア
      新たな扉クマ〜〜〜〜〜

      テーブルの作成

      マイケル
      マイケル
      まずはサーバ側のテーブルを作成しましょう!
      テーブル作成の細かい手順は過去の記事で紹介しているのでご参考ください!
      エレキベア
      エレキベア
      なつかしクマ〜〜〜〜
      マイケル
      マイケル
      今回はユーザを登録するための UserProfile テーブルを作成します!
      php artisan make:migration create_user_profile_table
      ↑マイグレーションファイルの作成
      <?php
      
      use Illuminate\Database\Migrations\Migration;
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Support\Facades\Schema;
      
      class CreateUserProfileTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('user_profile', function (Blueprint $table) {
                  $table->string('user_id', 37)->charset('utf8');
      			$table->string('user_name', 32)->charset('utf8');
      			$table->unsignedInteger('banana')->default(0);
      			$table->unsignedInteger('banana_free')->default(0);
      			$table->unsignedInteger('friend_coin')->default(0);
      			$table->unsignedSmallInteger('tutorial_progress')->default(0);
                  $table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
      			$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'));
      			$table->primary('user_id');
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('user_profile');
          }
      }
      
      マイケル
      マイケル
      ファイルを作成したらマイグレーションを実行しましょう!
      php artisan migrate
      マイケル
      マイケル
      これでテーブルが作成されました!
      ついでに後々使用する、モデルファイルも作成しておきましょう!
      <?php
      
      namespace App;
      
      use Illuminate\Database\Eloquent\Model;
      
      class UserProfile extends Model
      {
          protected $table = 'user_profile';
          public $incrementing = false;
          protected $primaryKey = 'user_id';
          public $timestamps = false;
      }
      
      エレキベア
      エレキベア
      準備完了クマ〜〜〜〜〜

      クライアント側の実装

      マイケル
      マイケル
      テーブルを作成したら、クライアント側の実装に入りましょう!
      下記の部分になります!
      エレキベア
      エレキベア
      サーバにリクエストを送る処理と
      サーバからレスポンスを受け取ってSQLiteに保存する処理クマね
      マイケル
      マイケル
      その通り!
      画面はシンプルに下記のような感じです!
      マイケル
      マイケル

      ① 入力フォームにユーザ名を入力して決定ボタン押下
      ② 入力した名前でユーザ情報を登録してスタートボタンとユーザ名を表示
      の流れになります!
      エレキベア
      エレキベア
      (このゲームはなんなんだクマ・・・。)

      SQLiteUnityKitのインポート

      マイケル
      マイケル
      それではまずはUnityでSQLiteが使えるように
      SQLiteUnityKitをインポートしましょう!
      マイケル
      マイケル
      僕は下記サイトを参考にしながら導入しました!

      参考サイト:
      UnityでSQLiteを扱う方法

      ① SQLiteUnityKitをクローンする
      git clone https://github.com/Busta117/SQLiteUnityKit.git
      ② クローンしたファイルを下記に配置する
      マイケル
      マイケル
      導入は以上で完了です!!
      エレキベア
      エレキベア
      簡単2ステップクマ〜〜〜〜

      SQLiteのテーブル作成処理

      マイケル
      マイケル
      SQLiteのアクセス方法は、以下のようになります!
      下記のようにUserProfileテーブルのクラスを作成しましょう!
      using System;
      
      [Serializable]
      public class UserProfileModel
      {
      	public string user_id;
      	public string user_name;
      	public int banana;
      	public int banana_free;
      	public int friend_coin;
      	public int tutorial_progress;
      }
      
      public static class UserProfile
      {
      	public static void CreateTable()
          {
      		string query = "create table if not exists user_profile (user_id text, user_name text, banana int, banana_free int, friend_coin int, tutorial_progress int, primary key(user_id))";
      		SqliteDatabase sqlDB = new SqliteDatabase(GameUtil.Const.SQLITE_FILE_NAME);
      		sqlDB.ExecuteQuery(query);
      	}
      
      	public static void Set(UserProfileModel user_profile)
          {
      		string query = "insert or replace into user_profile (user_id, user_name, banana, banana_free, friend_coin, tutorial_progress) values (\"" + user_profile.user_id + "\", \"" + user_profile.user_name + "\", " + user_profile.banana + ", " + user_profile.banana_free + ", " + user_profile.friend_coin + ", " + user_profile.tutorial_progress + ")";
      		SqliteDatabase sqlDB = new SqliteDatabase(GameUtil.Const.SQLITE_FILE_NAME);
      		sqlDB.ExecuteNonQuery(query);
      	}
      
      	public static UserProfileModel Get()
          {
      		string query = "select * from user_profile";
      		SqliteDatabase sqlDB = new SqliteDatabase(GameUtil.Const.SQLITE_FILE_NAME);
      		DataTable dataTable = sqlDB.ExecuteQuery(query);
      		UserProfileModel userProfileModel = new UserProfileModel();
      		foreach (DataRow dr in dataTable.Rows)
              {
      			userProfileModel.user_id = dr["user_id"].ToString();
      			userProfileModel.user_name = dr["user_name"].ToString();
      			userProfileModel.banana = int.Parse(dr["banana"].ToString());
      			userProfileModel.banana_free = int.Parse(dr["banana_free"].ToString());
      			userProfileModel.friend_coin = int.Parse(dr["friend_coin"].ToString());
      			userProfileModel.tutorial_progress = int.Parse(dr["tutorial_progress"].ToString());
      		}
      		return userProfileModel;
          }
      }
      
      マイケル
      マイケル
      それぞれ、

      CreateTable()メソッド → テーブル作成処理
      Set()メソッド → レコード登録処理
      Get()メソッド → レコード取得処理

      の処理になっています。
      エレキベア
      エレキベア
      他のDBと同じようにSQLを発行するクマね
      マイケル
      マイケル
      その通り!
      ちなみにアクセスするファイル名などは、
      下記のように定数クラスを作成して設定しています。
      using System;
      
      namespace GameUtil
      {
          public static class Const
          {
              /** DB情報 */
              public const string SERVER_URL = 【サーバURL】;
              public const string SQLITE_FILE_NAME = 【SQLiteファイル名】;
      
              /** エラーID */
              public const string ERROR_DB_UPDATE = "1";
          }
      }
      
      ↑定数クラス
      マイケル
      マイケル
      あとは今作成したテーブルクラスを呼び出す処理を記述します!
      SceneのManagerクラスとして下記を作成しましょう!
      using System;
      using System.IO;
      using UnityEngine;
      using UnityEngine.UI;
      
      public class TitleManager : MonoBehaviour
      {
          /** UI部品 */
          private const string REGIST_USER_NAME_TEXT = "RegistUserNameText";
          private const string START_USER_NAME_TEXT = "StartUserNameText";
          private const string REGIST_MSG_TEXT = "RegistMsg";
          private const string START_CANVAS = "StartCanvas";
          private const string REGIST_CANVAS = "RegistCanvas";
          private InputField registUserNameText;
          private Text startUserNameText;
          private Text registMsgText;
          private GameObject StartCanvas;
          private GameObject RegistCanvas;
      
          /** DBモデル */
          private UserProfileModel userProfileModel;
      
          private void Awake()
          {
              // SQLiteのDBファイル作成
              string DBPath = Application.persistentDataPath + "/" + GameUtil.Const.SQLITE_FILE_NAME;
              if (!File.Exists(DBPath)) {
                  File.Create(DBPath);
              }
              // テーブル作成処理
              UserProfile.CreateTable();
          }
      
          void Start()
          {
              StartCanvas = GameObject.Find(START_CANVAS);
              RegistCanvas = GameObject.Find(REGIST_CANVAS);
              registUserNameText = GameObject.Find(REGIST_USER_NAME_TEXT).GetComponent<InputField>();
              startUserNameText = GameObject.Find(START_USER_NAME_TEXT).GetComponent<Text>();
              registMsgText = GameObject.Find(REGIST_MSG_TEXT).GetComponent<Text>();
      
              // UserProfileの取得
              userProfileModel = UserProfile.Get();
              if (!string.IsNullOrEmpty(userProfileModel.user_id))
              {
                  // ユーザ登録済:StartCanvas表示
                  StartCanvas.SetActive(true);
                  RegistCanvas.SetActive(false);
                  startUserNameText.text = "User:" + userProfileModel.user_name;
              }
              else
              {
                  // ユーザ未登録:RegistCanvas表示
                  StartCanvas.SetActive(false);
                  RegistCanvas.SetActive(true);
              }
          }
      
          // --------------- ボタン押下時処理 ---------------
          // 登録ボタン押下
          public void PushRegistButton()
          {
              if (string.IsNullOrEmpty(registUserNameText.text))
              {
                  // ユーザ名未入力の場合
                  registMsgText.text = "ちゃんと入力するクマ";
                  
              }
              else if (registUserNameText.text.Length > 5)
              {
                  // ユーザ名が5文字以上の場合
                  registMsgText.text = "5文字以内で入力するクマ";
              }
              else
              {
                  // ユーザ登録処理
                  Action action = () => {
                      StartCanvas.SetActive(true);
                      RegistCanvas.SetActive(false);
                      startUserNameText.text = "User:" + registUserNameText.text;
                  };
                  StartCoroutine(CommunicationManager.ConnectServer("registration", "?user_name=" + registUserNameText.text, action));
              }
          }
      
          // スタートボタン押下
          public void PushStartButton()
          {
              // スタートボタン押下時の処理を書く!!
          }
      }
      
      マイケル
      マイケル
      DB処理に関連するのは下記部分です。
      Awake()の中で
      SQLiteのDBファイル作成とUserProfileテーブルの作成処理
      を記述することで、ゲーム起動時に必ず作成するようにしています。
      マイケル
      マイケル
      また、Start()の中では
      UserProfileテーブルのレコードが登録されているかどうか
      をチェックして処理を分岐させています。
      
      public class TitleManager : MonoBehaviour
      {
      
      ・・・略・・・
      
          /** DBモデル */
          private UserProfileModel userProfileModel;
      
          private void Awake()
          {
              // SQLiteのDBファイル作成
              string DBPath = Application.persistentDataPath + "/" + GameUtil.Const.SQLITE_FILE_NAME;
              if (!File.Exists(DBPath)) {
                  File.Create(DBPath);
              }
              // テーブル作成処理
              UserProfile.CreateTable();
          }
      
          void Start()
          {
      
      ・・・略・・・
      
              // UserProfileの取得
              userProfileModel = UserProfile.Get();
              if (!string.IsNullOrEmpty(userProfileModel.user_id))
              {
                  // ユーザ登録済:StartCanvas表示
                  StartCanvas.SetActive(true);
                  RegistCanvas.SetActive(false);
                  startUserNameText.text = "User:" + userProfileModel.user_name;
              }
              else
              {
                  // ユーザ未登録:RegistCanvas表示
                  StartCanvas.SetActive(false);
                  RegistCanvas.SetActive(true);
              }
          }
      
      ・・・略・・・
      
      }
      
      ↑テーブル作成と取得処理
      エレキベア
      エレキベア
      タイトル画面起動時にテーブルを作成すれば
      漏れが無くなるわけクマね
      マイケル
      マイケル
      その通りだ!
      そしてユーザ名を入力して決定ボタンを押下した時の処理は以下の部分!
      入力チェックをした後、これから作成する「ConnectServer()」メソッドを呼び出しているよ!
      
      
      ・・・略・・・
      
          // --------------- ボタン押下時処理 ---------------
          // 登録ボタン押下
          public void PushRegistButton()
          {
              if (string.IsNullOrEmpty(registUserNameText.text))
              {
                  // ユーザ名未入力の場合
                  registMsgText.text = "ちゃんと入力するクマ";
                  
              }
              else if (registUserNameText.text.Length > 5)
              {
                  // ユーザ名が5文字以上の場合
                  registMsgText.text = "5文字以内で入力するクマ";
              }
              else
              {
                  // ユーザ登録処理
                  Action action = () => {
                      StartCanvas.SetActive(true);
                      RegistCanvas.SetActive(false);
                      startUserNameText.text = "User:" + registUserNameText.text;
                  };
                  StartCoroutine(CommunicationManager.ConnectServer("registration", "?user_name=" + registUserNameText.text, action));
              }
          }
      
      ・・・略・・・
      
      }
      
      エレキベア
      エレキベア
      ここからサーバにリクエストに送るクマね
      マイケル
      マイケル
      そういうことさ!
      それじゃサーバとの連携クラスを作成していこう!

      サーバとの連携処理

      マイケル
      マイケル
      サーバとの連携クラスは下記になります!
      using System.Collections;
      using UnityEngine;
      using UnityEngine.Networking;
      using System;
      using System.Linq;
      
      [Serializable]
      public class ResponseObjects
      {
      	public UserProfileModel user_profile;
      }
      
      public class CommunicationManager : MonoBehaviour
      {
      
      	public static IEnumerator ConnectServer(string endpoint, string paramater, Action action = null)
          {
      		// *** リクエストの送付 ***
      		UnityWebRequest unityWebRequest = UnityWebRequest.Get(GameUtil.Const.SERVER_URL + endpoint + paramater);
      		yield return unityWebRequest.SendWebRequest();
      		// エラーの場合
      		if (!string.IsNullOrEmpty(unityWebRequest.error))
      		{
      			Debug.LogError(unityWebRequest.error);
      			yield break;
      		}
      
      		// *** レスポンスの取得 ***
      		string text = unityWebRequest.downloadHandler.text;
      		Debug.Log("レスポンス : " + text);
      		// エラーの場合
      		if (text.All(char.IsNumber))
      		{
      			switch (text)
      			{
      				case GameUtil.Const.ERROR_DB_UPDATE:
      					Debug.LogError("サーバーでエラーが発生しました。[データベース更新エラー]");
      					break;
      				default:
      					Debug.LogError("サーバーでエラーが発生しました。[システムエラー]");
      					break;
      			}
      			yield break;
      		}
      
      		// *** SQLiteへの保存処理 ***
      		ResponseObjects responseObjects = JsonUtility.FromJson<ResponseObjects>(text);
      		if (!string.IsNullOrEmpty(responseObjects.user_profile.user_id))
      			UserProfile.Set(responseObjects.user_profile);
      		// 正常終了アクション実行
      		if (action != null)
      		{
      			action();
      			action = null;
      		}
      	}
      }
      
      マイケル
      マイケル
      「UnityWebRequest」クラスを上記のように使用することで、
      リクエストを送ったりレスポンスを受け取ったりできるようになります!
      マイケル
      マイケル
      サーバ側でJSON形式で返すよう実装するので、
      受け取り後、SQLiteへ保存しましょう!
      エレキベア
      エレキベア
      こうしてみると内容が分かってきたクマ〜〜〜
      マイケル
      マイケル
      以上でクライアント側の実装は完了です!

      サーバ側の実装

      マイケル
      マイケル
      次はサーバ側の実装です!
      下記の部分になります!
      エレキベア
      エレキベア
      クライアントから受け取ったリクエストからDB登録して
      レスポンスを返す
      流れクマね
      マイケル
      マイケル
      サーバ側の処理は非常にシンプルです!
      下記Controllerクラスを作成しましょう!
      <?php
      
      namespace App\Http\Controllers;
      
      use Illuminate\Http\Request;
      use App\UserProfile;
      
      class RegistrationController extends Controller
      {
          public function Registration(Request $request)
      	{
      		//ユーザーIDの決定
      		$user_id = uniqid(); //例:4b3403665fea6
      
      		//初期データの設定
      		$user_profile = new UserProfile;
      		$user_profile->user_id = $user_id;
      		$user_profile->user_name = $request->user_name;
      		$user_profile->banana = config('constants.BANANA_DEFAULT');
      		$user_profile->banana_free = config('constants.BANANA_FREE_DEFAULT');
      		$user_profile->friend_coin = config('constants.FRIEND_COIN_DEFAULT');
      		$user_profile->tutorial_progress = config('constants.TUTORIAL_START');
      
      		//データの書き込み
      		try {
      			$user_profile->save();
      		} catch (\PDOException $e) {
      			logger($e->getMessage());
      			return config('error.ERROR_DB_UPDATE');
      		}
      
      		//クライアントへのレスポンス
      		$user_profile = UserProfile::where('user_id', $user_id)->first();
      
      		$response = array(
      			'user_profile' => $user_profile,
      		);
      
      		return json_encode($response);
      	}
      }
      
      マイケル
      マイケル
      リクエストを受け取ったら、
      ユーザのユニークIDを生成してUserProfileテーブルへの登録処理後、
      JSON形式で返却する
      処理にしています!
      マイケル
      マイケル
      ちなみに、こちらも config フォルダ内に下記のようなクラスを作成することで
      定数として扱うことができます!
      <?php
      
      return array(
          'ERROR_DB_UPDATE' => 1,
      );
      
      <?php
      
      return array(
          'BANANA_DEFAULT' => 0,
          'BANANA_FREE_DEFAULT' => 10,
          'FRIEND_COIN_DEFAULT' => 0,
          'TUTORIAL_START' => 0,
      );
      
      エレキベア
      エレキベア
      なるほどクマ〜〜〜〜〜
      マイケル
      マイケル
      あとは下記のようにルーティングを設定すれば完了です!!
      <?php
      
      Route::get('/', function () {
          return view('welcome');
      });
      Route::get('registration', 'RegistrationController@Registration');
      

      ↑完成図
      エレキベア
      エレキベア
      完成したクマ〜〜〜!!!

      おわりに

      マイケル
      マイケル
      というわけで今回はユーザ登録処理の実装でした!
      どうだったかな?
      エレキベア
      エレキベア
      サーバとの連携と聞くと難しそうクマが
      やってみると案外できてうれしかったクマ
      マイケル
      マイケル
      今回は簡単な処理だったけど、
      ここから少しずつレベルアップしていこうね!!
      マイケル
      マイケル
      それでは今日はこの辺で!
      アデュー!!!
      エレキベア
      エレキベア
      クマ〜〜〜〜〜〜〜

      【Unity】ソーシャルゲームを作る 〜ユーザ登録処理の実装〜【AWS × Laravel】 〜完〜


      UnityLaravelサーバサイド関連AWSソーシャルゲーム
      2020-11-23

      関連記事
      【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