【Unity】クリッカーゲーム(1)骨組みを作る【Game A Week】

クリッカーゲームを作る(1)

クソゲー作っていきます。今回はみんな大好きクリッカーゲーム。ひたすらクリックし続けることでデジタルデータが増殖していくという、生産性があるのかないのかよくわからないゲームです。でもなぜかクリックしちゃうよね。

目次

下準備

今回作るゲームはこんな感じです。寿司は私が描いた仮画像です。このままリリースしても良いクオリティだと思います。

寿司工場01

真ん中にある寿司をクリックするとお金が増えていくという夢のようなゲームです。コストを払ってクリック単価を上げたり、自動収益を得る仕組みも入れます。

まず、プロジェクトを作成して、いろいろアレします。

  1. Unity Hubで新しいプロジェクトを作成。テンプレートは 2D (Built-In Render Pipeline)。
  2. Hierarchyタブでオブジェクトをガシガシと作っていく。
  3. TextMeshProを使うための準備。日本語フォントのインポート、フォントアセットの作成。
  4. 画面のサイズやアスペクト比の設定。Gameタブは「16:9 Aspect」、Canvasオブジェクトの「UI Scale Mode」は「Scale With Screen Size」、「Screen Match Mode」は「Expand」。

現在のHierarchyはこんな感じです。今後変わるかも。

寿司工場02

Canvasオブジェクトの「UI Scale Mode」を「Scale With Screen Size」にすると、画面のサイズが変わってもオブジェクトがいい感じに拡大縮小してくれます。

寿司をクリックしたらお金が増える

まずは、寿司をクリックしたらお金が1円増える仕組みを作ります。下記のC#スクリプトを作成してSushiオブジェクトにアタッチします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;

public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
  private int money = 0; // 所持金額の初期値
  private TMP_Text moneyText; // 所持金額を表示するテキスト

  void Start()
  {
    // MoneyTextを持つオブジェクトを検索して、TMP_Textコンポーネントを取得する
    moneyText = GameObject.Find("MoneyText").GetComponent<TMP_Text>();
  }

  // 寿司オブジェクトがクリックされた時の処理
  public void OnPointerClick(PointerEventData eventData)
  {
    money += 1; // クリックすると1円増加する
    UpdateMoneyUI(); // UIを更新する関数を呼び出す
  }

  // UIを更新する関数
  void UpdateMoneyUI()
  {
    moneyText.text = money.ToString(); // moneyTextを更新する
  }
}

IPointerClickHandlerOnPointerClick(PointerEventData eventData)ですね。とか言いながらあまりよくわかってない。それでも寿司をクリックしたらお金が増えるようになったので良しとしましょう。

クリック単価を上げる(寿司職人)

お金を払ってクリック単価を上げる仕組みを導入します。10円払うと1クリックにつき2円、30円払うと1クリックにつき4円みたいにします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;

public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
  private int money = 0; // 所持金額の初期値
  private int moneyPerClick = 1; // クリックあたりの初期収益
  private TMP_Text moneyText; // 所持金額を表示するテキスト
  public Button upgradeButton; // クリック単価を上げるためのボタン
  public TMP_Text upgradeCostText; // アップグレードコスト表示テキスト

  private int[] upgradeCosts = { 10, 30, 90, 270, 810 }; // アップグレードのコストの配列
  private int currentUpgradeIndex = 0; // 現在のアップグレードインデックス

  void Start()
  {
    // MoneyTextを持つオブジェクトを検索して、TMP_Textコンポーネントを取得する
    moneyText = GameObject.Find("MoneyText").GetComponent<TMP_Text>();

    // ボタンのクリックイベントにUpgradeClickEfficiency関数を追加する
    upgradeButton.onClick.AddListener(UpgradeClickEfficiency);

    // 初期のアップグレードコストを表示する
    if (currentUpgradeIndex < upgradeCosts.Length)
    {
      upgradeCostText.text = upgradeCosts[currentUpgradeIndex].ToString() + "円でアップグレード";
    }
    else
    {
      upgradeCostText.text = "もう無理";
    }
  }

  // 寿司オブジェクトがクリックされた時の処理
  public void OnPointerClick(PointerEventData eventData)
  {
    money += moneyPerClick; // クリックすると現在のクリックあたりの収益分増加する
    UpdateMoneyUI(); // UIを更新する関数を呼び出す
  }

  // UIを更新する関数
  void UpdateMoneyUI()
  {
    moneyText.text = money.ToString(); // moneyTextを更新する
  }

  // クリック単価をアップグレードする関数
  void UpgradeClickEfficiency()
  {
    if (currentUpgradeIndex < upgradeCosts.Length && money >= upgradeCosts[currentUpgradeIndex])
    {
      money -= upgradeCosts[currentUpgradeIndex]; // アップグレードコストを支払う
      moneyPerClick *= 2; // クリックあたりの収益を倍にする
      currentUpgradeIndex++; // アップグレードインデックスを増加させる

      UpdateMoneyUI(); // お金のUIを更新する

      // 次のアップグレードコストを更新する
      if (currentUpgradeIndex < upgradeCosts.Length)
      {
        upgradeCostText.text = upgradeCosts[currentUpgradeIndex].ToString() + "円でアップグレード";
      }
      else
      {
        upgradeCostText.text = "もう無理";
      }
    }
  }
}

