(Unity/ニフクラ)オンラインゲームにおけるデータの保存方法

ゲーム開発
広告だよ!!!たくさんクリックしてね

(2021/03/15)ユーザーデータのパーミッション設定について追記

はじめに

 最近UnityでPhoton等を使ったオンラインゲームを作る方法を書いてくださっている記事が増えてきて、以前と比べるとオンラインゲーム制作のハードルが下がってきていると思うのですが、会員登録システム(レベル・経験値・所持金などの保存)の作り方を解説している記事がまだまだ少ないと思ったので記事にします。

この記事でやること

・ニフクラ Mobile Backend(https://mbaas.nifcloud.com/)を使って会員登録システム、データ読込・保存を実装する

記事の通りに進めていけば、思っていたより簡単に実装できると思います。

初期準備

公式HPイントロダクションの「SDKをインストールする」まで進めておいてください。※無料会員はSNS連携でしかサービス登録できません。

データクラスを作る

データクラスって言うと良く分かりにくいですが、要するにエクセルの表を作るみたいなものです

↑要するにこういうのを作る

1.会員管理から「新しいフィールド」を選ぶ

2.「UserDataID」として作成する

3.データストア→作成の「新規作成」を選ぶ

4.「UserData」クラスを作る

5.「新しいフィールド」を選ぶ

6.記録したいデータのフィールド(要するに変数みたいな)を定義する。ここではNameとMoneyを定義します。

7.多分こんな感じになったと思います。これで下準備は完成です。

ログイン処理を実装する

あらかじめSDKにログイン画面の雛形が用意されているので、簡単にできます。

0.APIキーを控えておいてください。

1.Assets/Scenes/Member/LoginSigninシーンを開きます。

2.NCMBSettingsを選んで

3.右のInspector上のApplicationKey, ClientKeyにさっき調べたAPIキーを入力してください。

4.サインイン(会員登録)処理をいじる

これでログイン処理・サインイン処理自体は完成しました。最後に、サインイン時に初期データを記録する処理を実装します。

4_1.「LoginSignin」シーンと同じ場所にある「Loginsignin」スクリプトを開きます。

4_2.以下のスクリプトに置き換えます。

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using NCMB;

//private InputField UserName;
//private InputField PassWord;

public class Loginsignin : MonoBehaviour
{
	public InputField UserName;
	public InputField PassWord;
    public GameObject Debug_Text = null; // Textオブジェクト

	//会員登録後初回のサインインかどうかを判別する
	private bool firstsign = false;

    // Use this for initialization
    void Start ()
	{

	}
	
	// Update is called once per frame
	void Update ()
	{
	
	}

	public void Login ()
	{
		print (UserName.text);
		print (PassWord.text);

		//NCMBUserのインスタンス作成 
		NCMBUser user = new NCMBUser ();
        // オブジェクトからTextコンポーネントを取得
        Text debug_text = Debug_Text.GetComponent<Text>();
        // ユーザー名とパスワードでログイン
        NCMBUser.LogInAsync (UserName.text, PassWord.text, (NCMBException e) => {    
			if (e != null) {
				UnityEngine.Debug.Log ("ログインに失敗: " + e.ErrorMessage);
                debug_text.text = "ログインに失敗: " + e.ErrorMessage;
            }
            else {
				UnityEngine.Debug.Log ("ログインに成功!");
                // テキストの表示を入れ替える
                debug_text.text = "ログインに成功";

				//初回ログインなら、初期パラメーターを設定する関数へ飛ばす
				if(firstsign){
					setfirsrparam();
				}

                //LogOutの部分は移動d先のScene名
                Application.LoadLevel ("LogOut");
			}
		});

	}

	public void Signin ()
	{
		print (UserName.text);
		print (PassWord.text);

		//NCMBUserのインスタンス作成 
		NCMBUser user = new NCMBUser ();
		
		//ユーザ名とパスワードの設定
		user.UserName = UserName.text;
		user.Password = PassWord.text;
        // オブジェクトからTextコンポーネントを取得
        Text debug_text = Debug_Text.GetComponent<Text>();
        //会員登録を行う
        user.SignUpAsync ((NCMBException e) => { 
			if (e != null) {
				UnityEngine.Debug.Log ("新規登録に失敗: " + e.ErrorMessage);
                debug_text.text = "新規登録に失敗: " + e.ErrorMessage;
            }
            else {
				UnityEngine.Debug.Log ("新規登録に成功");
                // テキストの表示を入れ替える
                debug_text.text = "新規登録に成功";

				//会員登録に成功したのでそのままログインする。初回ログイン判定をTrueにする
				firstsign = true;
				Login();
            }
		});

	}

	void setfirsrparam(){
		//ログイン中の会員データを保管する変数
		NCMBUser currUser = NCMBUser.CurrentUser;
		
		//「UserData」クラスを選択している変数(この時点ではデータの取得はしていない)
		NCMBObject obj = new NCMBObject("UserData");
		obj["Name"]  = UserName.text; //ここでは名前=ログイン時に使うユーザーネームということにします。
		obj["Money"] = 0;

		string selfID;
		//「UserData」クラスを保存する関数です
		obj.SaveAsync((NCMBException e) => {      
			if (e != null) {
				//エラー処理
				Debug.Log("UserDataの保存に失敗しました。");
			} else {
				//成功時の処理

				//UserDataクラスに登録した自分のセーブデータのオブジェクトID(管理番号)をselfIDに格納します。
				selfID = obj.ObjectId;
				//selfIDを会員データの「UserDataID」に格納します。
				currUser["UserDataID"] = selfID;

				//変更した会員データをサーバーに保存します。
				currUser.SaveAsync((NCMBException ee) => {      
					if (ee != null) {
						//エラー処理
						Debug.Log("ユーザーID設定失敗");

					} else {
						//成功時の処理
						Debug.Log("ユーザーID設定成功");
					}                   
				});
			}                   
		});			
	}
}


ソースコードのコメントを読めば大体どんな流れか察しがつくと思うので、重要な部分だけ解説します。

//ログイン中の会員データを保管する変数
		NCMBUser currUser = NCMBUser.CurrentUser;

(95行目)ログイン中のデータを「currUser」変数に代入しています。

//「UserData」クラスを選択している変数(この時点ではデータの取得はしていない)
NCMBObject obj = new NCMBObject("UserData");
obj["Name"]  = UserName.text; //ここでは名前=ログイン時に使うユーザーネームということにします。
obj["Money"] = 0;

(98行目)UserDataクラスをobjという名前で定義しています。

ここで注意してほしい事は、変数として定義しただけではサーバーからクラスのデータ情報(名前・所持金など)を取得していない、ということです。要するに、所持金を「0円」とか「100円」みたいに代入するのではなく、「+100円」みたいに既存データに上書きしたいときは別にデータの取得処理を行う必要があります(FetchAsyncという関数を使います。後述)。

obj.SaveAsync((NCMBException e) => {      
			if (e != null) {
				//エラー処理
				Debug.Log("UserDataの保存に失敗しました。");
			} else {
				//成功時の処理

				//UserDataクラスに登録した自分のセーブデータのオブジェクトID(管理番号)をselfIDに格納します。
				selfID = obj.ObjectId;
				//selfIDを会員データの「UserDataID」に格納します。
				currUser["UserDataID"] = selfID;

				//変更した会員データをサーバーに保存します。
				currUser.SaveAsync((NCMBException ee) => {      
					if (ee != null) {
						//エラー処理
						Debug.Log("ユーザーID設定失敗");

					} else {
						//成功時の処理
						Debug.Log("ユーザーID設定成功");
					}                   
				});
			}                   
		});	

(105行目)変更したデータをサーバーと同期(保存)する処理です。「UserData」クラスとして定義した「obj」変数にデータを代入するだけでは自動保存されません。データを書き換えたら「SaveAsync」を使って同期する必要があります。

「UserData」クラスの同期に成功したら、「UserData」クラスのオブジェクトIDを会員データに保存しています。これはどういうことなのか。少し複雑なので図で説明します。

名前、所持金、プレーヤーLVなどのデータは他のプレーヤーからでも取得できるようにしたい情報だと思います。そういったデータを会員データに入れてしまうと他のプレーヤーからは見れなくなってしまうので、基本的にセーブデータは誰でも見れる場所に保存するようにしています。セーブデータと会員データを紐づけているのが「オブジェクトID」です。会員データにオブジェクトIDを保存することで、セーブデータの場所を見失わないで済むわけです。

こうすれば、ほかのプレーヤーもオブジェクトIDさえ知っていればデータにアクセス可能になります。

※ただし、この方法はほかのプレーヤーもデータの書き換えが可能です。セキュリティーを高めたければ、パーミッションの設定が必要です。(後述)

4.ログイン処理完成!

完成です!お手軽!!!!

※パスワードが普通に表示されてるのってどうなの・・?って人は

Passwordを選択して

ContentTypeを

Passwordに変えるだけです。

セーブデータを取得したい

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

//宣言忘れずに!!!!
using NCMB;


public class PlayerInfo : MonoBehaviour
{

    // Start is called before the first frame update
    void Start()
    {
        NCMBObject obj2 = new NCMBObject ("UserData");
        //セーブデータの場所(オブジェクトID)を指定する
        obj2.ObjectId = NCMBUser.CurrentUser["UserDataID"].ToString();

        obj2.FetchAsync ((NCMBException e) => {        
            if (e != null) {
                //なんらかの原因でデータが取れなかったとき
                Debug.Log("Cannot get UserData.\n(" + e.ToString() + ")");

            } else {
                //成功時の処理
                Debug.Log("Name: " + obj2["Name"].ToString() + "\nMoney: " +  obj2["Money"].ToString());

            }               
        });

    }
}

最小限に書くとこんな感じです。UserDataクラスを定義して、取得したいセーブデータのオブジェクトIDを指定して、FetchAsyncで取得しています。取得したデータは(変数名)[“フィールド名”]で取得できます。

セーブデータの書き込み

一度上記のようにFetchAsyncでデータを取得した後、データを書き換えて、SaveAsync(上述)でサーバーに保存する流れがベターです。

ユーザーネーム(ID)を取得したい

NCMBUser.CurrentUser.UserNameに入っています。

UserDataのパーミッション設定

上述のアカウント管理システムの欠点として、パーミッション設定的に誰でも他の人のUserDataを改ざんできてしまうことが挙げられます。実際にはゲーム内でほかの人のデータをいじれてしまうような処理を書かなければ問題ないのですが、もしハッカーが無理やりデータをいじろうとしてしまえば出来てしまうパーミッション設定になっています。

これではセキュリティー的によろしくないので、気になる方はアカウント作成時にパーミッション設定を同時に行うよう処理を変更するといいです。

  1. LoginSignin.csの103行目に以下の処理を追加してください
//ACL = Access Control Listの略 アクセス権を制御するリスト
NCMBACL acl = new NCMBACL ();

//全員のアクセス権を一旦「読み込み可, 書き込み禁止」に変更する
acl.PublicReadAccess = true;
acl.PublicWriteAccess = false;
//例外として、自分だけは「読み込み可, 書き込み可」にする
acl.SetReadAccess (currUser.ObjectId, true);
acl.SetWriteAccess (currUser.ObjectId, true);

//オブジェクトにACLを設定
obj.ACL = acl;

2.ニフクラのコンソールからUserData→作成したアカウントデータのパーミッションを確認するとこうなっていると思います

注意してほしい事

初歩中の初歩なので忘れる方は少ないかも知れませんが、処理を行うスクリプトには必ず「using NCMB;」を入れてください。

NCMBUser.CurrentUserからデータをしつこく色んなシーンで取得しようとすると良く分からないエラーが出てくることがあるので、上で説明しているUserData用のObjectIDは最初にどこかの変数に取得しておいて格納するのが良いと思います エラーが出るのは自分が変な処理してただけでした。でもUserDataのObjectIDはstatic変数とかで格納した方が便利です

セーブデータ処理で参考になりそうなページ

https://github.com/NIFCLOUD-mbaas/UnityScriptApp

公式が出しているガチャの作り方講座です。所持金要素を実装しているので参考になると思います。自分も参考にしました。

おわりに

 以上のことが出来れば簡単なセーブデータの処理は出来るようになると思います。PhotonやStrixのようなオンラインゲームエンジンとニフクラを組み合わせればMMO等も比較的簡単に作れるのかもしれません。参考になれば!!

タイトルとURLをコピーしました