OAuth 2でハイスコアを追加する:Laravel Passport + Unity。 パート2



特定のユーザーからのゲームからサイトへのレコードの追加に関する記事の続き。 最初の部分では、 Laravelに記録ページを作成し、それらを追加するためのAPI(匿名ユーザーと許可ユーザーの両方)を準備しました。 このパートでは、完成したUnity on the WallのUnityゲームを完成させ、アカウントにログインして、認証トークンを使用してLaravelのサイトにレコードを送信します。

準備する


例として、最も単純な機能を備えたネズミについてランナーを使用することを提案します。ネズミは壁に沿ってい、パンは上に落ちます。 Unity 2017.1のプロジェクトはgithubからダウンロードできます。 必要に応じて、他のプロジェクトを使用できます。ここでは、その実装の原則とオプションの1つのみが考慮されます。

このチュートリアルでは、 Laravelの最初の部分から完成したサイトも使用します。 こちらからダウンロードできます。 http://127.0.0.1:8000/でサイトを利用可能にするには、次のコマンドを使用する必要があります。

php artisan serve 

Unityでプロジェクトを開きます。 基本的なゲームプレイは次のとおりです。



Playをクリックすると、ラットを制御し、特定の境界で壁に沿って移動し、落下するパンをかわすことができます。 左上にはポイントカウンターがあり、下には残りのライフがあります。 Escを押すと、一時停止メニュー(認証フォームを追加する必要がある空のパネル)が表示されます。 ゲーム終了後、 Rボタンを再起動できます。

最初に行うことは、匿名レコードを追加することです。

匿名記録


Create -> C# Script Project ]パネルのCreate -> C# Scriptコマンドを使用して、 Scriptsフォルダーに新しいスクリプトをCreate -> C# Script 。 それをWWWScoreと呼び、 Unityに使用するエディター( Visual StudioMonoDevelop )で結果のWWWScore.csファイルをWWWScore.csます。

最初に、サーバーのアドレスを保存するフィールドを追加します。 Unityの [ Inspector ]パネルからこのプライベート変数を変更できるように、 [SerializeField]指定します。

 [SerializeField] private string serverURL = "http://127.0.0.1:8000/"; 

デフォルトでは、アドレスをLaravelのサイトと同じに設定します。 必要に応じて、変更できます。

次に、匿名ユーザーからレコードを追加する機能に移りましょう。 この関数は、POST要求をサーバーに送信し、応答を待ちます。 そのようなリクエストを処理するオプションとして、 コルーチンを使用して関数を並行して実行します。 コルーチンで使用する関数は次のようになります。

 public IEnumerator AddRecord(int score) { WWWForm form = new WWWForm(); form.AddField("score", score); WWW w = new WWW(serverURL + "api/anonymrecord", form); yield return w; if(!string.IsNullOrEmpty(w.error)) { Debug.Log(w.error); } else { Debug.Log(" !"); } } 

POSTリクエスト( GameControllerクラスから呼び出されたときに渡すscore変数の値)のデータを追加し、 GameControllerリクエストを作成し、結果を待ちます 。 サーバーからの応答が到着すると(または要求のタイムアウトが切れると)、コンソールに「 レコードが追加されました!」というメッセージが表示されます。 、または失敗した場合のエラー情報。

[ Inspector ]パネルの[ コンポーネント追加 ]ボタンを使用するか、オブジェクト上でマウスを使ってスクリプトをドラッグするだけで、 WWWScore.csスクリプトをGame Controllerオブジェクトに追加します。