一気にコードが増えたけど、特に難しいところはないかな。ボタンがクリックされたら何かするときは.onClick.AddListener()

ボタンの無効化

で、これだとお金が足りないときやアップグレードの上限に達したときもアップグレードボタンをクリックできてしまいます。ボタンを無効化する処理を入れます。

public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
    ...省略...

    void Start()
    {
        ...省略...
        UpdateUpgradeButton(); // ボタンの状態を更新する
    }

    // 寿司オブジェクトがクリックされた時の処理
    public void OnPointerClick(PointerEventData eventData)
    {
        ...省略...
        UpdateUpgradeButton(); // ボタンの状態を更新する
    }

    ...省略...

    // クリック単価をアップグレードする関数
    void UpgradeClickEfficiency()
    {
        if (currentUpgradeIndex < upgradeCosts.Length && money >= upgradeCosts[currentUpgradeIndex])
        {
            ...省略...
            UpdateUpgradeButton(); // ボタンの状態を更新する
        }
    }

    // アップグレードボタンの状態を更新する関数
    void UpdateUpgradeButton()
    {
        if (currentUpgradeIndex < upgradeCosts.Length) // カンストしてなければ
        {
            upgradeCostText.text = upgradeCosts[currentUpgradeIndex].ToString() + "円でアップグレード"; // ボタンのテキスト
            upgradeButton.interactable = money >= upgradeCosts[currentUpgradeIndex]; // お金が足りていれば有効(true)、足りなければ無効(false)
        }
        else // カンストしてたら
        {
            upgradeCostText.text = "もう無理"; // ボタンのテキスト
            upgradeButton.interactable = false; // 無効
        }
    }
}

アップグレードボタンの状態を更新する関数UpdateUpgradeButton()を作ります。この中でボタンのテキストの作成、有効/無効の設定をします。

そして、ゲーム開始時(Start())、寿司クリック時(OnPointerClick())、クリック単価更新時(UpgradeClickEfficiency())にこの関数を呼び出しました。

アップグレードコストの計算を賢くしたい

クリック単価を上げるためのアップグレードコストは、upgradeCosts = { 10, 30, 90, 270, 810 }というように配列に入れて管理しています。これ、初期値が10で以降3倍に増えていくのでもっとうまく記述できそうです。

あと、現在のレベルとクリック単価も表示したい。

public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
    ...省略...
    public TMP_Text chefLevelText; // 職人のレベル表示テキスト
    public TMP_Text chefLevelRateText; // 職人のクリック単価表示テキスト

    private int initialUpgradeCost = 10; // 職人の初期アップグレードコスト
    private int currentUpgradeIndex = 0; // 職人の現在のレベル

    void Start()
    {
        ...省略...
        // 各UI要素の参照を取得
        chefLevelText = GameObject.Find("ChefLevel").GetComponent<TMP_Text>();
        chefLevelRateText = GameObject.Find("ChefLevelRate").GetComponent<TMP_Text>();

        ...省略...

        UpdateUpgradeButton(); // ボタンの状態を更新する
        UpdateLevelUI(); // レベルUIを更新
    }

    ...省略...

    // クリック単価をアップグレードする関数
    void UpgradeClickEfficiency()
    {
        int upgradeCost = initialUpgradeCost * (int)Mathf.Pow(3, currentUpgradeIndex); // アップグレードコストを計算

        if (money >= upgradeCost)
        {
            ...省略...
            UpdateLevelUI(); // レベルUIを更新する
        }
    }

    // アップグレードボタンの状態を更新する関数
    void UpdateUpgradeButton()
    {
        int upgradeCost = initialUpgradeCost * (int)Mathf.Pow(3, currentUpgradeIndex); // アップグレードコストを計算

        if (money >= upgradeCost) // お金が足りていたら
        {
            upgradeCostText.text = upgradeCost.ToString() + "円でアップグレード";
            upgradeButton.interactable = true;
        }
        else // お金が足りなければ
        {
            upgradeCostText.text = upgradeCost.ToString() + "円でアップグレード";
            upgradeButton.interactable = false;
        }

        if (currentUpgradeIndex >= 100) // レベルが100以上ならば
        {
            upgradeCostText.text = "もう無理";
            upgradeButton.interactable = false;
        }

        ...省略...
    }

    // レベルUIを更新する関数
    void UpdateLevelUI()
    {
        chefLevelText.text = "Lv." + currentUpgradeIndex.ToString(); // シェフレベルを表示する
        chefLevelRateText.text = moneyPerClick.ToString() + "円/クリック"; // クリック単価を表示する
    }
}

