【Unity】オブジェクト・プレハブをスクリプトで複製・コピー

当サイトで紹介する商品・サービス等の外部リンクは、アフィリエイト広告を含む場合があります。
スポンサーリンク
本記事を読むと次のようなプログラミングができるようになります

再生すると2行3列でボタンを配置し、それぞれ区別する。

オブジェクト・プレハブを行列で複製

Unityでボードゲームを作りたいというときに使う技術に、
UI・ゲームオブジェクト(game object)を複製するコードを書きます。

リバーシ(オセロ)は、8×8の盤面で石をひっくり返すゲームです。
つまり、同じパネルと石が64個ずつ必要になります。

Hierarchyウィンドウで大量のゲームオブジェクトを作成して、
パネル同士が均等の間隔で縦と横を並べて石がパネルの上になければいけません。

その他にも、将棋やチェスは駒(ポーン)前に1つだけ動けるものや全方向に2つ先まで動くなど変則的な動きをするので、リバーシ以上に複雑な動きをするゲームです。

このように、身近なボードゲームの作成をするだけでも、
UIやゲームオブジェクトを手作業で何個も作成するのは非常に効率が悪いです。

さらに、UIにOnclickなどをアタッチするときに、
数百個もアタッチして紐づけると管理もしづらくなります。

今回は、スクリプトからUIやゲームオブジェクトを複製して、
横一列に複製する方法と縦に何個、横に何個のように行列で配置する方法を紹介します。

本記事は次の人におすすめ
  • UIやゲームオブジェクトの使い方、複製の方法を知りたい。
  • Vecterの使い方を知りたい。
Udemyで学習する
スポンサーリンク

Instantiate関数

Instantiate関数について

ゲーム作りにおいて、同じゲームオブジェクト(素材)を配置するときに複製を使用します。

前述したリバーシの場合、64個の盤面・64個の石。
将棋の場合は盤面81個と駒によって動かし方が異なります。

何十個も素材を生成すると管理も大変になるので、
同じものは1つだけ配置して8行8列の複製をします。

参考:「Object-Instantiate – Unity スクリプトリファレンス

ソースコード

Instantiate関数を使ってスペースキーを押すと、
プレハブが画面上にランダムで配置されるようにします。

using UnityEngine;

public class Insta : MonoBehaviour
{
    public GameObject object_sq; // 生成するオブジェクトのプレハブ
    public Vector3 AreaMin = new Vector3(-10.0f, -3.0f, 0.0f); // 最小値
    public Vector3 AreaMax = new Vector3(10.0f, 3.0f, 0.0f);   // 最大値
    private int press_count;

    void Update()
    {
        // スペースキーを押すと新しいオブジェクトを生成
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // ランダムな位置を計算
            Vector3 randomPosition = new Vector3(
                Random.Range(AreaMin.x, AreaMax.x),
                Random.Range(AreaMin.y, AreaMax.y),
                Random.Range(AreaMin.z, AreaMax.z)
            );

            // Instantiate関数を使用してオブジェクトを生成
            GameObject newObject = Instantiate(object_sq, randomPosition, transform.rotation);
            press_count++; // スペースキーが押された回数をカウントアップ
            Debug.Log("Space key has been pressed " + press_count + " times.");
            Debug.Log("Random Position: " + randomPosition);
        }
    }
}

UdemyでUnityを学習

Udemyは、オンデマンド式の学習講座です。
趣味から実務まで使えるおすすめの講座を紹介します。

\Unityの学習から収益化の方法はこちらから!/



解説

object_sq は、パブリック変数で生成するオブジェクトのプレハブを保持します。
Unityのインスペクターで設定できます。

AreaMin・AreaMaxは、パブリック変数でランダムな位置を生成する範囲を指定します。
AreaMinは最小値、AreaMaxは最大値です。
デフォルトは、X座標が-10から10、Y座標が-3から3、Z座標が0に設定しています。

press_countは、スペースキーが押された回数をカウントするためのプライベートな整数変数です。

