『Unityの寺子屋 定番スマホゲーム開発入門』「Chapter04 クッキークリッカーに改造しよう」

Unityの寺子屋 定番スマホゲーム開発入門

Unityの寺子屋 定番スマホゲーム開発入門

序文

お久しぶりです。

少し帰省していました。

その関係で前回の勉強会には参加できなかったので、自習として学習しました。

weeyble-game.connpass.com

進捗

  • Chapter04 クッキークリッカーに改造しよう
    • 4-1 木魚をタップして点数が入るように改造する
    • 4-2 ゲームバランスを調整する

コード実装部分(一部)

180715TheTemple\Assets\Scripts\GameManager.cs

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

using UnityEngine.UI;
using System;

public class GameManager : MonoBehaviour
{

    // 定数定義
    private const int MAX_ORB = 30; //オーブ最大値
    private const int RESPAWN_TIME = 30; //オーブが発生する秒数
                                        //private const float RESPAWN_TIME = 0.1f; //Test用-徳大売り出し
    private const int MAX_LEVEL = 2;    //最大お寺レベル

    //データセーブ用キー
    private const string KEY_SCORE = "SCORE";   //スコア
    private const string KEY_LEVEL = "LEVEL";   //レベル
    private const string KEY_ORB = "ORB";   //オーブ数
    private const string KEY_TIME = "TIME"; //時間

    //オブジェクト参照
    public GameObject orbPrefab;    //オーブプレハブ
    public GameObject smokePrefab;  //煙プレハブ
    public GameObject kusudamaPrefab;   //くす玉プレハブ
    public GameObject canvasGame;   //ゲームキャンパス
    public GameObject textScore;    //スコアテキスト
    public GameObject imageTemple;  //お寺
    public GameObject imageMokugyo; //木魚

    // スコア加算SE
    public AudioClip getScoreSE;
    // レベルアップ時SE
    public AudioClip levelUpSE;
    // ゲームクリア時SE
    public AudioClip clearSE;

    // メンバ変数
    private int score = 0;  //現在のスコア
    private int nextScore = 10; //レベルアップまでに必要なスコア
    private int currentOrb = 0; //現在のオーブ数
    private int templeLevel = 0;    //寺のレベル
    private DateTime lastDateTime;  //前回オーブを生成した時間
    private int[] nextScoreTable = new int[] { 100, 1000, 10000 }; //レベルアップ値

    private AudioSource audioSource;

    // まとめて生成するオーブの数
    private int numOfOrb;

    void Start()
    {
        // 木魚イメージ取得
        imageMokugyo = GameObject.Find("ImageMokugyo");
        PlayerPrefs.DeleteAll();    //Test用PlayerPrefs初期化用
        // オーディオソース取得
        audioSource = this.gameObject.GetComponent<AudioSource>();

        // 初期設定
        score = PlayerPrefs.GetInt(KEY_SCORE, 0);
        templeLevel = PlayerPrefs.GetInt(KEY_LEVEL, 0);

        nextScore = nextScoreTable[templeLevel];
        imageTemple.GetComponent<TempleManager>().SetTemplePicture(templeLevel);
        imageTemple.GetComponent<TempleManager>().SetTempleScale(score, nextScore);
        RefreshScoreText();
    }


    //ゲームデータをセーブ
    void SaveGameData()
    {
        PlayerPrefs.SetInt(KEY_SCORE, score);
        PlayerPrefs.SetInt(KEY_LEVEL, templeLevel);
        PlayerPrefs.SetInt(KEY_ORB, currentOrb);
        // DateTime型をString型に変換して保存する
        PlayerPrefs.SetString(KEY_TIME, lastDateTime.ToBinary().ToString());

        PlayerPrefs.Save();
    }



    // Update is called once per frame
    void Update()
    {
        // まとめて生成するオーブがあれば生成
        while (numOfOrb > 0)
        {
            // 0.1sずつすらしてメソッドを呼び出すことで動きをつける
            Invoke("CreateNewOrb", 0.1f * numOfOrb);
            numOfOrb--;
        }
    }

    void OnApplicationPause(bool pauseStatus)
    {
        if (pauseStatus)
        {
            // アプリがバックグラウンドへ移行
        }
        else
        {
            // バックグラウンドから復帰
            // 時間の復元
            string time = PlayerPrefs.GetString(KEY_TIME, "");
            // 時間がセーブされていない場合は
            if (time == "")
            {
                // セーブ時間を設定する
                lastDateTime = DateTime.UtcNow;
            }
            else
            {
                // セーブ時間を復元する
                long temp = Convert.ToInt64(time);
                lastDateTime = DateTime.FromBinary(temp);
            }

            numOfOrb = 0;
            // 時間によるオーブ自動生成
            // 最終セーブ時間との差分
            TimeSpan timeSpan = DateTime.UtcNow - lastDateTime;

            // 差分がRESPAWN_TIME以上なら時間分のオーブを生み出す
            if (timeSpan >= TimeSpan.FromSeconds(RESPAWN_TIME))
            {
                while (timeSpan > TimeSpan.FromSeconds(RESPAWN_TIME))
                {
                    if (numOfOrb < MAX_ORB)
                    {
                        numOfOrb++;
                    }
                    timeSpan -= TimeSpan.FromSeconds(RESPAWN_TIME);
                }
            }
        }        
    }

