【Unity】第四回 ソーシャルゲームを作る 〜クエスト機能の実装〜 【AWS x Laravel】

Laravel
マイケル
マイケル
みなさんこんにちは!
マイケルです!!
エレキベア
エレキベア
クマ〜〜〜〜〜!!
マイケル
マイケル
今日は前回に引き続いて、ソシャゲ作成を進めていくぞ!
エレキベア
エレキベア
前回はマスタ登録まで実装したクマね
マイケル
マイケル
うむ!
今回は登録したマスタデータを使って、
簡単なクエスト機能を実装していくぞ!
エレキベア
エレキベア
ついに中身の実装クマ〜〜〜〜〜〜
スポンサーリンク

参考書籍

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

エレキベア
エレキベア
いつもお世話になってるクマ〜〜〜〜

クエスト機能の概要

マイケル
マイケル
実装する機能の概要は下記になります!
Screenshot 2020 12 13 0 39 13
マイケル
マイケル
メニューシーンに遷移すると、

① user_profileテーブルから所持アイテム数、
② master_questテーブルからクエスト一覧、
③ user_questテーブルからクエスト状態
を表示します!
Screenshot 2020 12 13 0 27 03
↑各テーブルの構造
マイケル
マイケル
クエストをクリアしたらアイテムを入手して、
クエスト状態と所持アイテム数を更新する

というシンプルな内容です!
01 QUEST
↑クエスト一覧の表示とクエスト画面への遷移
02 QUEST WIN
↑クエスト初クリア時にアイテム入手
マイケル
マイケル
なお、今回は機能の実装方法をメインとするため、
クエスト内容はゴロヤンとのジャンケン対決のみとしています。
エレキベア
エレキベア
(クソゲークマ・・・。)

サーバ側の実装

マイケル
マイケル
まずはサーバ側の実装を追加していきます!

テーブルの作成

マイケル
マイケル
下記テーブルを追加しましょう!
user_profileテーブルは第二回、
master_questテーブルは第三回で追加しているので、
新たに追加するのは user_questテーブルのみになります!
Screenshot 2020 12 13 0 27 03
マイケル
マイケル
これまでと同様、下記マイグレーションファイルからテーブルを作成し、
Modelクラスを作成しましょう!
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUserQuest extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('user_quest', function (Blueprint $table) {
            $table->string('user_id', 37)->charset('utf8');
			$table->unsignedInteger('quest_id')->default(0);
			$table->unsignedTinyInteger('status')->default(0);
			$table->unsignedInteger('score')->default(0);
			$table->unsignedInteger('clear_time')->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(array('user_id', 'quest_id'));
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('user_quest');
    }
}
↑マイグレーションファイルの作成
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class UserQuest extends Model
{
    protected $table = 'user_quest';
	public $incrementing = false;
	protected $primaryKey = 'user_id';
	public $timestamps = false;
}
↑Modelクラスの作成
エレキベア
エレキベア
テーブル作成も慣れてきたクマね

クエスト機能の実装

マイケル
マイケル
テーブルを作成したら、クエスト機能を実装していきます!

少し長いですが、Controllerクラスを作成後、

GetUserQuestList() → user_questリスト取得処理
Start() → クエスト開始処理 (user_questの新規作成)
End() → クエスト終了処理 (user_questの状態更新とアイテム取得)

の3つの処理を追加しましょう!

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Libs\MasterDataService;
use App\UserProfile;
use App\UserQuest;
use App\MasterQuest;

class QuestController extends Controller
{
    /**
     * user_questリスト取得処理
     * 
     */
    public function GetUserQuestList(Request $request)
    {
        // リクエストパラメータ取得
        $user_id = $request->user_id;

        // user_profileデータ取得
        $user_profile = UserProfile::where('user_id', $user_id)->first();
        if(!$user_profile) {
            return config('error.ERROR_INVALID_DATA');
        }
        // user_questデータリスト取得
        $user_quest_list = UserQuest::where('user_id', $user_id)->get();

        // レスポンスの返却
        $response = array(
            'user_quest' => $user_quest_list,
        );
        return json_encode($response);
    }

