AI×Unityでインベントリシステム作成!【AIを活用したクリッカーゲーム制作#07】

AIゲームクリエイターズ

AIを活用して、ゲーム制作!

初心者でもAIに助けてもらいながらUnityでゲームを開発する方法を解説します。

このページの内容は、下記の動画で詳しく説明しています

このページは、下記の動画で使用しているプロンプト(AIへの指示文)を載せることを目的として作成されています。

はじめに

ゲーム制作なんて自分には絶対に無理!

自分はプログラマーとか、専門家じゃないからゲームなんて作れるわけがない!

そんなふうに思っていませんか?

確かに自分ひとりでは難しいかもしれません

でも、最近はAIの進化がものすごく、これらAIを活用すれば

あなたもゲーム制作者になれるかもしれません!

さぁ、一緒に、AIに頼りまくったゲーム制作を始めていきましょう!

前回の振り返り

前回は、ゲーム(鉱石クリッカー)制作の進め方をChatGPTに提案してもらい

その提案された手順に従って、まずは採掘ツール(ピッケル)の種類を管理する仕組みを

ChatGPTに教えてもらいながら

UnityのScriptable Object(スクリプタブルオブジェクト)を使って作りました。

このチャレンジのコンセプト

  • AIを活用することで、初心者でもゲーム制作できることを目指す!
  • Unity(ユニティ)というゲームエンジンを使って制作する
  • AIはできる限り、無料のものを使用する(商用利用の関係や、どうしても必要な機能の関係で有料版を使用する場合もあり)
  • AIを活用したゲーム制作の過程を、省略せずに全てをお見せすることで、視聴者の方が真似をして再現できるようにする

このページで使用するAIツール

ChatGPT(チャットジーピーティー)

  • https://openai.com/ja-JP/chatgpt/
  • ChatGPTは、チャット形式で質問や指示を投げかけると、自然な言葉で回答してくれるAIです。
  • 今回は、ゲーム制作の進め方を提案してもらったり、具体的な手順を教えてもらったりしました。

Canva(キャンバ)

  • https://www.canva.com/
  • Canva(キャンバ)は、Webブラウザ上で使える画像デザインアプリです。
  • 直感的な操作感や、豊富なテンプレートで、簡単にデザイン作成ができます。
  • 今回は、機能の一つである「AI画像生成」を使用しました。

このページ(および動画)の活用方法

  • まずは真似して同じように試してみて、慣れてきたら少しアレンジするのが良いと思います
  • 手順などは、動画を見てもらうのが一番分かりやすいと思います
  • AIへの指示文(プロンプト)はこのページに載せていますので、コピペして、ご自身用に修正しながらご使用ください

それでは、AIに助けてもらいながら、ゲーム制作を始めていきましょう!

AIに進め方を聞く

下図は前回ChatGPTに提案してもらった「鉱石収集&ツール強化システム」を追加するための概略順序です。

前回は手順1~2まで進めましたので、今回は手順3から進めていきます。

まずは手順3の具体的な手順を聞いていきます。

次のステップに進んでください。
先ほどあなたが提案してくれた次のステップは下記の通りです。
このステップの手順を、できる限り具体的で詳細に教えてください。
既存のスクリプトに修正を加える場合は、必ずスクリプトの中身を書く前に
どのスクリプトを修正するかを私に伝えてください、現状のスクリプトを教えるようにします。

「次のステップ」
3. 鉱石の種類の管理
鉱石を種類ごとに管理する仕組みを導入。
鉱石ごとに異なるスプライトとポイント値を設定。
各鉱石の取得数を保存。
必要な変更:
スコア管理から鉱石ごとのカウントに変更。
UIの拡張(鉱石ごとの数値表示)。

「現状のヒエラルキー」

<現状のヒエラルキーを貼り付ける>
ChatGPTの回答例

鉱石の種類ごとに管理する仕組みを導入するためのステップを詳細に説明します。このステップでは、以下の要素を追加・修正します。

  1. 新しい鉱石データの管理
    • 各鉱石のスプライト、ポイント値、カウントを管理するスクリプタブルオブジェクトを作成します。
  2. UIの拡張
    • 各鉱石の取得数を表示するUIを追加します。
  3. スコア管理の変更
    • 鉱石ごとにカウントを保存し、ポイント加算処理を変更します。

ステップ 1: スクリプタブルオブジェクトの作成

  1. 新しいスクリプタブルオブジェクトを作成
    • Assets フォルダーで右クリックし、Create > C# Scriptを選択して、OreData.csという名前のスクリプトを作成します。
    • 以下のコードを記述してください。
