【Unity】ゲームオブジェクトをスクリプトで複製、行列で配置

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

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

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

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

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

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

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

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

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

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

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);
        }
    }
}

思い通りのゲームが作れない

Unityでゲーム開発しているけど完成しない。
技術的な壁や知識不足が原因で、思い描いたゲームを実現するのは難しいです。

しかし、Udemyは動画で実践的なゲーム開発を解説していて、
購入した講座は再生・停止・スキップなどが可能なオンデマンド形式なので、
専門的な内容を自分のペースで学習できます。

Udemyの特徴
  • プロのエンジニアによる講習が受けられる
  • 自分のペースで学習を進められる
  • オンデマンド形式だから何度でも視聴可能
  • 不満足なコースは視聴していても返金可能返金ポリシー

Unityの機能を網羅したいや作りたいゲームがある人はUdemy学習を取り入れましょう。
数多くある講座の中から特におすすめな講座を3つ紹介します。

初夏のセール開催中!(5月23日まで)
対象のコースが1300円から。

Unityのはじめの一歩としておすすめ。開発例に物理挙動やアニメーションを使用しているので、今後の開発が円滑になる。

トランプを題材にした講座。カードゲームやボードゲーム開発に応用可能

UnityエンジンのインストールやC#の文法に加えて、App StoreとGoogle Playにゲームをリリース方法を解説。

解説

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に格納します。

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

Unityでランダム配置するには?

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

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

変化させない方向(y軸・Z軸)は、0と置いてください。

格納した変数をInstantiate関数で生成します。

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

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

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

キーボード入力だけではなく、UI操作でカウントアップする方法があります。

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

実演

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

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

Transform-SetParent

ボタンやテキストなどのUIを表示するためには、Hierarchyウィンドウで「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ウィンドウに偶数だけ出力されます。

アロー式との違いは

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

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

横一列でボタンを配置

初めに、横に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)を使用したデータ操作があります。

この記事を書いた人

プロフィール

アリッシア

                 

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

Contact icon

contact

X icon

X

Instagram icon

Instagram

Note icon

Note

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