    /**
     * クエスト開始処理
     * 
     */
    public function Start(Request $request)
    {
        // リクエストパラメータ取得
        $client_master_version = $request->client_master_version;
        $user_id = $request->user_id;
        $quest_id = $request->quest_id;

        // マスタデータチェック
        if (!MasterDataService::CheckMasterDataVersion($client_master_version)) {
            return config('error.ERROR_MASTER_DATA_UPDATE');
        }

        // master_questデータ取得
        $master_quest = MasterQuest::GetMasterQuestByQuestId($quest_id);
        if (is_null($master_quest)) {
            return config('error.ERROR_INVALID_DATA');
        }
        if(time() < strtotime($master_quest->open_at) || strtotime($master_quest->close_at < time())) {
            return config('error.ERROR_INVALID_SCHEDULE');
        }

        // user_profileデータ取得
        $user_profile = UserProfile::where('user_id', $user_id)->first();
        if(!$user_profile) {
            return config('error.ERROR_INVALID_DATA');
        }

        // user_questデータ取得
        $user_quest = UserQuest::where('user_id', $user_id)->where('quest_id', $quest_id)->first();
        if (!$user_quest) {
            // 存在しない場合、新規作成
            $user_quest = new UserQuest;
            $user_quest->user_id = $user_id;
            $user_quest->quest_id = $quest_id;
            $user_quest->status = config('constants.QUEST_START');
        }

        // データの書き込み
        try {
            $user_quest->save();
        } catch (\PODException $e) {
            return config('error.ERROR_DB_UPDATE');
        }

        // レスポンス作成
        $user_quest_list = UserQuest::where('user_id', $user_id)->get();
        $response = array(
            'user_quest' => $user_quest_list,
        );
        return json_encode($response);
    }

    /**
     * クエスト終了処理
     * 
     */
    public function End(Request $request)
    {
        // リクエストパラメータ取得
        $user_id = $request->user_id;
        $quest_id = (int)$request->quest_id;
        $score = (int)$request->score;
        $clear_time = (int)$request->clear_time;

        // パラメータチェック処理
        if ($score == 0 || 1000000 < $score) {
            return config('error.ERROR_INVALID_DATA');
        }
        if ($clear_time <= 10 || 1000000 < $clear_time) {
            return config('error.ERROR_INVALID_DATA');
        }

        // master_questデータ取得
        $master_quest = MasterQuest::GetMasterQuestByQuestId($quest_id);
        if (is_null($master_quest)) {
            return config('error.ERROR_INVALID_DATA');
        }
        if(time() < strtotime($master_quest->open_at) || strtotime($master_quest->close_at < time())) {
            return config('error.ERROR_INVALID_SCHEDULE');
        }

        // user_questデータ取得
        $user_quest = UserQuest::where('user_id', $user_id)->where('quest_id', $quest_id)->first();
        if (!$user_quest) {
            return config('error.ERROR_INVALID_DATA');
        }

        // user_profileデータ取得
        $user_profile = UserProfile::where('user_id', $user_id)->first();
        if(!$user_profile) {
            return config('error.ERROR_INVALID_DATA');
        }

        // 初回クリア報酬
        if ($user_quest->status != config('constants.QUEST_CLEAR')) {
            switch ($master_quest->item_type) {
                case config('constants.ITEM_TYPE_BANANA'):
                    $user_profile->banana += $master_quest->item_count;
                    break;
                case config('constants.ITEM_TYPE_BANANA_FREE'):
                    $user_profile->banana_free += $master_quest->item_count;
                    break;
                case config('constants.ITEM_TYPE_FRIEND_COIN'):
                    $user_profile->friend_coin += $master_quest->item_count;
                    break;
                default:
                    break;
            }
        }

        // DB更新処理
        $user_quest->status = config('constants.QUEST_END');
        $user_quest->score = $score;
        $user_quest->clear_time = $clear_time;
        try {
            $user_profile->save();
            $user_quest->save();
        } catch (\PODException $e) {
            return config('error.ERROR_DB_UPDATE');
        }

        // レスポンス作成
        $user_quest_list = UserQuest::where('user_id', $user_id)->get();
        $response = array(
            'user_profile' => $user_profile,
            'user_quest' => $user_quest_list,
        );
        return json_encode($response);
    }
}
エレキベア
エレキベア
あとはそれぞれの処理をクライアント側から呼び出すクマね
マイケル
マイケル
その通り!
今回新たに定数を追加しているため、
下記値も追記しておきましょう!
<?php