using UnityEngine;

[CreateAssetMenu(fileName = "OreData", menuName = "MiningGame/Ore")]
public class OreData : ScriptableObject
{
    public string oreName; // 鉱石の名前
    public Sprite oreSprite; // 鉱石のスプライト
    public int pointValue; // クリック時のポイント値
    public int count; // プレイヤーが取得した数
}
  1. 鉱石データを設定
    • 作成したOreDataを元に、Assetsフォルダー内で右クリックしてCreate > MiningGame > Oreを選択します。
    • 鉱石の名前、スプライト、ポイント値を設定します。複数の鉱石(例: 石炭、鉄、金)を作成してください。

ステップ 2: 鉱石のUIを追加

  1. Canvasに新しいUI要素を追加
    • Canvas内に新しいGameObjectを作成し、名前をOreInfoPanelとします。
    • 子要素として以下を追加:
      • OreInfo (GameObject): 各鉱石情報を表示するテンプレート。子要素としてImageTextを配置します。
        • Image: 鉱石のスプライトを表示。
        • Text: 鉱石の取得数を表示。
  2. OreInfoのレイアウト設定
    • Vertical Layout GroupOreInfoPanelに追加して、鉱石情報を縦に並べます。
    • OreInfoImageTextを整えて見やすく配置します。

ステップ 3: スクリプトの変更

新しいスクリプトを作成: OreManager

鉱石の種類を管理するスクリプトを作成します。

  1. Assetsで新しいスクリプトを作成し、OreManager.csと名付ける
  2. 以下の内容で実装する
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class OreManager : MonoBehaviour
{
    public OreData[] ores; // 鉱石のリスト
    public GameObject oreInfoPrefab; // 鉱石情報UIのプレハブ
    public Transform oreInfoPanel; // 鉱石情報を表示するパネル

    private Dictionary<OreData, GameObject> oreInfoUIMap = new Dictionary<OreData, GameObject>();

    private void Start()
    {
        // UIを初期化
        foreach (var ore in ores)
        {
            var oreInfo = Instantiate(oreInfoPrefab, oreInfoPanel);
            oreInfo.GetComponentInChildren<Image>().sprite = ore.oreSprite;
            oreInfo.GetComponentInChildren<Text>().text = $"{ore.oreName}: {ore.count}";
            oreInfoUIMap.Add(ore, oreInfo);
        }
    }

    public void AddOre(OreData ore)
    {
        ore.count++;
        UpdateOreUI(ore);
    }

    private void UpdateOreUI(OreData ore)
    {
        if (oreInfoUIMap.TryGetValue(ore, out var oreInfo))
        {
            oreInfo.GetComponentInChildren<Text>().text = $"{ore.oreName}: {ore.count}";
        }
    }
}

ステップ 4: 必要なスクリプトを修正

「OreController」を修正します。 鉱石クリック時に特定のOreDataを加算する機能を追加します。


「OreController」の修正内容 次の修正が必要ですか?必要であれば現状のスクリプトを送ってください。

OreControllerスクリプトの修正内容を教えてください。
現状のスクリプトを添付します。
スクリプトは修正箇所だけではなく、そのまま置き換えられるように全体を書いてください。

「現状のOreController」

<現状のOreController.csの中身を貼り付ける>
ChatGPTの回答例

以下に「OreController」スクリプトを修正した完全版を示します。このスクリプトは、鉱石の種類をOreDataで管理し、取得時にその種類に基づいてカウントを増加させるように変更されています。

修正内容:

  • OreData を参照する仕組みを追加。
  • 鉱石取得時に OreManager.AddOre() を呼び出し、鉱石ごとのカウントを更新。
  • ShowOrePopup() でポップアップのテキストやスプライトを更新。

修正版「OreController」

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

public class OreController : MonoBehaviour
{
    public GameManager gameManager; // GameManagerの参照
    public PickaxeManager pickaxeManager; // PickaxeManagerの参照
    public OreManager oreManager; // OreManagerの参照
    public ParticleSystem oreEffectPrefab; // 破片エフェクトのPrefabを登録
    public GameObject orePopupPrefab; // 鉱石ポップアップ用のPrefab
    private ClickEffect clickEffect;
    private Camera mainCamera; // カメラの参照
    private AudioSource audioSource; // 効果音再生用のAudioSource

    [SerializeField] private List<OreData> oreList; // 鉱石のリスト

