UnityUnityメモ

【Unity】簡単リスト操作!クリックしたオブジェクトを追加・削除する仕組みを作ろう

Unity

1. はじめに

Unityでゲームやシミュレーションを作成する際、オブジェクトの選択や管理を効率よく行う仕組みは非常に重要です。本記事では、クリックしたオブジェクトをリストに追加し、スペースキーでリストの先頭から順番に削除する簡単な仕組みを作る方法を解説します。


このプロジェクトを通じて、以下のスキルを学ぶことができます:

  • C#スクリプトを使ったリスト操作
  • Raycastを使ったオブジェクトのクリック検出
  • キー入力で動作をトリガーする方法

初心者でもわかりやすい手順で進めていきますので、Unityを初めて触る方でも安心して取り組めます!また、今回作成する仕組みはゲームやツール開発の基礎として応用が効くので、しっかりマスターしましょう。


Unityを触ったことがないという方はコチラの記事から見てみてください!



2. シーンの準備

この記事では、Unityのシーンに複数の3Dオブジェクトを配置して、リスト操作の準備を行います。以下の手順を順番に進めてください。


1. 新しいシーンの作成

まずは新しいシーンを用意しましょう。

  1. ファイル(File)メニューから「New Scene」を選択して、新しいシーンを作成します。
  2. 作成したシーンを適切な名前(例: ObjectListScene)で保存してください。

2. 3Dオブジェクトの追加

次に、リストに追加する対象となる3Dオブジェクトをシーンに配置します。

  1. ヒエラルキー(Hierarchy)ウィンドウを右クリックします。
  2. 3D ObjectCube を選び、シーンにキューブを作成します。
  3. 同じ方法で以下のオブジェクトも作成してください:
    • Sphere(球体)
    • Capsule(カプセル)
    • Cylinder(シリンダー)

3. オブジェクトを配置する

次に、作成したオブジェクトを適当な間隔でシーンに並べます。

  1. 各オブジェクトをクリックして、Inspectorウィンドウで位置を調整します。
    • 例として、以下のような位置に配置することをおすすめします:
      • Cube: (0, 0.5, 0)
      • Sphere: (2, 0.5, 0)
      • Capsule: (4, 0.5, 0)
      • Cylinder: (6, 0.5, 0)
  2. 配置の間隔や順番は自由に調整して、見やすいレイアウトにしましょう。

4. カメラとライトの調整

シーン内でオブジェクトが見やすくなるように、カメラとライトを調整します。

  1. **メインカメラ(Main Camera)**を選択し、位置や角度を調整してすべてのオブジェクトが画面に映るようにします。
    • 例: 位置を (3, 3, -10)、回転を (10, 0, 0) に設定。
  2. ライトが適切に当たるように、Directional Lightの角度を調整します。

これでシーンの準備は完了です!次のステップでは、スクリプトを作成してクリックイベントを実装していきます。



3. スクリプトの作成

このセクションでは、新しいスクリプトを作成し、コードを記述して、空のオブジェクトにアタッチする手順を説明します。Unity初心者でもスムーズに進められるように丁寧に解説します。

1. プロジェクトウィンドウを開く

Unityエディターの下部にある「Project」タブを確認します。

2. 新規スクリプトを作成

  • 「Assets」フォルダー内の任意の場所で右クリックします。
  • メニューから「Create」→「C# Script」を選択します。

3. スクリプトに名前を付ける

作成されたスクリプトの名前を「ListControl」に変更します。

  • 注意: 名前の先頭は必ず大文字にしてください。

4.コードを記述する

プロジェクトウィンドウで「ListControl」スクリプトをダブルクリックして開きます。Visual Studioやお使いのコードエディターが起動します。

以下のコードをエディターに入力します。

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

public class ListControl : MonoBehaviour
{
    public List<GameObject> myList = new List<GameObject>();

    void Update()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        // マウス左クリックでオブジェクトをリストに追加
        if (Input.GetMouseButtonDown(0))
        {
            if (Physics.Raycast(ray, out hit))
            {
                myList.Add(hit.collider.gameObject);
            }
        }

        // スペースキーでリスト先頭を削除
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (myList.Count > 0) // リストが空でないかチェック
            {
                myList.RemoveAt(0);
            }
        }
    }
}

5. コードを保存

コードエディターで「Ctrl + S」(Macの場合は「Cmd + S」)を押して変更を保存します。




4.スクリプトをオブジェクトにアタッチする

1. 空のオブジェクトを作成

  • ヒエラルキー(Hierarchy)ウィンドウを右クリックします。
  • メニューから「Create Empty」を選択します。
  • 新しく作成されたオブジェクトの名前を「GameController」に変更します。

2. スクリプトをドラッグ&ドロップ

  • プロジェクトウィンドウから「ListControl」スクリプトをヒエラルキーの「GameController」にドラッグ&ドロップします。
  • 「Inspector」ウィンドウでスクリプトがアタッチされたことを確認してください。

