ゲーム開発
Unity
UnrealEngine
C++
ゲーム数学
ゲームAI
サウンド
アニメーション
GBDK
制作日記
3DCG
Houdini
Blender
USD
グラフィックス
テクノロジ
ツール開発
フロントエンド関連
サーバサイド関連
ソフトウェア設計
ハードウェア関連
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
その他
都会のエレキベア
ラーメン日記
四コマ漫画
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • ゲーム数学
    • ゲームAI
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • 3DCG
    • Houdini
    • Blender
    • USD
    • グラフィックス
  • テクノロジ
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • ソフトウェア設計
    • ハードウェア関連
    • おすすめ技術書
  • 音楽
    • 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
記事をSNSで共有する
X
Facebook
LINE
はてなブックマーク
Pocket
LinkedIn
Reddit

著者の各種アカウント
フォローいただけると大変励みになります!

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