return array(
    'ERROR_MASTER_DATA_UPDATE' => 1,
    'ERROR_DB_UPDATE' => 2,
    // ↓追加↓
    'ERROR_INVALID_DATA' => 3,
    'ERROR_INVALID_SCHEDULE' => 4,
    // ↑追加↑
);
↑エラー情報の定数
<?php

return array(
    'MASTER_DATA_VERSION' => 1,
    'BANANA_DEFAULT' => 0,
    'BANANA_FREE_DEFAULT' => 10,
    'FRIEND_COIN_DEFAULT' => 0,
    'TUTORIAL_START' => 0,
    // ↓追加↓
    'ITEM_TYPE_BANANA' => 1,
    'ITEM_TYPE_BANANA_FREE' => 2,
    'ITEM_TYPE_FRIEND_COIN' => 3,
    'QUEST_START' => 1,
    'QUEST_END' => 2,
    // ↑追加↑
);
↑ゲーム情報の定数
マイケル
マイケル
最後にRouteを登録すれば、
サーバ側の実装は完了です!
Route::get('user_quest', 'QuestController@GetUserQuestList');
Route::get('quest_start', 'QuestController@Start');
Route::get('quest_end', 'QuestController@End');
↑Routeの登録
エレキベア
エレキベア
Laravelは楽しいクマ〜〜〜〜

クライアント側の実装

Modelクラスの作成

マイケル
マイケル
次はクライアント側の実装です!
こちらもサーバ側と同様、
user_questテーブルのModelクラスを作成しておきましょう!
using System;

[Serializable]
public class UserQuestModel
{
    public int quest_id;
    public int status;
    public int score;
    public int clear_time;
}

public static class UserQuest
{
    public static void CreateTable()
    {
        string query = "create table if not exists user_quest (quest_id int, status int, score int, clear_time int, primary key(quest_id))";
        SqliteDatabase sqlDB = new SqliteDatabase(GameUtil.Const.SQLITE_FILE_NAME);
        sqlDB.ExecuteQuery(query);
    }

    public static void Set(UserQuestModel[] user_quest_model_list)
    {
        SqliteDatabase sqlDB = new SqliteDatabase(GameUtil.Const.SQLITE_FILE_NAME);
        foreach (UserQuestModel userQuestModel in user_quest_model_list)
        {
            string query = "insert or replace into user_quest (quest_id, status, score, clear_time) values(" +
                userQuestModel.quest_id + "," +
                userQuestModel.status + "," +
                userQuestModel.score + "," +
                userQuestModel.clear_time + ")";
            sqlDB.ExecuteNonQuery(query);
        }
    }

    public static UserQuestModel Get(int quest_id)
    {
        UserQuestModel userQuestModel = new UserQuestModel();
        string query = "select * from user_quest where quest_id = " + quest_id;
        SqliteDatabase sqlDB = new SqliteDatabase(GameUtil.Const.SQLITE_FILE_NAME);
        DataTable dataTable = sqlDB.ExecuteQuery(query);
        foreach (DataRow dr in dataTable.Rows)
        {
            userQuestModel.quest_id = int.Parse(dr["quest_id"].ToString());
            userQuestModel.status = int.Parse(dr["status"].ToString());
            userQuestModel.score = int.Parse(dr["score"].ToString());
            userQuestModel.clear_time = int.Parse(dr["clear_time"].ToString());
        }
        return userQuestModel;
    }
}
↑Modelクラスの作成