initialUpgradeCost変数を追加して、アップグレードコストが3倍ずつ増えるように計算するロジックを追加しました。upgradeCost = initialUpgradeCost * (int)Mathf.Pow(3, currentUpgradeIndex)の部分ですね。

そして、chefLevelTextchefLevelRateText変数を追加して、UpdateLevelUI()関数でこれらのテキストを更新するようにしました。

以上で、寿司職人のレベルを上げるとクリック単価が上がる仕組みができあがりました。いえい。

自動的にお金が増える(寿司ロボ)

寿司ロボのレベルを上げると自動的にお金が増えていく仕組みを導入したい!

public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
    ...省略...
    // 寿司ロボの追加要素
    public Button roboUpgradeButton; // 寿司ロボのアップグレードボタン
    public TMP_Text roboUpgradeCostText; // 寿司ロボのアップグレードコストテキスト
    public TMP_Text roboLevelText; // 寿司ロボのレベル表示テキスト
    public TMP_Text roboLevelRateText; // 寿司ロボの自動収益表示テキスト

    private int roboInitialUpgradeCost = 1000; // 寿司ロボの初期アップグレードコスト
    private int roboCurrentUpgradeIndex = 0; // 寿司ロボの現在のレベル
    private int roboMoneyPerSecond = 0; // 寿司ロボの1秒あたりの収益

    void Start()
    {
        ...省略...
        roboLevelText = GameObject.Find("RoboLevelText").GetComponent<TMP_Text>();
        roboLevelRateText = GameObject.Find("RoboLevelRateText").GetComponent<TMP_Text>();

        // ボタンのクリックイベントにUpgradeClickEfficiency関数を追加する
        chefUpgradeButton.onClick.AddListener(UpgradeClickEfficiency);
        roboUpgradeButton.onClick.AddListener(UpgradeRoboEfficiency);

        // 初期のアップグレードコストとレベルを表示する
        UpdateUpgradeButton();
        UpdateRoboUpgradeButton();
        UpdateLevelUI();
        UpdateRoboLevelUI();

        // 寿司ロボの自動収益を1秒ごとに加算する
        StartCoroutine(AutoIncrementMoney());
    }

    // 寿司オブジェクトがクリックされた時の処理
    public void OnPointerClick(PointerEventData eventData)
    {
        ...省略...
    }

    // UIを更新する関数
    void UpdateMoneyUI()
    {
        ...省略...
    }

    // 寿司職人をアップグレードする関数
    void UpgradeClickEfficiency()
    {
        ...省略...
    }

    // 寿司ロボをアップグレードする関数
    void UpgradeRoboEfficiency()
    {
        int upgradeCost = roboInitialUpgradeCost * (int)Mathf.Pow(3, roboCurrentUpgradeIndex); // アップグレードコストを計算

        if (money >= upgradeCost)
        {
            money -= upgradeCost; // アップグレードコストを支払う
            roboMoneyPerSecond = (int)Mathf.Pow(2, roboCurrentUpgradeIndex); // 1秒あたりの収益を増やす
            roboCurrentUpgradeIndex++; // アップグレードインデックス(レベル)を増加させる

            UpdateMoneyUI(); // お金のUIを更新する
            UpdateRoboUpgradeButton(); // ボタンの状態を更新する
            UpdateRoboLevelUI(); // レベルのUIを更新する
        }
    }

    // 寿司職人のアップグレードボタンの状態を更新する関数
    void UpdateUpgradeButton()
    {
        ...省略...
    }

    // 寿司ロボのアップグレードボタンの状態を更新する関数
    void UpdateRoboUpgradeButton()
    {
        int upgradeCost = roboInitialUpgradeCost * (int)Mathf.Pow(3, roboCurrentUpgradeIndex); // アップグレードコストを計算

        if (money >= upgradeCost)
        {
            roboUpgradeCostText.text = upgradeCost.ToString() + "円でアップグレード";
            roboUpgradeButton.interactable = true;
        }
        else
        {
            roboUpgradeCostText.text = upgradeCost.ToString() + "円でアップグレード";
            roboUpgradeButton.interactable = false;
        }

        if (roboCurrentUpgradeIndex >= 100)
        {
            roboUpgradeCostText.text = "もう無理";
            roboUpgradeButton.interactable = false;
        }

        // ボタンがインタラクティブでない場合にグレーアウトするためのスタイルを適用
        ColorBlock colors = roboUpgradeButton.colors;
        if (roboUpgradeButton.interactable)
        {
            colors.normalColor = Color.white;
        }
        else
        {
            colors.normalColor = Color.gray;
        }
        roboUpgradeButton.colors = colors;
    }

    // 寿司職人のレベルUIを更新する関数
    void UpdateLevelUI()
    {
        ...省略...
    }

    // 寿司ロボのレベルUIを更新する関数
    void UpdateRoboLevelUI()
    {
        roboLevelText.text = "Lv." + roboCurrentUpgradeIndex.ToString(); // 寿司ロボのレベルを表示する
        roboLevelRateText.text = roboMoneyPerSecond.ToString() + "円/秒"; // 1秒あたりの収益を表示する
    }

    // 寿司ロボの自動収益を1秒ごとに加算するコルーチン
    IEnumerator AutoIncrementMoney()
    {
        while (true)
        {
            yield return new WaitForSeconds(1);
            money += roboMoneyPerSecond;
            UpdateMoneyUI();
            UpdateChefUpgradeButton();
            UpdateRoboUpgradeButton();
        }
    }
}