確認ポイント

  • スクリプトのアタッチ確認
    • ヒエラルキーで「GameController」を選択し、「Inspector」ウィンドウで「ListControl」スクリプトが表示されているかチェックします。

これでスクリプトの作成とアタッチが完了です!次は、スクリプトの動作をテストする方法を説明します。



5. テストプレイ

作成したスクリプトと設定を確認するために、Unityのプレイモードでテストプレイを行いましょう。このステップでは、クリックによるオブジェクトのリスト追加と、スペースキーによるリスト削除の動作を検証します。


手順 1: プレイモードを開始する

  1. シーンを保存: 忘れずに現在のシーンを保存してください。
    • メニューバーから File → Save を選択するか、ショートカットキー(Windows: Ctrl+S / Mac: Cmd+S)を使用します。
  2. プレイモードに切り替える:
    • Unityエディターの上部中央にある「Play」ボタン(三角形のアイコン)をクリックして、プレイモードを開始します。

手順 2: オブジェクトをクリックしてリストに追加する

  1. オブジェクトをクリック:
    • シーン内に配置したキューブやスフィアなどのオブジェクトを左クリックしてください。
  2. 動作の確認:
    • コンソールウィンドウでリストの変化を確認します。
      • ヒント: スクリプトに Debug.Log を追加することで、クリック時にリスト内容を表示させると便利です。
        myList.Add(hit.collider.gameObject);
        Debug.Log("Added: " + hit.collider.gameObject.name);

手順 3: スペースキーでリストの先頭を削除する

  1. スペースキーを押す:
    • スペースキーを押してリストからオブジェクトを削除します。
  2. 動作の確認:
    • コンソールウィンドウで、削除されたオブジェクトの情報を表示させるようにスクリプトを調整すると動作確認が簡単です。
      • 例:if (myList.Count > 0) { Debug.Log("Removed: " + myList[0].name); myList.RemoveAt(0); }

手順 4: エラーや問題を確認する

  1. リストが空の場合の動作:
    • リストが空の状態でスペースキーを押してもエラーが出ないか確認します。
    • スクリプトに適切な空チェック(if (myList.Count > 0))を追加している場合、問題なく動作するはずです。
  2. クリックが認識されない場合:
    • カメラがオブジェクトを正しく向いているか、オブジェクトにColliderが設定されているか確認してください。

成功時の動作

  • 左クリックでオブジェクトがリストに追加され、スペースキーを押すとリストの先頭から順番に削除される。
  • コンソールでリストの追加・削除の動作ログが表示される。

以上でテストプレイは完了です。問題なく動作すれば、リスト操作の仕組みが実装できています!



6. 応用のアイデア

この記事で作成した「クリックでオブジェクトをリストに追加し、スペースキーで削除する仕組み」は、さまざまな場面で応用できます。ここではいくつかのアイデアを紹介します!


1. 削除ではなく、別の動作を実装する

リストからオブジェクトを削除する代わりに、オブジェクトにエフェクトを追加したり、色を変えたりすることで、よりインタラクティブな演出が可能です。例えば、以下のコードを使ってリストの先頭のオブジェクトを赤く変える処理を実装できます。

if (Input.GetKeyDown(KeyCode.Space))
{
if (myList.Count > 0)
{
myList[0].GetComponent<Renderer>().material.color = Color.red;
myList.RemoveAt(0);
}
}

2. リストの内容をUIに表示する

現在リストに追加されているオブジェクト名をUI上に表示することで、プレイヤーがどのオブジェクトを操作しているのか視覚的に確認できるようになります。以下のような流れで実装できます。

  1. Canvasを作成して、Text要素を配置します。
  2. スクリプトにusing UnityEngine.UI;を追加し、Text要素への参照を作成します。
  3. Update関数でリストの内容をText要素に反映します。

コード例:

using UnityEngine.UI;

// Textへの参照を追加
public Text listDisplay;

void Update()
{
string displayText = "Objects in List:\n";
foreach (var obj in myList)
{
displayText += obj.name + "\n";
}
listDisplay.text = displayText;
}

3. 特定のオブジェクトだけを追加対象にする

現在のスクリプトでは、クリックしたすべてのオブジェクトがリストに追加されますが、特定のタグを持つオブジェクトだけをリストに追加するように変更できます。以下のコードでタグを判定できます。

if (Physics.Raycast(ray, out hit))
{
if (hit.collider.CompareTag("Clickable"))
{
myList.Add(hit.collider.gameObject);
}
}

これにより、タグが「Clickable」に設定されたオブジェクトだけがリストに追加されます。タグの設定は、Inspectorウィンドウから簡単に行えます。


4. リストの内容を順番にアニメーションさせる

リストに追加されたオブジェクトを順番に移動させることで、動きのある演出ができます。IEnumeratorを使えば、以下のような流れで処理を実行可能です。