    private void Start()
    {
        clickEffect = GetComponent<ClickEffect>();
        audioSource = GetComponent<AudioSource>(); // AudioSourceの取得

        if (audioSource == null)
        {
            Debug.LogError("AudioSource component not found on the ore object!");
        }

        // 各コンポーネントとオブジェクトのエラーチェック
        if (clickEffect == null)
        {
            Debug.LogError("ClickEffect component not found on the ore object!");
        }

        if (gameManager == null)
        {
            Debug.LogError("GameManager reference is not set in the Inspector!");
        }

        if (pickaxeManager == null)
        {
            Debug.LogError("PickaxeManager reference is not set in the Inspector!");
        }

        if (oreManager == null)
        {
            Debug.LogError("OreManager reference is not set in the Inspector!");
        }

        if (oreEffectPrefab == null)
        {
            Debug.LogError("OreEffectPrefab is not assigned in the Inspector!");
        }

        if (orePopupPrefab == null)
        {
            Debug.LogError("OrePopupPrefab is not assigned in the Inspector!");
        }

        mainCamera = Camera.main;

        if (oreList == null || oreList.Count == 0)
        {
            Debug.LogError("Ore list is empty! Please assign ores in the Inspector.");
        }
    }

    private void OnMouseDown()
    {
        Debug.Log("Ore clicked!");

        // 効果音を再生
        if (audioSource != null)
        {
            audioSource.Play();
        }

        // ランダムな鉱石を取得
        OreData selectedOre = GetRandomOre();

        if (selectedOre != null)
        {
            int miningPower = pickaxeManager.GetMiningPower(); // ピッケルの採掘力を取得
            int pointsToAdd = selectedOre.pointValue * miningPower;

            // スコアと鉱石カウントを更新
            gameManager.AddPoints(pointsToAdd);
            oreManager.AddOre(selectedOre);

            Debug.Log($"Obtained {selectedOre.oreName} worth {pointsToAdd} points!");
            ShowOrePopup(selectedOre, pointsToAdd);
        }
        else
        {
            Debug.LogError("Failed to obtain an ore!");
        }

        if (clickEffect != null)
        {
            clickEffect.Play();
        }
        else
        {
            Debug.LogError("ClickEffect is null!");
        }

        PlayOreEffectAtClickPosition();
    }

    private OreData GetRandomOre()
    {
        float randomValue = Random.value;
        float cumulativeProbability = 0;

        foreach (OreData ore in oreList)
        {
            cumulativeProbability += ore.pointValue; // 各鉱石のポイント値を確率として利用
            if (randomValue <= cumulativeProbability)
            {
                return ore;
            }
        }
        return null;
    }

    private void PlayOreEffectAtClickPosition()
    {
        if (oreEffectPrefab != null)
        {
            Vector3 clickPosition = mainCamera.ScreenToWorldPoint(Input.mousePosition);
            clickPosition.z = 0;

            ParticleSystem effect = Instantiate(oreEffectPrefab, clickPosition, Quaternion.identity);
            effect.Play();
            Destroy(effect.gameObject, effect.main.duration);
        }
        else
        {
            Debug.LogError("OreEffectPrefab is null!");
        }
    }

    private void ShowOrePopup(OreData selectedOre, int points)
    {
        Vector3 clickPosition = mainCamera.ScreenToWorldPoint(Input.mousePosition);
        clickPosition.z = 0;

        GameObject popup = Instantiate(orePopupPrefab, clickPosition, Quaternion.identity);

        // スプライトとポイントを表示
        SpriteRenderer spriteRenderer = popup.GetComponent<SpriteRenderer>();
        if (spriteRenderer != null && selectedOre.oreSprite != null)
        {
            spriteRenderer.sprite = selectedOre.oreSprite;
        }

        TextMesh textMesh = popup.GetComponentInChildren<TextMesh>();
        if (textMesh != null)
        {
            textMesh.text = $"+{points}";
        }

        StartCoroutine(AnimatePopup(popup));
        Destroy(popup, 0.75f);
    }

    private IEnumerator AnimatePopup(GameObject popup)
    {
        float duration = 0.5f;
        float elapsedTime = 0;
        Vector3 originalPosition = popup.transform.position;

        while (elapsedTime < duration)
        {
            float yOffset = Mathf.Sin(elapsedTime / duration * Mathf.PI) * 0.5f;
            popup.transform.position = originalPosition + new Vector3(0, yOffset, 0);
            elapsedTime += Time.deltaTime;
            yield return null;
        }
    }
}

変更点概要

  1. OreDataの利用:
    • 鉱石ごとのデータを参照。
  2. OreManager.AddOre呼び出し:
    • 各鉱石のカウントを増加。
  3. ポップアップ表示の改良:
    • 獲得ポイントをテキストで表示。
  4. 確率の修正:
    • pointValueを使用して確率を計算。