基本的には寿司職人のアップグレードと同じ処理をします。違うのは、寿司職人がクリックするとお金が増えるのに対して、寿司ロボは自動的にお金が増えるところです。

それが、31行目のStartCoroutine(AutoIncrementMoney())と124行目のIEnumerator AutoIncrementMoney(){...}ですね。

大きい数値を扱いたい

ここで問題が発生しました。ゲームをプレイしてみたところ、金額が大きくなるとうまくお金が増えていかないのです。お金が増えていくことで幸福感を得るゲームなのにこのバグは致命的です。

原因は、金額を入れる変数のデータ型がintだったためでした。int型で扱える数値はおよそ±21億なんですね。もっとお金がほしい!ということで、金額を入れる変数の型をlongに変更しました。long型は±922京の整数を扱うことができます。

この時はこれで解決したと思っていました(なぬ)。

コードの効率化

寿司職人関連のコードと寿司ロボ関連のコードに重複するところがあるので、効率化したい。こういうのはChatGPT氏にやってもらうが吉です。

using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;

public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
    private long money = 0;
    private long moneyPerClick = 1;
    private TMP_Text moneyText;
    public Button chefUpgradeButton;
    public TMP_Text chefUpgradeCostText;
    public TMP_Text chefLevelText;
    public TMP_Text chefLevelRateText;
    public Button roboUpgradeButton;
    public TMP_Text roboUpgradeCostText;
    public TMP_Text roboLevelText;
    public TMP_Text roboLevelRateText;

    private long initialUpgradeCost = 10;
    private int currentUpgradeIndex = 0;
    private long roboInitialUpgradeCost = 500;
    private int roboCurrentUpgradeIndex = 0;
    private long roboMoneyPerSecond = 0;

    void Start()
    {
        moneyText = GameObject.Find("MoneyText").GetComponent<TMP_Text>();
        chefLevelText = GameObject.Find("ChefLevelText").GetComponent<TMP_Text>();
        chefLevelRateText = GameObject.Find("ChefLevelRateText").GetComponent<TMP_Text>();
        roboLevelText = GameObject.Find("RoboLevelText").GetComponent<TMP_Text>();
        roboLevelRateText = GameObject.Find("RoboLevelRateText").GetComponent<TMP_Text>();

        chefUpgradeButton.onClick.AddListener(UpgradeClickEfficiency);
        roboUpgradeButton.onClick.AddListener(UpgradeRoboEfficiency);

        UpdateUI();
        StartCoroutine(AutoIncrementMoney());
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        money += moneyPerClick;
        UpdateUI();
    }

    void UpdateUI()
    {
        moneyText.text = money.ToString();
        UpdateUpgradeButton(chefUpgradeButton, chefUpgradeCostText, initialUpgradeCost, currentUpgradeIndex, 3);
        UpdateUpgradeButton(roboUpgradeButton, roboUpgradeCostText, roboInitialUpgradeCost, roboCurrentUpgradeIndex, 2);
        chefLevelText.text = "Lv." + currentUpgradeIndex;
        chefLevelRateText.text = moneyPerClick + "円/クリック";
        roboLevelText.text = "Lv." + roboCurrentUpgradeIndex;
        roboLevelRateText.text = roboMoneyPerSecond + "円/秒";
    }

    void UpdateUpgradeButton(Button upgradeButton, TMP_Text upgradeCostText, long initialCost, int currentIndex, int multiplier)
    {
        long upgradeCost = initialCost * (long)Mathf.Pow(multiplier, currentIndex);

        if (currentIndex >= 100)
        {
            upgradeCostText.text = "もう無理";
            upgradeButton.interactable = false;
        }
        else
        {
            upgradeCostText.text = upgradeCost + "円でアップグレード";
            upgradeButton.interactable = money >= upgradeCost;
        }

        ColorBlock colors = upgradeButton.colors;
        colors.normalColor = upgradeButton.interactable ? Color.white : Color.gray;
        upgradeButton.colors = colors;
    }

    void UpgradeClickEfficiency()
    {
        UpgradeEfficiency(ref currentUpgradeIndex, ref moneyPerClick, initialUpgradeCost, 3, true);
    }

    void UpgradeRoboEfficiency()
    {
        UpgradeEfficiency(ref roboCurrentUpgradeIndex, ref roboMoneyPerSecond, roboInitialUpgradeCost, 2, false);
    }

    void UpgradeEfficiency(ref int currentIndex, ref long income, long initialCost, int multiplier, bool isChef)
    {
        long upgradeCost = initialCost * (long)Mathf.Pow(multiplier, currentIndex);

        if (money >= upgradeCost)
        {
            money -= upgradeCost;
            if (isChef)
            {
                income *= 2;
            }
            else
            {
                income = (long)Mathf.Pow(2, currentIndex);
            }
            currentIndex++;
            UpdateUI();
        }
    }

    IEnumerator AutoIncrementMoney()
    {
        while (true)
        {
            yield return new WaitForSeconds(1);
            money += roboMoneyPerSecond;
            UpdateUI();
        }
    }
}