    // 新しいオーブの生成
    public void CreateNewOrb()
    {
        // 新しいオーブが生成されるタイミングでセーブ処理を行う
        lastDateTime = DateTime.UtcNow;
        Debug.Log("currentOrb = " + currentOrb);
        Debug.Log("MAX_ORB = " + MAX_ORB);
        if (currentOrb >= MAX_ORB)
        {
            return;
        }
        CreateOrb();
        currentOrb++;

        SaveGameData();
    }

    // オーブ生成
    public void CreateOrb()
    {
        // Debug.Log("CREATE ORB");
        GameObject orb = (GameObject)Instantiate(orbPrefab);
        // orbをCanvasGameの子要素に設定する
        orb.transform.SetParent(canvasGame.transform, false);
        // オーブをランダムに配置
        orb.transform.localPosition = new Vector3(
            UnityEngine.Random.Range(-100.0f, 100.0f),
            UnityEngine.Random.Range(-300.0f, -450.0f),
            0f);

        // オーブの種類を設定
        int kind = UnityEngine.Random.Range(0, templeLevel + 1);
        switch (kind)
        {
            case 0:
                orb.GetComponent<OrbManager>().SetKind(OrbManager.ORB_KIND.BLUE);
                break;
            case 1:
                orb.GetComponent<OrbManager>().SetKind(OrbManager.ORB_KIND.GREEN);
                break;
            case 2:
                orb.GetComponent<OrbManager>().SetKind(OrbManager.ORB_KIND.PURPLE);
                break;
        }

        // オーブを飛ばす
        orb.GetComponent<OrbManager>().FlyOrb();

        audioSource.PlayOneShot(getScoreSE);
        // 木魚アニメ再生
        // 現在のアニメーションステートを取得
        AnimatorStateInfo stateInfo
            = imageMokugyo.GetComponent<Animator>().GetCurrentAnimatorStateInfo(0);
        if(stateInfo.fullPathHash == Animator.StringToHash("Base Layer.get@ImageMokugyo"))
        {
            // すでに再生中なら先頭から
            imageMokugyo.GetComponent<Animator>().Play(stateInfo.fullPathHash, 0, 0.0f);
        }
        else
        {
            imageMokugyo.GetComponent<Animator>().SetTrigger("isGetScore");
        }
    }

    //オーブ入手
    public void GetOrb(int getScore)
    {
        if (score < nextScore)
        {
            score += getScore;

            //レベルアップ値を超えないよう制限
            if (score > nextScore)
            {
                score = nextScore;
            }

            // 寺レベルアップのチェック
            TempleLevelUp();
            // スコア更新
            RefreshScoreText();
            // 次のレベルが近づくに連れてスケールを大きくしていく
            imageTemple.GetComponent<TempleManager>().SetTempleScale(score, nextScore);

            //ゲームクリア判定
            if ((score == nextScore) && (templeLevel == MAX_LEVEL))
            {
                ClearEffect();
            }
        }
        currentOrb--;

        SaveGameData();
    }

    //スコアテキスト更新
    void RefreshScoreText()
    {
        textScore.GetComponent<Text>().text =
            "徳:" + score + " / " + nextScore;

    }
    //寺のレベル管理
    void TempleLevelUp()
    {
        if (score >= nextScore)
        {
            if (templeLevel < MAX_LEVEL)
            {
                templeLevel++;
                score = 0;
                // レベルアップエフェクト
                TempleLevelUpEffect();
                // 次のレベルに必要なスコアをセットする
                nextScore = nextScoreTable[templeLevel];
                // スプライトを変える
                imageTemple.GetComponent<TempleManager>().SetTemplePicture(templeLevel);
            }
        }
    }

    //レベルアップ時の演出
    void TempleLevelUpEffect()
    {
        // 煙オブジェクトをプレハブから生成
        GameObject smoke = (GameObject)Instantiate(smokePrefab);
        // 煙オブジェクトをcanvasGameの子要素に設定
        smoke.transform.SetParent(canvasGame.transform, false);
        // 並び順を調整する
        smoke.transform.SetSiblingIndex(2);
        // 0.5秒後に削除
        Destroy(smoke, 0.5f);

        // レベルアップSEを鳴らす
        audioSource.PlayOneShot(levelUpSE);
    }

    //寺が最後まで育った時の演出
    void ClearEffect()
    {
        // プレハブからくす玉オブジェクトを生成
        GameObject kusudama = (GameObject)Instantiate(kusudamaPrefab);
        kusudama.transform.SetParent(canvasGame.transform, false);

        // SEをならす
        audioSource.PlayOneShot(clearSE);
    }
}

実行結果

www.youtube.com …おもしろいのこれ…?

感想

クッキークリッカーというジャンルらしいが、普段あんまりゲームをやらないからか(昨日からどうぶつの森始めたけど)、おもしろさがよくわからない。
いや、おもしろくないよね、これ…?

まだ、改造前のなぞってオーブを集めるやつのほうがおもしろかったなー。

ちょっと個人的な事情で勉強以上に優先すべきタスクができてしまったので、しばらくブログの更新が滞りがちになると思います。

ユニティ雀!の開発は続行します。

yjkym.hatenablog.com

勉強会への参加はなるべく続けたいと思っていますが、面倒くさいなぁと思っています(笑)。

カロリーメイトください。

BGM

トランキライザー / 小西真奈美

www.youtube.com