このスクリプトをそのまま「OreController.cs」に置き換えてください。

インベントリUIのイメージを作ってもらう

これから作るインベントリUIは、どのような感じになるのか、イメージ画像を作ってもらいます

先ほどあなたが提案してくれた下記の、獲得した鉱石の管理UIの設定手順について。
この手順で作成できるゲームUIのイメージを画像にしてください。

### **ステップ 2: 鉱石のUIを追加**

1. **Canvasに新しいUI要素を追加**
   - **Canvas**内に新しい`GameObject`を作成し、名前を**OreInfoPanel**とします。
   - 子要素として以下を追加:
     - **OreInfo (GameObject)**: 各鉱石情報を表示するテンプレート。子要素として`Image`と`Text`を配置します。
       - **Image**: 鉱石のスプライトを表示。
       - **Text**: 鉱石の取得数を表示。

2. **OreInfoのレイアウト設定**
   - `Vertical Layout Group`を`OreInfoPanel`に追加して、鉱石情報を縦に並べます。
   - `OreInfo`の`Image`と`Text`を整えて見やすく配置します。
ChatGPTの回答例

こちらが鉱石管理パネルのUIデザインイメージです。このUIは、鉱石の種類ごとの収集状況を一目で確認できるように設計されています。鉱石アイコンと取得数が縦に並び、シンプルで直感的なレイアウトとなっています。このイメージを参考に、実際のUI作成を進めてください!

OreManagerのアタッチ先を聞く

ステップ3で作成した OreManager.csスクリプトはどこにアタッチして使うのか、指示が無いので聞いてみます。

OreManager.csはどのオブジェクトにアタッチすれば良いですか?
「現状のヒエラルキー」

<現状のヒエラルキーを貼り付け>
ChatGPTの回答例

OreManager.csは、鉱石データを管理し、UI(OreInfoPanel)に情報を反映する役割を持ちます。このスクリプトはGameManagerオブジェクトにアタッチするのが適切です。

理由

  • GameManagerはゲーム全体のロジックを統括する役割を持ち、鉱石情報管理はその一部に該当します。
  • 鉱石管理に関するロジックを分散させないため、関連するスクリプト(GameManagerPickaxeManager)と同じオブジェクトに配置することで、管理が容易になります。

修正後のヒエラルキー

Main Camera
  - Transform
  - Camera
  - AudioListener
  - UniversalAdditionalCameraData
  - Physics2DRaycaster
Global Light 2D
  - Transform
  - Light2D
Ore
  - Transform
  - SpriteRenderer
  - OreController
  - BoxCollider2D
  - ClickEffect
  - AudioSource
  - InspectorSettingsLogger
GameManager
  - Transform
  - GameManager
  - PickaxeManager
  - OreManager (*新しくアタッチされたスクリプト*)
Canvas
  - RectTransform
  - Canvas
  - CanvasScaler
  - GraphicRaycaster
  OreInfoPanel
    - RectTransform
    - CanvasRenderer
    - Image
    - GridLayoutGroup
    OreInfo
      - RectTransform
      - CanvasRenderer
      - Image
      Image
        - RectTransform
        - CanvasRenderer
        - Image
      Text
        - RectTransform
        - CanvasRenderer
        - Text
  ScoreText
    - RectTransform
    - CanvasRenderer
    - Text
  UpgradeButton
    - RectTransform
    - CanvasRenderer
    - Image
    - Button
    Text (Legacy)
      - RectTransform
      - CanvasRenderer
      - Text
  ProgressBar
    - RectTransform
    - Slider
    Background
      - RectTransform
      - CanvasRenderer
      - Image
    Fill Area
      - RectTransform
      Fill
        - RectTransform
        - CanvasRenderer
        - Image
EventSystem
  - Transform
  - EventSystem
  - StandaloneInputModule
  - BaseInput
BackGround
  - Transform
  - SpriteRenderer

OreManager.csを**GameManagerオブジェクト**にアタッチしてください。その後、スクリプトの必要なパブリックフィールドをインスペクターから設定する準備を進めます。

画像入れ替え不具合の修正

ゲームを実行して確認すると、画像の設定がおかしくなっているので(下図参照)

修正方法をChatGPTに聞いてみます

Unityでゲームを実行した時の、OreInfoの鉱石画像の設定が上手くできていません。
OreInfoの子要素であるOreImageに鉱石画像を設定して欲しいのですが、それができていないようです
おそらくこの処理をしていると思われる「OreManager.cs」の中身を示します、もしこのスクリプトが関係無い時は言ってください。
OreInfoのヒエラルキーも添付しておきます。