はい、スッキリしました。

ゲームバランスを調整できるようにしたい

この手のゲームというのは、ゲームバランスが重要ですよね。いくらコストをかけたら、どれくらいの効率でお金を稼げるのかというバランスです。このバランス調整をしやすくします。

public class SushiClicker : MonoBehaviour, IPointerClickHandler
{
    ...省略...
    private float chefUpgradeCostFactor = 3.0f;
    private float chefIncomeFactor = 2.0f;
    private float roboUpgradeCostFactor = 2.0f;
    private float roboIncomeFactor = 2.0f;

    void Start()
    {
        ...省略...
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        ...省略...
    }

    void UpdateUI()
    {
        ...省略...
        UpdateUpgradeButton(chefUpgradeButton, chefUpgradeCostText, initialUpgradeCost, currentUpgradeIndex, chefUpgradeCostFactor);
        UpdateUpgradeButton(roboUpgradeButton, roboUpgradeCostText, roboInitialUpgradeCost, roboCurrentUpgradeIndex, roboUpgradeCostFactor);
        ...省略...
    }

    void UpdateUpgradeButton()
    {
        ...省略...
    }

    void UpgradeClickEfficiency()
    {
        UpgradeEfficiency(ref currentUpgradeIndex, ref moneyPerClick, initialUpgradeCost, chefUpgradeCostFactor, chefIncomeFactor, true);
    }

    void UpgradeRoboEfficiency()
    {
        UpgradeEfficiency(ref roboCurrentUpgradeIndex, ref roboMoneyPerSecond, roboInitialUpgradeCost, roboUpgradeCostFactor, roboIncomeFactor, false);
    }

    void UpgradeEfficiency(ref int currentIndex, ref float income, long initialCost, float costMultiplier, float incomeMultiplier, bool isChef)
    {
        long upgradeCost = (long)(initialCost * Mathf.Pow(costMultiplier, currentIndex));

        if (money >= upgradeCost)
        {
            money -= upgradeCost;
            if (isChef)
            {
                income *= incomeMultiplier;
            }
            else
            {
                income = Mathf.Pow(incomeMultiplier, currentIndex);
            }
            currentIndex++;
            UpdateUI();
        }
    }