IEnumerator AnimateObjects()
{
foreach (var obj in myList)
{
Vector3 targetPosition = obj.transform.position + Vector3.up * 2;
while (Vector3.Distance(obj.transform.position, targetPosition) > 0.01f)
{
obj.transform.position = Vector3.Lerp(obj.transform.position, targetPosition, Time.deltaTime * 2);
yield return null;
}
}
}

これらのアイデアを活用して、今回学んだ仕組みをさまざまなプロジェクトで発展させてみてください!



後に追加された要素から削除するスクリプト

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

public class ListControl : MonoBehaviour
{
    public List<GameObject> myList = new List<GameObject>(); // ゲームオブジェクトのリスト
    private int count; // 要素数をカウント

    void Start()
    {
        count = 0; // 初期化
    }

    void Update()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // マウス位置からレイを飛ばす
        RaycastHit hit;

        // 左クリックでオブジェクトをリストに追加
        if (Input.GetMouseButtonDown(0))
        {
            if (Physics.Raycast(ray, out hit)) // オブジェクトにヒットしたか確認
            {
                myList.Add(hit.collider.gameObject); // リストに追加
                count++;
                Debug.Log($"Added: {hit.collider.gameObject.name}"); // コンソールに追加したオブジェクト名を表示
            }
        }

        // スペースキーで最後の要素を削除
        if (Input.GetKeyDown(KeyCode.Space) && count > 0) // リストが空でないか確認
        {
            count--;
            Debug.Log($"Removed: {myList[count].name}"); // 削除するオブジェクト名を表示
            myList.RemoveAt(count); // リストから削除
        }
    }
}

番号キーで削除するスクリプト

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

public class ListControl : MonoBehaviour
{
    public List<GameObject> myList = new List<GameObject>();

    void Update()
    {
        // カメラからマウス位置に向けたRayを作成
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        // 左クリックでオブジェクトをリストに追加
        if (Input.GetMouseButtonDown(0))
        {
            if (Physics.Raycast(ray, out hit))
            {
                myList.Add(hit.collider.gameObject);
                Debug.Log($"{hit.collider.gameObject.name}をリストに追加しました!");
            }
        }

        // 数字キーで対応するインデックスのオブジェクトを削除
        for (int i = 0; i <= 5; i++)
        {
            if (Input.GetKeyDown(KeyCode.Alpha0 + i))
            {
                if (i < myList.Count) // インデックスがリスト内に収まっているか確認
                {
                    Debug.Log($"{myList[i].name}をリストから削除しました!");
                    myList.RemoveAt(i);
                }
                else
                {
                    Debug.Log($"リストにインデックス{i}の要素がありません!");
                }
            }
        }
    }
}

よくある質問(FAQ)

Q
リストが空の場合にエラーが出ますか?
A

いいえ、今回のスクリプトではリストが空かどうかをif (myList.Count > 0)でチェックしています。この条件によって、リストが空の状態で要素を削除しようとしてもエラーが発生しません。リストの状態を確認するこのような条件文は、安定したプログラムを作成する上で重要です。

Q
Raycastがうまく動作しない場合の対処法は?
A

以下の点を確認してください:

  • カメラにMain Cameraタグが付いているか確認してください。このスクリプトではCamera.mainを使用しており、Main Cameraタグがないと動作しません。
  • オブジェクトにCollider(コライダー)が付いているか確認してください。Raycastはコライダーが付いていないオブジェクトには反応しません。
  • Raycastの距離や方向が正しいか確認してください。必要に応じて、スクリプト内でデバッグ用のログを追加し、Rayがどこに飛んでいるのかを確認すると良いでしょう。
Q
クリックで追加されるオブジェクトを特定の種類に限定するには?
A

オブジェクトを特定の種類に限定したい場合、以下のような条件文を追加できます:

if (Physics.Raycast(ray, out hit))
{
if (hit.collider.gameObject.CompareTag("Selectable"))
{
myList.Add(hit.collider.gameObject);
}
}

このコードでは、タグがSelectableに設定されたオブジェクトだけをリストに追加します。対象とするオブジェクトに事前にタグを設定しておけば簡単に管理できます。タグはオブジェクトのインスペクターウィンドウから設定できます。

おすすめのアセット

「World Building Bundle – 2024 Edition」は、Unityでの3Dワールド構築を効率化するための包括的なツールセットです。このバンドルを活用することで、地形生成からシーンの最適化まで、幅広い作業をスムーズに進められます。

主な特徴:

  • Gaia Pro 2023: 高品質な地形とシーンを自動生成するツールで、直感的な操作が可能です。
  • GeNa Pro: 地形上に道路や川、村などを簡単に配置・生成できるスプラウンツールです。
  • SECTR COMPLETE 2019: シーンのストリーミングと最適化を行い、パフォーマンスを向上させます。

おすすめポイント:

  • 初心者にも優しい設計: 複雑な設定なしで、プロジェクトにすぐに組み込めます。
  • 時間と労力の節約: ゼロからワールドを作成する手間を省き、開発効率を向上させます。
  • プロフェッショナルな仕上がり: 高品質なデザインで、プロジェクトの見栄えを向上させます。