所持アイテム数の表示

マイケル
マイケル
それでは機能を追加していきます!
まずは所持アイテム数の表示からです!
マイケル
マイケル
こちらは簡単で、下記のようにメニューシーンのスクリプトを用意して、
UserProfileModelから設定するだけです!
Screenshot 2020 12 13 1 05 47
↑アイテム表示部の構造
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MenuManager : MonoBehaviour
{
    /** UI部品 */
    private const string LOGIN_USER_TEXT = "LoginUserText";
    private const string COIN_COUNT_TEXT = "CoinCountText";
    private const string BANANA_FREE_COUNT_TEXT = "BananaFreeCountText";
    private const string BANANA_COUNT_TEXT = "BananaCountText";
    private Text loginUserText;
    private Text bananaCountText;
    private Text bananaFreeCountText;
    private Text coinCountText;

    void Start()
    {
        loginUserText = GameObject.Find(LOGIN_USER_TEXT).GetComponent<Text>();
        bananaCountText = GameObject.Find(BANANA_COUNT_TEXT).GetComponent<Text>();
        bananaFreeCountText = GameObject.Find(BANANA_FREE_COUNT_TEXT).GetComponent<Text>();
        coinCountText = GameObject.Find(COIN_COUNT_TEXT).GetComponent<Text>();

        UserProfileModel userProfileModel = UserProfile.Get();
        if (string.IsNullOrEmpty(userProfileModel.user_id))
        {
            Debug.LogError("TitleSceneを起動してユーザー登録を行ってください。");
            return;
        }
        // アイテム数を設定
        loginUserText.text = userProfileModel.user_name.ToString();
        bananaCountText.text = userProfileModel.banana.ToString();
        bananaFreeCountText.text = userProfileModel.banana_free.ToString();
        coinCountText.text = userProfileModel.friend_coin.ToString();
    }
}
マイケル
マイケル
UserProfileはログイン時に取得しているはずなので、
UserProfileModelクラスからの取得だけで大丈夫です!
エレキベア
エレキベア
簡単クマ〜〜〜〜〜

クエスト一覧の表示

マイケル
マイケル
次にクエスト一覧の表示です!
こちらは取得したクエストレコードの数だけ、
ScrollViewコンポーネントにアイテムを追加する
形で実装しています!
Screenshot 2020 12 13 1 06 31
↑クエスト一覧の構造
Screenshot 2020 12 13 1 07 58
↑リストに表示するアイテム
エレキベア
エレキベア
ここは少し難しそうクマね・・・。
マイケル
マイケル
まずは準備として、
定数クラスに使用する定数、
タイトル画面のテーブル作成処理にuser_questテーブル

を追加しましょう!
using System;

namespace GameUtil
{
    public static class Const
    {

・・・略・・・

        /** 定数値 */
        public const string ITEM_TYPE_BANANA = "1";
        public const string ITEM_TYPE_BANANA_FREE = "2";
        public const string ITEM_TYPE_FRIEND_COIN = "3";
        public const string QUEST_START = "1";
        public const string QUEST_END = "2";
    }
}
↑定数の追加
public class TitleManager : MonoBehaviour
{

・・・略・・・

    private void Awake()
    {
        // SQLiteのDBファイル作成
        string DBPath = Application.persistentDataPath + "/" + GameUtil.Const.SQLITE_FILE_NAME;
        if (!File.Exists(DBPath)) {
            File.Create(DBPath);
        }
        // テーブル作成処理
        UserProfile.CreateTable();
        UserQuest.CreateTable();
        MasterQuest.CreateTable();
    }

・・・略・・・

}
↑テーブルの作成処理
マイケル
マイケル
準備ができたらScrollViewのContentオブジェクトに下記を追加します!
メイン処理となるので少し長いですが、
基本的にはmaster_questテーブルからのレコード取得と、
クエスト情報を表示のための変換処理
となります。
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UniRx;