    IEnumerator AutoIncrementMoney()
    {
        ...省略...
    }
}

4〜7行目の変数を使って、簡単にゲームバランスを調整できるようにしました。

お金の表示をかっこよくしたい

金額表示を細かく更新したい

今、お金の表示は1秒ごとに増加する仕様になっています。これだとワクワク感が足りません。10ミリ秒ごとに表示を更新したほうが楽しいはずです。

...省略....
private float money = 0f;
private float moneyPerClick = 1f;
private float roboMoneyPerSecond = 0f;

...省略....

void UpdateUI()
{
    moneyText.text = Mathf.FloorToInt(money).ToString();
    ...省略....
}

...省略....

IEnumerator AutoIncrementMoney()
{
    while (true)
    {
        yield return new WaitForSeconds(0.01f);
        money += roboMoneyPerSecond / 100f;
        UpdateUI();
    }
}
  • (20行目)AutoIncrementMoney コルーチンの WaitForSeconds メソッドの引数を 0.01f に変更します。これでコルーチンの待機時間が10ミリ秒になります。
  • (21行目)roboMoneyPerSecondを100で割った値をmoneyに加算します。
  • (2-4行目)金額を入れる変数の型はlongにしていましたが、小数を扱うことになったのですべてfloatにします。
  • (10行目)Mathf.FloorToInt(money).ToString()で、金額を整数に変換して表示します。

これで、自動収益が10ミリ秒ごとに加算されるようになりました。

大きい数値を扱いたい(再)

金額を入れる変数の型をfloatにしたことで、金額が大きくなったときの不具合が再発してしまいました。この問題を解消するために金額を入れる変数の型をdoubleに変更しました。

それでも不具合は解消しません。原因はMathfクラスでした。Mathf.Pow()System.Math.Pow()に、Mathf.FloorToInt()System.Math.Floor()に修正したらうまくいきました。

MathfUnity用に最適化されており、高速に動作する。
浮動小数点数の精度で計算される。
System.Math.NET Framework標準のライブラリで、Mathfより低速。
高精度な計算ができる。

金額を読みやすくしたい

金額の表示が数字の羅列だとわかりにくいので読みやすくしたい。「1234567890円」を「12.345億円」みたいにします。

3桁ごとにカンマで区切る方法もあるんだけど、日本の数値は4桁区切りなのよね。

string FormatNumber(double number)
{
    if (number >= 1e20)
        return (number / 1e20).ToString("F3") + "垓";
    if (number >= 1e16)
        return (number / 1e16).ToString("F3") + "京";
    if (number >= 1e12)
        return (number / 1e12).ToString("F3") + "兆";
    if (number >= 1e8)
        return (number / 1e8).ToString("F3") + "億";
    if (number >= 1e4)
        return (number / 1e4).ToString("F3") + "万";
    return number.ToString("F0");
}

このような関数を作りました。以上はどうしよう。無量大数までやったほうがいい?

収益の効率アップ(おしながき)

ここまでで、寿司職人をアップグレードしてクリック単価を上げる仕組みと、寿司ロボをアップグレードして自動収益を得る仕組みを導入しました。

さらにもう一つ要素を追加したい!ちゅーことで、寿司メニューをアップグレードすると収益の効率がn%アップする仕組みをこっそり導入しました。

  • 寿司職人: クリック収益。
  • 寿司ロボ: 自動収益。
  • おしながき: 収益効率。

この3つの要素をアップグレードしながらお金を増やしていくというゲームになります。

振り返り

  • ボタン以外のオブジェクトをクリックしたときに何かしたいときはOnPointerClick(PointerEventData eventData)を使うということを学んだ。
  • ボタンをクリックしたときは.onClick.AddListener()
  • ボタンを有効/無効化する時はButton.interactable
  • 金額表示を自動的に増やす処理にコルーチンを使った。まだちゃんと理解できてない。コルーチンコルーチンコルーチン。
  • 金額を入れる変数の型が決まらず二転三転してしまった。大きな桁を扱うならば整数はlong、小数はdouble
  • 一応ゲームっぽい動作をするものが作れたので、あとはグラフィック、アニメーション、SEを追加していい感じにしたい。

でわでわ

クリッカーゲームを作る(1)

この記事が気に入ったら
いいね または フォローしてね!

シェアしてね

コメント

コメントする

目次