Unity(C#)は、public・privateのようなアクセス修飾子を変数で定義します。
その他にも、JavaやC++でもアクセス修飾子を使用します。

アクセス修飾子の使い分けについて解説しています。

スペースキーを押すとRandom.Rangeを使用して、
指定した範囲内でランダムな位置(X、Y、Z座標)を計算し、randomPositionに格納します。

参考:「Random-Range – Unity スクリプトリファレンス

Instantiate関数を使用して、object_sqをランダムな位置に生成します。

ランダムな配置、順列について、解説しています。

Unityでゲームオブジェクトをランダムに配置するには、
「Vector」と「Random.Range」、「Instantiate関数」を使用して計算する必要があります。

Vector3 randomPosition = new Vector3(Random.Range(min,max),0,0)

変化させない方向(y軸・Z軸)は、0と置いてください。
格納した変数をInstantiate関数で生成します。

Vectorは、Unityでプレハブを移動させたいときに使います。

プレハブ・ゲームオブジェクトを移動させる方法を紹介しています。

space keyを押した数だけ、press_countを加算していきます。
カウントアップした回数は、デバッグ情報としてconsoleウィンドウに表示します。

文字列と数字をリアルタイムでカウントアップする方法を紹介しています・

画面上に数字を表示するときは、intやfloat型の数字ではなくて、stringで文字に変換します。

実演

Hierarchyウィンドウにスクリプトをアタッチしてください。
また、複製するプレハブをInspectorウィンドウに表示されているスクリプトにアタッチしてください。

ゲームオブジェクトをアタッチ

再生ボタンを押してスペースキーを押します。
押下した回数、同じプレハブが表示されます。

ランダムで配置

Transform-SetParent

Transform-SetParentについて

ボタンやテキストなどのUIを表示するためには、
Hierarchyウィンドウで「Canvas」の配下になければいけません。

もしも、UIとCanvasの親子関係を外すと、画面上に表示されなくなります。

UIとCanvasの親子関係

このように、ゲームオブジェクトを表示させるには、親子関係が必要になります。

複製したプレハブをコピー元と同じ場所(Canvasの配下)に配置するために
Transform-SetParentを使用します。

参考:「Transform-SetParent – Unity スクリプトリファレンス

ラムダ式

ラムダ式について

ラムダ式は、プログラミング言語に使用される概念で、簡潔に関数を定義するために使われます。
Unity(C#)だけではなく、RubyやJavaScript、C++などで使用されます。

一般的に、ラムダ式の記法は以下のようになります。

  • C#
(parameter) => {expression}
  • Ruby
-> (parameter) {expression}
  • JavaScript
(parameter) => {expression}

JavaScriptは、Webサイトに動きを付けるときに使います。
アロ―関数を使ったプログラミングの一例は以下の記事をご覧ください。

アロー関数で横スクロールを紹介しています。

具体的な使用例は、イベントハンドリングとLINQ(Language Integrated Query)を使用したデータ操作があります。

イベントハンドリング

ボタンクリック、コライダー衝突などゲームオブジェクトのイベントをハンドリングする際にラムダ式が便利です。

ボタンがクリックされた時の処理は、Inspectorウィンドウの「Onclick」にアタッチしなければいけません。
この操作を省略するのがラムダ式になります。

using UnityEngine;
using UnityEngine.UI;

public class Btn_Click : MonoBehaviour
{
    private void Start()
    {
        Button button = GetComponent<Button>();
        button.onClick.AddListener(() => {
            // ボタンがクリックされた時の処理
            Debug.Log("ボタンがクリックされました");
        });
    }
}

スクリプトのアタッチは、クリックするボタンにしてください。
Onclickには、何もしなくてよいです。

スクリプトのアタッチ

再生ボタンを押して、UIボタンを押すと、Consoleウィンドウに出力されます。

イベントハンドリングについて

LINQ

Unity内でデータを処理する場合、ラムダ式を使って、
データをフィルタリング、ソート、変換することができます。

ラムダ式を使って配列の数字の中で偶数だけ出力します。

using UnityEngine;
using System.Linq;

public class Data : MonoBehaviour
{
    private void Start()
    {
        int[] numbers = { 1, 2, 3, 4, 5 };

        // ラムダ式を使って偶数だけを抽出
        var evenNumbers = numbers.Where(x => x % 2 == 0);

        foreach (var num in evenNumbers)
        {
            Debug.Log(num);
        }
    }
}

スクリプトをHierarchyウィンドウにアタッチして再生ボタンを押してください。
Consoleウィンドウに偶数だけ出力されます。

LINQの実行

アロー式との違いは

ラムダ式の他にもアロー式やアロー演算子、アロー関数など
「アロー」という言葉も一緒に出てきます。

基本的にはラムダとアローは一緒と考えてください。

横一列でボタンを配置

横一列に配置について

初めに、横に3つのゲームオブジェクト(UIボタン)を配置するコードを作成します。

作成したUIボタンを押すと、何番目のボタンがクリックされたかconsoleウィンドウで表示します。

ソースコード

using UnityEngine;
using UnityEngine.UI;

public class column : MonoBehaviour
{
    int columnCount = 3;        // 列数
    public Button button;       // 複製元のボタン
    Button newButton;           // 複製したボタン

    void Start()
    {
        // 列数を元に、複製するボタンをグリッド状に配置する
        for (int j = 0; j < columnCount; j++)
        {
            // 複製するボタンのインスタンスを作成する
            newButton = Instantiate(button);

            // 複製したボタンの親を元のボタンと同じにする
            newButton.transform.SetParent(button.transform.parent);

            // ボタンをRectTransform形式で配置する
            float xPos = button.transform.position.x + (button.GetComponent<RectTransform>().rect.width + 10f) * j;
            float yPos = button.transform.position.y;
            newButton.transform.position = new Vector3(xPos, yPos, button.transform.position.z);

            // ボタンそれぞれに名前を付ける
            newButton.gameObject.name = button.gameObject.name + " (Copy " + (j + 1) + ")";

            // クリックされたボタンの番号をログに表示するリスナーを追加する
            int buttonNumber = j + 1;
            newButton.onClick.AddListener(delegate { Click(buttonNumber); });
        }
    }

    // ボタンがクリックされた時の処理
    public void Click(int buttonNumber)
    {
        Debug.Log("Button " + buttonNumber + " was clicked.");
    }
}

解説

初めに、名前空間を定義します。

UIボタンを使用するので、using UnityEngine.UI;を使用します。
詳細:「Namespace UnityEngine.UI | Unity UI | 1.0.0

using UnityEngine;
using UnityEngine.UI;

「columnCount」は、int型の変数で列数を指定します。
「button」は、複製元のボタンを指定するためのパブリックな変数です。
「newButton」は、複製したボタンの一時的な参照を保持するための変数です。

int columnCount = 3;        // 列数
public Button button;       // 複製元のボタン
Button newButton;           // 複製したボタン

Startメソッドは、for文で列数の数だけ加算します。加算数を0から始めているので、小なりイコール「<=」にすると一つ多く生成されます。

「newButton = Instantiate(button); 」は、複製元のボタンを複製するためのコードです。
新しいボタンは、newButton 変数に格納されます。

「newButton.transform.SetParent(button.transform.parent);」は、複製したボタンの親要素
元のボタンと同じ親要素に設定します。
これにより、ボタンは同じ親要素内に配置されます。

「XPos」・「YPos」は、float型の変数で、ボタンをグリッド状に配置するためのX座標とY座標を計算します。ボタンは元のボタンの右に並び、間隔は10ピクセルです。

「newButton.gameObject.name = button.gameObject.name + ” (Copy ” + (j + 1) + “)”;」 は、複製したボタンに名前を付けます。
名前は元のボタンの名前に “(Copy 1)”、”(Copy 2)” などの番号が付きます。

「newButton.onClick.AddListener(delegate { Click(buttonNumber); }); 」は、複製したボタンがクリックされたときに、Clickメソッドを呼び出すイベントリスナーを追加します。
各ボタンの番号が buttonNumber 変数に設定され、クリック時にログに表示されます。

        // 列数を元に、複製するボタンをグリッド状に配置する
        for (int j = 0; j < columnCount; j++)
        {
            // 複製するボタンのインスタンスを作成する
            newButton = Instantiate(button);

            // 複製したボタンの親を元のボタンと同じにする
            newButton.transform.SetParent(button.transform.parent);

            // ボタンをRectTransform形式で配置する
            float xPos = button.transform.position.x + (button.GetComponent<RectTransform>().rect.width + 10f) * j;
            float yPos = button.transform.position.y;
            newButton.transform.position = new Vector3(xPos, yPos, button.transform.position.z);

            // ボタンそれぞれに名前を付ける
            newButton.gameObject.name = button.gameObject.name + " (Copy " + (j + 1) + ")";

            // クリックされたボタンの番号をログに表示するリスナーを追加する
            int buttonNumber = j + 1;
            newButton.onClick.AddListener(delegate { Click(buttonNumber); });
        }

「public void Click(int buttonNumber)」 メソッドは、
ボタンがクリックされたときに呼び出され、クリックされたボタンの番号をログに表示します。

    // ボタンがクリックされた時の処理
    public void Click(int buttonNumber)
    {
        Debug.Log("Button " + buttonNumber + " was clicked.");
    }

実演

先ほどのスクリプトをHierarchyウィンドウ内のオブジェクトにアタッチします。

再生ボタンを押すと、UIボタンが3つ複製されて表示されます。

横配列の実行

縦横の行列でボタンを配置

縦横の行列について

それでは指定したボタンを複製し、それらをグリッド状に行列で配置します。
縦横に均等に配置するのは、for文で繰り返す構文を2重にすることで作ることができます。

今回の製作は2行3列でボタンを配置します。

ソースコード

using UnityEngine;
using UnityEngine.UI;

public class row_columon : MonoBehaviour
{
    int rowCount = 2;           // 行数
    int columnCount = 3;        // 列数
    int index;                  // ボタン番号
    public Button button;       // 複製元のボタン
    Button newButton;           // 複製したボタン

    void Start()
    {
        // 行列数を元に、複製するボタンをグリッド状に配置する
        for (int i = 0; i < rowCount; i++)
        {
            for (int j = 0; j < columnCount; j++)
            {
                // 複製するボタン番号を計算する
                index = i * columnCount + j;

                // 複製するボタンのインスタンスを作成する
                newButton = Instantiate(button);

                // 複製したボタンの親を元のボタンと同じにする
                newButton.transform.SetParent(button.transform.parent);

                // ボタンをRectTransform形式で配置する
                float xPos = button.transform.position.x + (button.GetComponent<RectTransform>().rect.width + 10f) * j;
                float yPos = button.transform.position.y - (button.GetComponent<RectTransform>().rect.height + 10f) * i;
                newButton.transform.position = new Vector3(xPos, yPos, button.transform.position.z);

                // ボタンそれぞれに名前を付ける
                newButton.gameObject.name = button.gameObject.name + " (Copy " + (index + 1) + ")";

                // クリックされたボタンの番号をログに表示するリスナーを追加する
                int buttonNumber = index + 1;
                newButton.onClick.AddListener(() => Click(buttonNumber));
            }
        }
    }

    // ボタンがクリックされた時の処理
    public void Click(int buttonNumber)
    {
        Debug.Log("Button " + buttonNumber + " was clicked.");
    }
}

解説

名前空間は変更・追加はありません。
追加した変数を定義します。

rowCountは、グリッドに表示するボタンの行数を指定します。
index は、複製される各ボタンに付与する番号を保持する変数です。

int rowCount = 2;           // 行数
int index;                  // ボタン番号

forループを2重に使用して、行数と列数を元にグリッド状にボタンを配置します
i は行番号を、j は列番号を表します。

次に、行番号と列番号を元に、複製するボタンの番号を計算します。
例えば、rowCount が 2、columnCount が 3 の場合、
1行目のボタン番号は 0、1、2。2行目のボタン番号は 3、4、5になります。

// 複製するボタン番号を計算する
index = i * columnCount + j;

buttonNumberは、ボタンの番号をログに表示するint型の変数です。

// クリックされたボタンの番号をログに表示するリスナーを追加する
  int buttonNumber = index + 1;

実演

先ほどと同様ですので、アタッチし直す必要はありません。
再生ボタンを押すと、冒頭のように縦横「2×3」のUIボタンが配置されます。

まとめ

ゲーム作りにおいて、ゲームオブジェクト(素材)の配置には複製をします。

複製には、Instantiate関数を使用します。

また、UIとCanvasの親子関係を外すと、画面上に表示されないように、
複製した素材は、親子関係が必要になります。
複製したプレハブをコピー元と同じ場所(Canvasの配下)に配置するためにTransform-SetParentを使用します。

ラムダ式は、アロー式と同様なことで、
Unityにおいてスクリプトを簡潔に関数を定義するときに使います。

主にイベントハンドリングとLINQ(Language Integrated Query)を
使用したデータ操作があります。

Udemyで学習する

この記事を書いた人

プロフィール

アリッシア

                 

大学4年間で何か胸を張れるスキルを身に着けたくて当サイト運営を始めました。
現在、大学院に進学するか就職するか迷いながら勉強しています。
詳しいプロフィールはこちら

Contact icon

contact

X icon

X

Instagram icon

Instagram

Note icon

Note

スポンサーリンク
Unity
フォローする
タイトルとURLをコピーしました