public class ScrollContentManager : MonoBehaviour
{
    /** 設定値 */
    public GameObject questItemPrefab;
    public Sprite newQuestImage;
    public Sprite clearQuestImage;

    /** UI部品 */
    private const string QUEST_ITEM_ID_HEAD = "QuestItem_";
    private const string QUEST_NAME_TEXT = "QuestNameText";
    private const string QUEST_STAR_LABEL = "label";
    private const string QUEST_STAR_TEXT = "QuestStarText";
    private const string QUEST_START_BUTTON = "QuestStartButton";
    private const string QUEST_DETAIL_TEXT = "QuestDetailText";
    private const string QUEST_STATUS_IMAGE = "QuestStatusImage";
    private GameObject questStartButton;
    private GameObject questDetailText;

    /** 変数 */
    private string selectQuestId;

    void OnEnable()
    {
        questDetailText = GameObject.Find(QUEST_DETAIL_TEXT);
        questStartButton = GameObject.Find(QUEST_START_BUTTON);
        // 初期化処理
        questDetailText.GetComponent<Text>().text = "";
        questStartButton.GetComponent<Button>().interactable = false;
        // 子オブジェクトを削除
        foreach (Transform n in transform)
        {
            Destroy(n.gameObject);
        }
    }

    private void Update()
    {
        // クエストが選択されたら活性
        if (!string.IsNullOrEmpty(selectQuestId))
            questStartButton.GetComponent<Button>().interactable = true;
    }

    // スクロールアイテム設定処理
    public void SetScrollItem()
    {
        UserProfileModel userProfileModel = UserProfile.Get();
        if (string.IsNullOrEmpty(userProfileModel.user_id))
        {
            Debug.LogError("TitleSceneを起動してユーザー登録を行ってください。");
            return;
        }

        // user_questデータリスト取得後処理
        Action action = () =>
        {
            // MasterQuestデータを取得
            Dictionary<int, MasterQuestModel> masterQuestListModel = MasterQuest.GetMasterQuest();
            foreach (var element in masterQuestListModel)
            {
                MasterQuestModel masterQuestModel = masterQuestListModel[element.Key];
                GameObject item = Instantiate(questItemPrefab);
                item.GetComponent<RectTransform>().SetParent(transform, false);
                // 名前を設定
                item.name = QUEST_ITEM_ID_HEAD + masterQuestModel.quest_id;
                // クエスト名を設定
                Text questName = item.GetComponent<Transform>().Find(QUEST_NAME_TEXT).GetComponent<Text>();
                questName.text = masterQuestModel.quest_name;
                // クエスト星を設定
                Text questStar = item.GetComponent<Transform>().Find(QUEST_STAR_LABEL).Find(QUEST_STAR_TEXT).GetComponent<Text>();
                questStar.text = ConvQuestStar(masterQuestModel.quest_star);
                // イベントトリガー追加(引数:クエストID)
                EventTrigger eventTrigger = item.AddComponent<EventTrigger>();
                eventTrigger.triggers = new List<EventTrigger.Entry>();
                EventTrigger.Entry entry = new EventTrigger.Entry();
                entry.eventID = EventTriggerType.PointerDown;
                entry.callback.AddListener((x) => PushQuestItem(masterQuestModel.quest_id.ToString()));
                eventTrigger.triggers.Add(entry);
                // クエスト状態を設定
                Sprite statusImage = null;
                UserQuestModel userQuestModel = UserQuest.Get(masterQuestModel.quest_id);
                if (userQuestModel == null || userQuestModel.quest_id == 0)
                {
                    // UserQuest未作成 -> NEW
                    statusImage = newQuestImage;
                }
                else
                {
                    // クエスト状態:終了 -> CLEAR
                    switch (userQuestModel.status.ToString())
                    {
                        case GameUtil.Const.QUEST_END:
                            statusImage = clearQuestImage;
                            break;
                    }
                }
                if (statusImage == null)
                    item.GetComponent<Transform>().Find(QUEST_STATUS_IMAGE).gameObject.SetActive(false);
                else
                    item.GetComponent<Transform>().Find(QUEST_STATUS_IMAGE).GetComponent<Image>().sprite = statusImage;
            }
        };
        // user_questデータリストを取得
        StartCoroutine(CommunicationManager.ConnectServer("user_quest", "&user_id=" + userProfileModel.user_id, action));
    }