ここで、スクリプトGameController.cs編集し、そこにコルーチンコールを追加します。

 void Update () { if (gameover){ // ,          if (!gameoverStarted) { gameoverStarted = true; //   restartText.SetActive(true); //   //   StartCoroutine(GetComponent<WWWScore>().AddRecord(score)); } // ... } else { // ... } // ... } 

コルーチンは、ゲームが終了した瞬間に一度-ゲームの再起動インターフェイスをオンにした直後に呼び出されます。 Rを押すと、シーンが再開され、再びゲームの最後に到達して、レコードが追加されます。

スクリプトを保存し、ゲームを確認します。 ゲームの終了後しばらくすると、「 レコードが追加されました!



サイトでハイスコアを開き、リクエストが実際に送信されたことを確認できます。



匿名の追加レコードが機能します。 承認に移りましょう。

認証コード


Login(string email, string password)認証関数Login(string email, string password)WWWScore.cs 、それをコルーチンに渡します。 レコードを追加する機能と同様に、 LaravelのサイトにPOSTリクエストを生成し、その中のデータセットをアドレスhttp://127.0.0.1:8000/oauth/tokenに送信します 。 記事の最初の部分で、承認に必要なデータセットを検討しました。

 WWWForm form = new WWWForm(); form.AddField("grant_type", "password"); form.AddField("client_id", "<Client ID>"); form.AddField("client_secret", "<Client Secret>"); form.AddField("username", email); //   form.AddField("password", password); //   form.AddField("scope", "*"); 

クエリ結果を受け取ったら、 jsonからデータを変換する必要があります。 これは、 jsonをオブジェクトに変換することにより、 JsonUtilityを使用して実行できます。 WWWScoreクラスのWWWScore前に、同じWWWScore.csファイルでオブジェクトのクラスをWWWScoreます。

 [Serializable] public class TokenResponse { public string access_token; } 

覚えているように、受け取ったjsonオブジェクトには4つのフィールドがありますが、クラスで記述するaccess_tokenフィールドのみが必要です。 これで、json変換をオブジェクト自体に追加できます。

 TokenResponse tokenResponse = JsonUtility.FromJson<TokenResponse>(w.text); 

認証トークンを受け取ったら、保存する必要があります。 簡単にするために、ユーザー設定を保存するためだけに設計されたPlayerPrefsクラスを使用します。

 PlayerPrefs.SetString("Token", tokenResponse.access_token); 

トークンを保存した後、それを使用してこのユーザーからレコードを追加できます。 ただし、その前に、現在のユーザーに関する情報を要求して、ログインしているユーザーをゲームに表示することもできます。 これを行うには、対応していない関数でコルーチンを呼び出しますが、この関数はまだ利用できません。

 StartCoroutine(GetUserInfo()); 

この関数も作成します。

完全なログイン機能コード
 [Serializable] public class TokenResponse { public string access_token; } public class WWWScore : MonoBehaviour { // ... public IEnumerator Login(string email, string password) { WWWForm form = new WWWForm(); form.AddField("grant_type", "password"); form.AddField("client_id", "3"); form.AddField("client_secret", "W82LfjDg4DpN2gWlg8Y7eNIUrxkOcyPpA3BM0g3s"); form.AddField("username", email); form.AddField("password", password); form.AddField("scope", "*"); WWW w = new WWW(serverURL + "oauth/token", form); yield return w; if (!string.IsNullOrEmpty(w.error)) { Debug.Log(w.error); } else { TokenResponse tokenResponse = JsonUtility.FromJson<TokenResponse>(w.text); if (tokenResponse == null) { Debug.Log("  !"); } else { //     PlayerPrefs.SetString("Token", tokenResponse.access_token); Debug.Log(" !"); //    StartCoroutine(GetUserInfo()); } } } } 


ユーザー情報の取得


http://127.0.0.1:8000/api/userでGETリクエストを実行し、リクエストのヘッダーに認証データを登録し、リクエスト内の他のデータを送信しないようにする必要がありnullnull )。

 Dictionary<string, string> headers = new Dictionary<string, string>(); headers.Add("Authorization", "Bearer " + PlayerPrefs.GetString("Token")); WWW w = new WWW(serverURL + "api/user", null, headers); 

前の関数と同様に、 json構造の全体から必要な唯一のフィールド(名前)を持つ別のクラスを作成するために必要な解析のために、 jsonを答えとして取得します。

 [Serializable] public class UserInfo { public string name; } 

jsonをこのクラスのオブジェクトに変換します。

 UserInfo userInfo = JsonUtility.FromJson<UserInfo>(w.text); 

設定にユーザー名を保存します。

 PlayerPrefs.SetString("UserName", userInfo.name); 

完全なGetUserInfo関数コード
 //  TokenResponse // ... [Serializable] public class UserInfo { public string name; } public class WWWScore : MonoBehaviour { // ... //  Login // ... public IEnumerator GetUserInfo() { Dictionary<string, string> headers = new Dictionary<string, string>(); headers.Add("Authorization", "Bearer " + PlayerPrefs.GetString("Token")); WWW w = new WWW(serverURL + "api/user", null, headers); yield return w; if (!string.IsNullOrEmpty(w.error)) { Debug.Log(w.error); } else { UserInfo userInfo = JsonUtility.FromJson<UserInfo>(w.text); if (userInfo == null) { Debug.Log("  !"); } else { //     PlayerPrefs.SetString("UserName", userInfo.name); Debug.Log("  !"); } } } } 


レコードを追加するためのコードの変更


承認されたユーザーからレコードを追加AddRecord(int score)は、 AddRecord(int score)関数のコードをわずかにAddRecord(int score)ます。 設定に認証トークンが入力されているかどうかを確認するチェックを追加します。入力されている場合は、ユーザーに関する情報を受信したときと同じ方法でヘッダーに追加します。唯一の違いは、POSTリクエストのデータでレコードを送信することだけです。

 WWW w; if (PlayerPrefs.HasKey("Token")) { Dictionary<string, string> headers = new Dictionary<string, string>(); byte[] rawData = form.data; headers.Add("Authorization", "Bearer " + PlayerPrefs.GetString("Token")); w = new WWW(serverURL + "api/record", rawData, headers); } else { w = new WWW(serverURL + "api/anonymrecord", form); } 

変更されたAddRecord関数の完全なコード
 public IEnumerator AddRecord(int score) { WWWForm form = new WWWForm(); form.AddField("score", score); WWW w; if (PlayerPrefs.HasKey("Token")) { Dictionary<string, string> headers = new Dictionary<string, string>(); byte[] rawData = form.data; headers.Add("Authorization", "Bearer " + PlayerPrefs.GetString("Token")); w = new WWW(serverURL + "api/record", rawData, headers); } else { w = new WWW(serverURL + "api/anonymrecord", form); } yield return w; if(!string.IsNullOrEmpty(w.error)) { Debug.Log(w.error); } else { Debug.Log(" !"); } } 


終了コード


ゲームからユーザーを結婚するには、設定でユーザーに関するすべてのデータを削除する必要があります。 この場合、他の設定はないため、すべての設定をクリアします。 プロジェクトではこれに注意してください。

 public void Logout() { PlayerPrefs.DeleteAll(); } 


メインコントローラー


次に、ユーザー認証を操作するためのメインゲームコントローラー( GameController.cs )を準備します。 これらをloginObjは、 loginObj許可loginObjlogoutObj出口パネルを持つオブジェクトが必要です。 認証パネルには、電子メールアドレス( inputFieldEmail )およびパスワード( inputFieldPassword )の入力フィールドが含まれます。 また、アカウントにログインしたユーザーの名前を表示するには、碑文userNameTextが必要です。

 //   public GameObject loginObj; //   public GameObject logoutObj; //  E-mail public GameObject inputFieldEmail; //   public GameObject inputFieldPassword; //     public GameObject userNameText; 

承認のために、 Login()関数を作成します。これは、 Loginボタンをクリックして呼び出され、パスワードでメールアドレスを読み取り、 WWWScore.csから同じ名前の関数でコルーチンを呼び出します。

 public void Login() { var email = inputFieldEmail.GetComponent<InputField>().text; var password = inputFieldPassword.GetComponent<InputField>().text; StartCoroutine(GetComponent<WWWScore>().Login(email, password)); } 

終了関数は非常に単純です- 終了ボタンをクリックして呼び出され、 WWWScore.csからWWWScore.csなしで同じ名前の関数を呼び出します。

 public void Logout() { GetComponent<WWWScore>().Logout(); } 

承認パネルと終了パネルの表示を切り替えるには、対応する設定がPlayerPrefsに保存されているかどうかを確認し、それに応じて目的のパネルを表示します。

 public void SetLoginVisible() { if (PlayerPrefs.HasKey("Token")) { loginObj.SetActive(false); logoutObj.SetActive(true); } else { loginObj.SetActive(true); logoutObj.SetActive(false); } } 

同様に、ユーザー名を表示するには、名前の設定を確認し、存在しない場合はAnonymousと記述します

 public void SetUserName() { if (PlayerPrefs.HasKey("UserName")) { userNameText.GetComponent<Text>().text = PlayerPrefs.GetString("UserName"); } else { userNameText.GetComponent<Text>().text = ""; } } 

最後の2つの関数は、対応する設定を変更するとき(および初期化プロセス中)にのみ呼び出す必要がありますが、このチュートリアルのフレームワーク内では、 Update()関数でこれを実行できます。

 void Update () { // ... //   // ... SetUserName(); SetLoginVisible(); } 

次に、視覚コンポーネントに目を向けます。

認証インターフェース


認証インターフェースを追加します。 CanvasオブジェクトにネストされたPauseパネルの[ 有効化]チェックボックスをオンにします。 新しい空のオブジェクトを作成しCreate Empty )、それをLoginと呼び、 TitlePause text)と同じレベルで、 Pauseパネル内に配置します。 Graphic Raycasterコンポーネントを追加しGraphic Raycaster (ネストされた要素を正しく機能させるため)。



このLoginオブジェクトにInputFieldEmailInputFieldPassword 2つの入力フィールドを追加し( InputFieldPassword > Input Field )、わかりやすくするためにプレースホルダーテキストを変更します。 InputFieldEmailオブジェクトの入力フィールドコンポーネントで、[ コンテンツタイプ]フィールドのデータタイプを[ 電子メールアドレス]に変更し、 InputFieldPasswordオブジェクトを[ パスワード]に変更します。 ButtonLoginボタン( ButtonLogin > Button )を同じLoginオブジェクトに追加します。 インターフェイスは次のようになります(フォントとコンポーネントのサイズを使用する場合)。



以前に作成した関数を、 ButtonLoginボタンをクリックするイベントにバインドします。 InspectorパネルのButtonコンポーネントについては、 On Click()イベントのプラス記号をクリックし、リストからEditorとRuntimeを選択し(デバッグ中の正しい作業のため)、そこにGame Controllerオブジェクトをドラッグします(マウスまたはオブジェクトフィールドの隣の選択円をクリックして選択します) ) この後に表示されるポップアップメニューで、 GameControllerコンポーネントとその中のLogin()関数を選択します。



LoginオブジェクトのEnableチェックボックスをオフにします-その表示はGameController.csで規制されていGameController.cs

終了インターフェース


PauseネストされたLoginオブジェクト( Graphic Raycasterコンポーネントを忘れないでください)と同様に、新しいLogoutオブジェクトを作成しましょう。 ButtonLogoutボタンのみをLogoutオブジェクトに追加します。 前のボタンと同様に、同じ名前のオブジェクトのGameControllerコンポーネントのLogout()関数をclickイベントに添付します。



LogoutオブジェクトとPauseパネル自体の「有効化」チェックボックスをオフにします。

ユーザー名の表示


Userテキスト要素( UI-> Text )をPause要素の前のメインCanvasに追加し、その中にAnonymousを書き込み(または、碑文がGameController.csで割り当てられるため、空白のままにしGameController.cs )、右上隅に配置します。 許可されたユーザーの名前がここに表示されます。



コントローラーへのオブジェクトの割り当て


GameControllerオブジェクトを選択します。 インスペクターパネルでは、 Game Controllerコンポーネントには、前のコードで追加した空のフィールドがいくつかあります。 階層パネルからマウスをドラッグするか、フィールドの近くにある選択円をクリックしてリストから選択することにより、適切なオブジェクトを割り当てます。



テスト中


最後の部分、つまりすべてが正常に機能することを確認します。 ゲームを起動してEscを押します 。 私たちの前に承認パネルが開きます。 サイトに登録されているユーザーのデータを収集します(前回の記事でhabr@habrahabr.ru / habrahabrを使用しました )。



[ ログイン ]ボタンをクリックします。 成功した場合、しばらくするとユーザー認証パネルが終了パネルに置き換えられ、対応するボタンのみが残されます。 匿名ではなく、 Habrが右上に書き込まれます-サイトのユーザー名。



ここで、もう一度Escを押してレコードを設定すると、匿名ユーザーからではなく、許可されたユーザーから送信されます。



これは、サイトのレコードページに移動して確認できます。



これで私の最初のチュートリアルは終わりです。 私はそれについての質問に答えてうれしいです!

完全なWWWScore.csコード
 using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [Serializable] public class TokenResponse { public string access_token; } [Serializable] public class UserInfo { public string name; } public class WWWScore : MonoBehaviour { [SerializeField] private string serverURL = "http://127.0.0.1:8000/"; public IEnumerator AddRecord(int score) { WWWForm form = new WWWForm(); form.AddField("score", score); WWW w; if (PlayerPrefs.HasKey("Token")) { Dictionary<string, string> headers = new Dictionary<string, string>(); byte[] rawData = form.data; headers.Add("Authorization", "Bearer " + PlayerPrefs.GetString("Token")); w = new WWW(serverURL + "api/record", rawData, headers); } else { w = new WWW(serverURL + "api/anonymrecord", form); } yield return w; if(!string.IsNullOrEmpty(w.error)) { Debug.Log(w.error); } else { Debug.Log(" !"); } } public IEnumerator Login(string email, string password) { WWWForm form = new WWWForm(); form.AddField("grant_type", "password"); form.AddField("client_id", "3"); //   form.AddField("client_secret", "W82LfjDg4DpN2gWlg8Y7eNIUrxkOcyPpA3BM0g3s"); //   form.AddField("username", email); form.AddField("password", password); form.AddField("scope", "*"); WWW w = new WWW(serverURL + "oauth/token", form); yield return w; if (!string.IsNullOrEmpty(w.error)) { Debug.Log(w.error); } else { TokenResponse tokenResponse = JsonUtility.FromJson<TokenResponse>(w.text); if (tokenResponse == null) { Debug.Log("  !"); } else { //     PlayerPrefs.SetString("Token", tokenResponse.access_token); Debug.Log(" !"); //    StartCoroutine(GetUserInfo()); } } } public IEnumerator GetUserInfo() { Dictionary<string, string> headers = new Dictionary<string, string>(); headers.Add("Authorization", "Bearer " + PlayerPrefs.GetString("Token")); WWW w = new WWW(serverURL + "api/user", null, headers); yield return w; if (!string.IsNullOrEmpty(w.error)) { Debug.Log(w.error); } else { UserInfo userInfo = JsonUtility.FromJson<UserInfo>(w.text); if (userInfo == null) { Debug.Log("  !"); } else { //     PlayerPrefs.SetString("UserName", userInfo.name); Debug.Log("  !"); } } } public void Logout() { PlayerPrefs.DeleteAll(); } } 

完全なGameController.csコード
 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; //   [System.Serializable] public class PanClass { //   public GameObject panObj; //      public float start; //    public float pause; } public class GameController : MonoBehaviour { //   public PanClass pan; //   public Vector2 spawnValues; //     public GameObject scoreText; //      public GameObject restartText; //      public GameObject pausePanel; //     public float scoreRate = 1.0F; // ,     public int scoreAdd = 10; //  public static int score; //    public static bool gameover; //     private float nextScore = 0.0F; //  ,         private bool gameoverStarted; //   public GameObject loginObj; //   public GameObject logoutObj; //  E-mail public GameObject inputFieldEmail; //   public GameObject inputFieldPassword; //     public GameObject userNameText; void Start () { //   ( ) gameover = false; score = 0; gameoverStarted = false; //    StartCoroutine(PanSpawn()); } void FixedUpdate() { if (!gameover) { //   scoreText.GetComponent<Text>().text = score.ToString(); } } void Update () { if (gameover){ // ,          if (!gameoverStarted) { gameoverStarted = true; //    restartText.SetActive(true); //   StartCoroutine(GetComponent<WWWScore>().AddRecord(score)); } //   R if (Input.GetKey(KeyCode.R)) { //   SceneManager.LoadScene(0); } } else { if (Input.GetKeyDown(KeyCode.Escape)) { if (Time.timeScale != 0) { //    Time.timeScale = 0; pausePanel.SetActive(true); } else { //    Time.timeScale = 1; pausePanel.SetActive(false); } } } //   if (!gameover && (Time.time > nextScore)) { nextScore = Time.time + scoreRate; score = score + scoreAdd; } SetUserName(); SetLoginVisible(); } //   IEnumerator PanSpawn() { //      yield return new WaitForSeconds(pan.start); //  ,    while (!gameover) { //          Vector2 spawnPosition = new Vector2(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y); Quaternion spawnRotation = Quaternion.identity; Instantiate(pan.panObj, spawnPosition, spawnRotation); yield return new WaitForSeconds(pan.pause); } } //  public void Login() { var email = inputFieldEmail.GetComponent<InputField>().text; var password = inputFieldPassword.GetComponent<InputField>().text; StartCoroutine(GetComponent<WWWScore>().Login(email, password)); } //  public void Logout() { GetComponent<WWWScore>().Logout(); } //     public void SetLoginVisible() { if (PlayerPrefs.HasKey("Token")) { loginObj.SetActive(false); logoutObj.SetActive(true); } else { loginObj.SetActive(true); logoutObj.SetActive(false); } } //      public void SetUserName() { if (PlayerPrefs.HasKey("UserName")) { userNameText.GetComponent<Text>().text = PlayerPrefs.GetString("UserName"); } else { userNameText.GetComponent<Text>().text = ""; } } } 

前編
Laravelの準備完了プロジェクト
Unity基本プロジェクトmasterブランチ)
Unityの準備完了プロジェクトfinalブランチ)

Source: https://habr.com/ru/post/J340362/


All Articles