「OreManager」

<現状のOreManager.csの中身を貼り付ける>


「OreInfoのヒエラルキー」
    OreInfo(Clone)
      - RectTransform
      - CanvasRenderer
      - Image
      OreImage
        - RectTransform
        - CanvasRenderer
        - Image
      OreText
        - RectTransform
        - CanvasRenderer
        - Text
ChatGPTの回答例

提供していただいた「OreManager.cs」と「OreInfo」のヒエラルキーから判断すると、鉱石画像の設定が上手くいかない理由は、oreInfo.GetComponentInChildren<Image>()が、OreInfo(Clone)直下のImage(背景用など)を取得しており、子要素のOreImageではない可能性が高いです。

解決策

OreInfo(Clone)の中にある**OreImage**を正しく取得するために、Transform.Find()を使用して特定の子要素を探し、そのImageコンポーネントにアクセスするようにスクリプトを修正します。

以下は修正した「OreManager.cs」のコード全体です。


修正版「OreManager.cs」

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

public class OreManager : MonoBehaviour
{
    public OreData[] ores; // 鉱石のリスト
    public GameObject oreInfoPrefab; // 鉱石情報UIのプレハブ
    public Transform oreInfoPanel; // 鉱石情報を表示するパネル

    private Dictionary<OreData, GameObject> oreInfoUIMap = new Dictionary<OreData, GameObject>();

    private void Start()
    {
        // UIを初期化
        foreach (var ore in ores)
        {
            var oreInfo = Instantiate(oreInfoPrefab, oreInfoPanel);
            
            // 鉱石画像 (OreImage) を設定
            Transform oreImageTransform = oreInfo.transform.Find("OreImage");
            if (oreImageTransform != null)
            {
                var oreImage = oreImageTransform.GetComponent<Image>();
                if (oreImage != null)
                {
                    oreImage.sprite = ore.oreSprite;
                }
                else
                {
                    Debug.LogError("OreImage is missing an Image component!");
                }
            }
            else
            {
                Debug.LogError("OreImage child not found in OreInfo prefab!");
            }

            // 鉱石名と取得数 (OreText) を設定
            Transform oreTextTransform = oreInfo.transform.Find("OreText");
            if (oreTextTransform != null)
            {
                var oreText = oreTextTransform.GetComponent<Text>();
                if (oreText != null)
                {
                    oreText.text = $"{ore.oreName}: {ore.count}";
                }
                else
                {
                    Debug.LogError("OreText is missing a Text component!");
                }
            }
            else
            {
                Debug.LogError("OreText child not found in OreInfo prefab!");
            }

            // 辞書に登録
            oreInfoUIMap.Add(ore, oreInfo);
        }
    }

    public void AddOre(OreData ore)
    {
        ore.count++;
        UpdateOreUI(ore);
    }

    private void UpdateOreUI(OreData ore)
    {
        if (oreInfoUIMap.TryGetValue(ore, out var oreInfo))
        {
            Transform oreTextTransform = oreInfo.transform.Find("OreText");
            if (oreTextTransform != null)
            {
                var oreText = oreTextTransform.GetComponent<Text>();
                if (oreText != null)
                {
                    oreText.text = $"{ore.oreName}: {ore.count}";
                }
            }
        }
    }
}

修正のポイント

  1. OreImageへのアクセス
    • oreInfo.transform.Find("OreImage")で子要素OreImageを取得。
    • Imageコンポーネントにore.oreSpriteを正しく設定。
  2. OreTextへのアクセス
    • oreInfo.transform.Find("OreText")で子要素OreTextを取得。
    • Textコンポーネントに鉱石名とカウントを表示。
  3. エラーハンドリング
    • Find()で子要素が見つからなかった場合、エラーメッセージをログに出力。
    • 必要なコンポーネントがない場合も同様にエラーメッセージを出力。

修正後の動作確認

  1. Unityエディターでゲームを実行。
  2. OreInfoPanel内に生成される各OreInfoに、正しい鉱石画像(OreImage)が設定されていることを確認。
  3. コンソールにエラーが出力されないことを確認。

これで問題が解決するはずです。試してみてください!

鉱石クリック時のエラー修正

鉱石をクリックするとエラーが出たので、修正方法を聞いていきます。

鉱石をクリックしたらエラーが出ました。
エラー文を貼りますので解決方法を教えてください。
また、このエラーとは関係無いかもしれませんが、OreManagerのOresと、OreControllerのOreListが重複しているように思っています。
現状OreManagerのOresにはOreDataをセットしていますが、OreControllerのOreListにはOreDataをセットしていません。
これらが関係しているか、これらのあるべき状態も確認してください。