    // 難易度(星)に変換する
    private string ConvQuestStar(int count)
    {
        string star = "";
        for (int i = 0; i < count; i++)
        {
            if (star.Length != 0)
                star += " ";
            star += "★";
        }
        return star;
    }

    // クエストアイテム押下時
    void PushQuestItem(string id)
    {
        selectQuestId = id;
        // IDに紐づくクエスト詳細を取得
        MasterQuestModel masterQuestModel = MasterQuest.GetMasterQuest(id);
        questDetailText.GetComponent<Text>().text = masterQuestModel.quest_detail;
        Debug.Log("SELECT:" + id);
    }

    // スタートボタン押下時
    public void PushStartButton()
    {
        // クエストシーンに遷移
        SceneManager.LoadSceneAsync(GameUtil.Const.SCENE_BUTTLE).AsObservable()
            .Subscribe(_ =>
            {
                ButtleManager buttleManager = FindObjectOfType<ButtleManager>();
                buttleManager.questId = selectQuestId;
            });
    }
}
↑クエスト一覧の表示処理
マイケル
マイケル
上記スクリプトをScrollView内のContentにアタッチし、
リスト表示したいタイミングでSetScrollItem()メソッドを呼び出しましょう!
また、押されたクエストのIDが分かるように、
EventTriggerの引数にIDを設定している点には注意です。
エレキベア
エレキベア
じっくり見ればわかってきたクマ〜〜〜
マイケル
マイケル
あとはこれまでも使用してきた
CommunicationManagerクラスにも
エラー処理とレスポンスからのテーブル更新処理
を追加しておきます!
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using System;
using System.Linq;

[Serializable]
public class ResponseObjects
{
	public int master_data_version;
	public UserProfileModel user_profile;
	public UserQuestModel[] user_quest;
	public MasterQuestModel[] master_quest;
}

public class CommunicationManager : MonoBehaviour
{

・・・略・・・

	public static IEnumerator ConnectServer(string endpoint, string paramater, Action action = null)
    {
		// *** リクエストの送付 ***
		UnityWebRequest unityWebRequest = UnityWebRequest.Get(GameUtil.Const.SERVER_URL + endpoint + "?client_master_version=" + LocalDataManager.GetMasterDataVersion() + 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_INVALID_DATA:
					Debug.LogError("サーバーでエラーが発生しました。[不正なデータ]");
					break;
				case GameUtil.Const.ERROR_INVALID_SCHEDULE:
					Debug.LogError("サーバーでエラーが発生しました。[期間外]");
					break;
				default:
					Debug.LogError("サーバーでエラーが発生しました。[システムエラー]");
					break;
			}
			yield break;
		}

		// *** SQLiteへの保存処理 ***
		ResponseObjects responseObjects = JsonUtility.FromJson<ResponseObjects>(text);

・・・略・・・
        // user_questテーブルの登録
		if (responseObjects.user_quest != null)
			UserQuest.Set(responseObjects.user_quest)
		// 正常終了アクション実行
		if (action != null)
		{
			action();
			action = null;
		}
	}
}
マイケル
マイケル
これでクエスト一覧の表示は完成です。
エレキベア
エレキベア
やったクマ〜〜〜〜

クエスト処理の作成

マイケル
マイケル
最後にクエストシーンを実装しましょう!
Screenshot 2020 12 13 18 32 23
↑クエストシーンの構造
マイケル
マイケル
今回は中身にはこだわらず、