「エラー文」
NullReferenceException: Object reference not set to an instance of an object
OreController.GetRandomOre () (at Assets/Scripts/OreController.cs:116)
OreController.OnMouseDown () (at Assets/Scripts/OreController.cs:78)
UnityEngine.SendMouseEvents:DoSendMouseEvents(Int32)

「エラーが出ている箇所周辺のコード」
        foreach (OreData ore in oreList)
        {
            cumulativeProbability += ore.pointValue; // 各鉱石のポイント値を確率として利用
            if (randomValue <= cumulativeProbability)
            {
                return ore;
            }
        }

「OreController」

<現状のOreController.csの中身を貼り付ける>


「OreManager」

<現状のOreManager.csの中身を貼り付ける>
ChatGPTの回答例

問題の分析

エラーの原因として以下の点が考えられます。

  1. NullReferenceException
    OreController.GetRandomOre()oreListnullまたは空の状態の可能性があります。
    • oreListSerializeFieldでインスペクターから設定できるようになっていますが、値が設定されていない場合、このエラーが発生します。
  2. OreManageroresOreControlleroreListの重複
    OreManagerOreControllerが両方とも鉱石のリスト(OreData[]またはList<OreData>)を持っており、役割が曖昧になっている可能性があります。
    • oresOreManagerがUI管理のために使用しています。
    • oreListはランダム選択のためにOreControllerが使用しています。

改善案

  1. エラーの修正:oreListの初期化を確認
    OreController内のoreListが設定されていない場合に備えて、oreListOreManagerから取得するようにします。これによりデータの一元管理が可能になります。
  2. OreManagerOreControllerの役割整理
    • OreManagerは鉱石データ全体(ores)を管理する責任を持つ。
    • OreControllerはクリック対象としての鉱石オブジェクトに集中し、データの取得はOreManagerから行う。

以下に修正版コードを示します。


修正版「OreController.cs」

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

public class OreController : MonoBehaviour
{
    public GameManager gameManager; // GameManagerの参照
    public PickaxeManager pickaxeManager; // PickaxeManagerの参照
    public OreManager oreManager; // OreManagerの参照
    public ParticleSystem oreEffectPrefab; // 破片エフェクトのPrefabを登録
    public GameObject orePopupPrefab; // 鉱石ポップアップ用のPrefab
    private ClickEffect clickEffect;
    private Camera mainCamera; // カメラの参照
    private AudioSource audioSource; // 効果音再生用のAudioSource

    private void Start()
    {
        clickEffect = GetComponent<ClickEffect>();
        audioSource = GetComponent<AudioSource>(); // AudioSourceの取得
        mainCamera = Camera.main;

        // コンポーネントの確認
        if (gameManager == null || pickaxeManager == null || oreManager == null)
        {
            Debug.LogError("必須コンポーネントが設定されていません!");
        }

        if (oreManager.ores == null || oreManager.ores.Length == 0)
        {
            Debug.LogError("OreManagerに鉱石データが設定されていません!");
        }
    }

    private void OnMouseDown()
    {
        Debug.Log("Ore clicked!");

        // 効果音を再生
        if (audioSource != null)
        {
            audioSource.Play();
        }

        // ランダムな鉱石を取得
        OreData selectedOre = GetRandomOre();

        if (selectedOre != null)
        {
            int miningPower = pickaxeManager.GetMiningPower(); // ピッケルの採掘力を取得
            int pointsToAdd = selectedOre.pointValue * miningPower;

            // スコアと鉱石カウントを更新
            gameManager.AddPoints(pointsToAdd);
            oreManager.AddOre(selectedOre);

            Debug.Log($"Obtained {selectedOre.oreName} worth {pointsToAdd} points!");
            ShowOrePopup(selectedOre, pointsToAdd);
        }
        else
        {
            Debug.LogError("Failed to obtain an ore!");
        }

        if (clickEffect != null)
        {
            clickEffect.Play();
        }

        PlayOreEffectAtClickPosition();
    }

    private OreData GetRandomOre()
    {
        float randomValue = Random.value;
        float cumulativeProbability = 0;

        foreach (OreData ore in oreManager.ores)
        {
            cumulativeProbability += ore.pointValue; // 各鉱石のポイント値を確率として利用
            if (randomValue <= cumulativeProbability)
            {
                return ore;
            }
        }
        return null;
    }

    private void PlayOreEffectAtClickPosition()
    {
        if (oreEffectPrefab != null)
        {
            Vector3 clickPosition = mainCamera.ScreenToWorldPoint(Input.mousePosition);
            clickPosition.z = 0;

            ParticleSystem effect = Instantiate(oreEffectPrefab, clickPosition, Quaternion.identity);
            effect.Play();
            Destroy(effect.gameObject, effect.main.duration);
        }
        else
        {
            Debug.LogError("OreEffectPrefab is null!");
        }
    }

    private void ShowOrePopup(OreData selectedOre, int points)
    {
        Vector3 clickPosition = mainCamera.ScreenToWorldPoint(Input.mousePosition);
        clickPosition.z = 0;

        GameObject popup = Instantiate(orePopupPrefab, clickPosition, Quaternion.identity);

        // スプライトとポイントを表示
        SpriteRenderer spriteRenderer = popup.GetComponent<SpriteRenderer>();
        if (spriteRenderer != null && selectedOre.oreSprite != null)
        {
            spriteRenderer.sprite = selectedOre.oreSprite;
        }

        TextMesh textMesh = popup.GetComponentInChildren<TextMesh>();
        if (textMesh != null)
        {
            textMesh.text = $"+{points}";
        }

        StartCoroutine(AnimatePopup(popup));
        Destroy(popup, 0.75f);
    }

    private IEnumerator AnimatePopup(GameObject popup)
    {
        float duration = 0.5f;
        float elapsedTime = 0;
        Vector3 originalPosition = popup.transform.position;

        while (elapsedTime < duration)
        {
            float yOffset = Mathf.Sin(elapsedTime / duration * Mathf.PI) * 0.5f;
            popup.transform.position = originalPosition + new Vector3(0, yOffset, 0);
            elapsedTime += Time.deltaTime;
            yield return null;
        }
    }
}

修正のポイント

  1. OreManageroresを直接使用
    • OreControlleroreListを削除し、OreManageroresを使用することでデータを一元管理。
  2. エラーチェックの追加
    • OreManagerが正しく設定されているか確認。
    • oresが空の場合にエラーを表示。
  3. 役割の整理
    • OreManagerは鉱石データの管理。
    • OreControllerはクリック処理を担当。

確認手順

  1. Unityエディターでスクリプトを保存後、OreControllerのインスペクターに必要な参照(GameManagerPickaxeManagerOreManagerなど)が設定されているか確認。
  2. ゲームを実行し、鉱石クリック時に正しくポイントが加算されることを確認。
  3. エラーが発生しないことを確認。

鉱石の確率取得を修正

クリック時のエラーは解消しましたが

クリックしても「石」しか取得できないので、確率で他の鉱石も取得できるようにしてもらいます。

エラーは解消しました。
今度は、鉱石を何度クリックしても獲得できるのは「石」の鉱石だけで、他の鉱石が獲得できません。
以前確率で獲得できる鉱石の種類を変える処理をしていたのが、働いていないと思います。
現状のヒエラルキーと、スクリプト間の依存関係の情報を示しますので、この処理に関係するスクリプトを教えてください。
まだ変更後のスクリプトは書かないでください、関係するスクリプトを教えてもらったら
私が現状のスクリプトをあなたに教えます。

「ヒエラルキー」

<現状のヒエラルキーを貼り付け>


「スクリプト間の依存関係」

<現状のスクリプト間の依存関係を貼り付け>
ChatGPTの回答例

提供していただいた情報に基づき、鉱石の種類を確率的に獲得する処理に関係するスクリプトは以下の通りです:

1. OreController

  • 関連性: プレイヤーが鉱石をクリックした際にランダムな鉱石を選択する役割を持つ GetRandomOre メソッドが含まれています。
  • 依存関係: OreManager を参照しており、設定された鉱石リスト (oreList) を用いてランダム選択を行います。

2. OreManager

  • 関連性: 全ての鉱石データ (ores) を管理し、各鉱石の属性(名前、スプライト、確率、ポイント値など)を保持します。
  • 依存関係: 各鉱石のデータは OreData クラスで定義されています。また、鉱石をクリックした結果をUIに反映する部分(AddOre メソッド)があります。

3. OreData (スクリプトがあれば)

  • 関連性: 各鉱石のデータ構造を定義します(例: 名前、確率、ポイント値、スプライト、カウントなど)。
  • 依存関係: OreManagerOreControllerOreData の情報を利用します。

これらのスクリプトを確認し、以下のポイントをチェックしてください:

  1. OreController 内の oreList が適切に初期化されているか。
    • OreManager から oreList にデータが正しく渡されている必要があります。
  2. OreManagerores 配列に設定されている各鉱石の確率やポイント値が正しいか。
    • 確率値が適切でない場合、選ばれる鉱石が偏る可能性があります。
  3. OreData クラス(または構造体)に確率やポイント値が正しく定義されているか。