クエストを選択したらクエストシーンに遷移、
クエスト(じゃんけん)をクリアしたら、アイテム取得と状態更新

を行う処理とします!
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class ButtleManager : MonoBehaviour
{
    /** コンポーネント */
    public Sprite guJanImage;
    public Sprite chokiJanImage;
    public Sprite paJanImage;
    public Sprite itemBanana;
    public Sprite itemBananaFree;
    public Sprite itemCoin;

    /** UI部品 */
    private const string ITEM_CANVAS = "ItemCanvas";
    private const string ITEM_POPUP = "ItemPopup";
    private const string ITEM_IMAGE = "ItemImage";
    private const string ITEM_COUNT_TEXT = "ItemCountText";
    private const string ENEMY_IMAGE = "Enemy";
    private const string ENEMY_JAN_IMAGE = "EnemyJan";
    private const string MY_JAN_GU = "JanGu";
    private const string MY_JAN_CHOKI = "JanChoki";
    private const string MY_JAN_PA = "JanPa";
    private const string RESULT_TEXT = "ResultText";
    private GameObject itemCanvas;
    private Animator popupAnimator;
    private Image itemImage;
    private Text itemCountText;
    private Image enemyImage;
    private Image enemyJanImage;
    private GameObject myJanGu;
    private GameObject myJanChoki;
    private GameObject myJanPa;
    private GameObject resultText;
    private List<Sprite> janList;
    private string selectJan;

    /** 変数 */
    public string questId;
    private int dispJanCount = 0;
    private bool boolRulete;
    private UserProfileModel userProfileModel;
    private float time = 0;

    void Start()
    {
        itemCanvas = GameObject.Find(ITEM_CANVAS);
        popupAnimator = GameObject.Find(ITEM_POPUP).GetComponent<Animator>();
        itemImage = GameObject.Find(ITEM_IMAGE).GetComponent<Image>();
        itemCountText = GameObject.Find(ITEM_COUNT_TEXT).GetComponent<Text>();
        enemyImage = GameObject.Find(ENEMY_IMAGE).GetComponent<Image>();
        enemyJanImage = GameObject.Find(ENEMY_JAN_IMAGE).GetComponent<Image>();
        myJanGu = GameObject.Find(MY_JAN_GU);
        myJanChoki = GameObject.Find(MY_JAN_CHOKI);
        myJanPa = GameObject.Find(MY_JAN_PA);
        resultText = GameObject.Find(RESULT_TEXT);
        // ジャンケンリスト格納
        janList = new List<Sprite>();
        janList.Add(guJanImage);
        janList.Add(chokiJanImage);
        janList.Add(paJanImage);
        // アイテムCanvas非表示
        itemCanvas.SetActive(false);

        userProfileModel = UserProfile.Get();
        if (string.IsNullOrEmpty(userProfileModel.user_id))
        {
            Debug.LogError("TitleSceneを起動してユーザー登録を行ってください。");
            return;
        }
        // クエスト開始処理
        Action action = () =>
        {
            boolRulete = true;
        };
        StartCoroutine(CommunicationManager.ConnectServer("quest_start", "&user_id=" + userProfileModel.user_id + "&quest_id=" + questId, action));
    }

    void Update()
    {
        if (!boolRulete)
            return;
        //クリアタイム計算
        time += Time.deltaTime * 100;
        // ジャンケンを表示
        enemyJanImage.sprite = janList[dispJanCount];
        dispJanCount++;
        if (dispJanCount >= janList.Count)
            dispJanCount = 0;
    }

    // ジャンケンボタン押下時
    public void PushJanButton(GameObject pushJan)
    {
        if (!boolRulete)
            return;
        // 押されたオブジェクトのみ表示
        myJanGu.SetActive(false);
        myJanChoki.SetActive(false);
        myJanPa.SetActive(false);
        pushJan.SetActive(true);
        // 敵の手を表示
        selectJan = pushJan.GetComponent<Image>().sprite.name;
        boolRulete = false;
        Invoke("DispResult", 2.0f);
    }

    // ジャンケン結果表示
    private void DispResult()
    {
        // 結果判定
        string result = "DRAW";
        switch (enemyJanImage.sprite.name)
        {
            case "jan_gu":
                if (selectJan == "jan_pa")
                    result = "WIN";
                else if (selectJan == "jan_choki")
                    result = "LOSE";
                break;
            case "jan_choki":
                if (selectJan == "jan_gu")
                    result = "WIN";
                else if (selectJan == "jan_pa")
                    result = "LOSE";
                break;
            case "jan_pa":
                if (selectJan == "jan_choki")
                    result = "WIN";
                else if (selectJan == "jan_gu")
                    result = "LOSE";
                break;
        }
        resultText.GetComponent<Text>().text = result;

        // 結果により処理分岐
        UserQuestModel userQuestModel = UserQuest.Get(int.Parse(questId));
        if (result == "WIN" && userQuestModel.status.ToString() != GameUtil.Const.QUEST_END)
            GameClear();
        else
            Invoke("BackScene", 2.0f);
    }

    // ゲームクリア
    private void GameClear()
    {
        // クエスト情報を取得
        MasterQuestModel masterQuestModel = MasterQuest.GetMasterQuest(questId);

        // 初回クリアの場合、アイテム情報を表示
        itemCanvas.SetActive(true);
        popupAnimator.SetTrigger(GameUtil.Const.POPUP_ANIM_OPEN);
        Sprite setItemImage = null;
        switch (masterQuestModel.item_type.ToString())
        {
            case GameUtil.Const.ITEM_TYPE_BANANA:
                setItemImage = itemBanana;
                break;
            case GameUtil.Const.ITEM_TYPE_BANANA_FREE:
                setItemImage = itemBananaFree;
                break;
            case GameUtil.Const.ITEM_TYPE_FRIEND_COIN:
                setItemImage = itemCoin;
                break;
            default:
                break;
        }
        itemImage.sprite = setItemImage;
        itemCountText.text = masterQuestModel.item_count.ToString();
    }

    // アイテム閉じるボタン
    public void PushItemCloseButton()
    {
        popupAnimator.SetTrigger(GameUtil.Const.POPUP_ANIM_CLOSE);
        // 勝った場合:クリア情報を登録
        Action action = () =>
        {
            Invoke("BackScene", 2.0f);
        };
        StartCoroutine(CommunicationManager.ConnectServer("quest_end", "&user_id=" + userProfileModel.user_id + "&quest_id=" + questId + "&score=" + (time * 0.5) + "&clear_time=" + Mathf.CeilToInt(time), action));
    }

    // メインシーン遷移処理
    private void BackScene()
    {
        SceneManager.LoadScene(GameUtil.Const.SCENE_MAIN);
    }
}
マイケル
マイケル
このようにクエスト開始時と終了時に更新処理を行うことで
進行状況を管理しています!
マイケル
マイケル
IDもパラメタとしては渡してはいますが、
何を選んでもゴロヤンと戦うのみとなっています・・・。
エレキベア
エレキベア
(クソゲークマ・・・・。)
マイケル
マイケル
みなさんは受け取ったパラメータを駆使して
バリエーションを広げてみてくださいね!

おわりに

マイケル
マイケル
というわけで今回は簡単なクエスト処理の実装でした!
どうだったかな?
エレキベア
エレキベア
大変だったけどアイテム入手や進行状況が変わるのは楽しかったクマ〜〜〜〜
マイケル
マイケル
土台ができればあとは中身を実装していくだけだしね!
今後も地道に作っていこう!
マイケル
マイケル
それでは今日はこの辺で!
次回もお楽しみに!!
エレキベア
エレキベア
クマ〜〜〜〜〜〜〜

【Unity】第四回 ソーシャルゲームを作る 〜クエスト機能の実装〜 【AWS x Laravel】 〜完〜

コメント