次のステップとして、それぞれのスクリプト(OreController, OreManager, OreData)の内容を教えていただければ、具体的な原因の特定と修正方法をお伝えできます。

OreController、OreManager、OreDataのスクリプトの内容を下記に示します。

「OreController」

<現状のOreController.csの中身を貼り付ける>


「OreManager」

<現状のOreManager.csの中身を貼り付ける>


「OreData」

<現状のOreData.csの中身を貼り付ける>
ChatGPTの回答例

提供いただいたスクリプトを確認したところ、鉱石の種類が確率的に選ばれない原因として以下の問題が考えられます:

問題の可能性

  1. OreControllerGetRandomOre メソッドの実装:
    • cumulativeProbability += ore.pointValue で累積確率を計算していますが、pointValue(鉱石のポイント値)を確率として使用するのは不適切です。確率とポイント値は異なる性質を持つため、このままだと意図した確率分布になりません。
    • 各鉱石の出現確率を正確に設定する必要がありますが、その情報が OreData に含まれていません。
  2. OreData に確率値がない:
    • OreData に「出現確率」を示すプロパティ(例: float spawnProbability)が存在しないため、確率の割り当てができていません。

解決方法

以下の手順で修正を提案します。

1. OreData に出現確率を追加

OreData に新しいフィールドを追加して、鉱石ごとの出現確率を設定します。

public float spawnProbability; // 出現確率 (0.0 ~ 1.0)

2. GetRandomOre メソッドの修正

OreControllerGetRandomOre メソッドを修正して、鉱石の出現確率を適切に考慮するようにします。

private OreData GetRandomOre()
{
    float randomValue = Random.value; // 0.0 ~ 1.0 のランダム値を生成
    float cumulativeProbability = 0;

    foreach (OreData ore in oreManager.ores)
    {
        cumulativeProbability += ore.spawnProbability; // 累積確率を計算
        if (randomValue <= cumulativeProbability)
        {
            return ore;
        }
    }

    return null; // 確率の範囲外の場合
}

3. 出現確率の設定

OreData のインスペクター上で、鉱石ごとの spawnProbability を設定します。

  • 合計が 1.0 になるように設定してください(例: 石: 0.6, 銀: 0.3, 金: 0.1)。

これらの修正を実施すると、意図した確率に基づいて鉱石が選ばれるようになるはずです。
次に進む準備ができたら教えてください!

UIテキストの表示を変更

問題は解決しました。
次は、鉱石獲得数の表示テキストを少し修正したいです。
下記のように、現状は1行で表示されているのを2行に分けて表示させたいです
この表示をしていると思われるコードも貼り付けますので、2行で表示されるようにしてください。

「現状」
鉱石名: 獲得数

「修正後」
鉱石名
獲得数

「関係すると思われるコード」
oreText.text = $"{ore.oreName}: {ore.count}";
ChatGPTの回答例

OreText のテキストを2行に分けて表示するには、文字列に改行を追加する必要があります。C# では、\n を使用して改行を表現できます。

修正後のコード例は以下の通りです:

oreText.text = $"{ore.oreName}\n{ore.count}";

修正ポイント

  1. 改行コード:
    • \n を追加して「鉱石名」と「獲得数」を分けます。
  2. 見た目の調整:
    • UI テキストの「Alignment」設定を確認し、必要に応じて中央揃え(Center)に変更してください。
      • Unity エディタで、該当のテキストコンポーネントを選択し、Paragraph の「Horizontal Alignment」や「Vertical Alignment」を設定します。

実装例

OreManager スクリプトの UpdateOreUI メソッドを以下のように修正します:

private void UpdateOreUI(OreData ore)
{
    if (oreInfoUIMap.TryGetValue(ore, out var oreInfo))
    {
        Transform oreTextTransform = oreInfo.transform.Find("OreText");
        if (oreTextTransform != null)
        {
            var oreText = oreTextTransform.GetComponent<Text>();
            if (oreText != null)
            {
                // 改行を追加して2行表示に変更
                oreText.text = $"{ore.oreName}\n{ore.count}";
            }
        }
    }
}

この変更により、表示が「2行表示」に切り替わります。


Unity 内での確認事項

  1. テキストエリアの大きさ:
    • RectTransform の高さを十分確保してください。2行目が見切れないようにする必要があります。
  2. フォントサイズ:
    • 必要に応じてフォントサイズを調整し、文字が読みやすくなるようにします。

修正を試して、問題があれば教えてください!

コメント

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