再生すると2行3列でボタンを配置し、それぞれ区別する。
Unityでボードゲームを作りたいというときに使う技術に、
UI・ゲームオブジェクト(game object)を複製するコードを書きます。
リバーシ(オセロ)は、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);
}
}
}
UdemyでUnityを学習
Udemyは、オンデマンド式の学習講座です。
趣味から実務まで使えるおすすめの講座を紹介します。
- 【Unity C# ゲーム開発超入門】7つのミニゲームを作っておぼえる!UnityとC#講座
Unityで頻出の機能を実際のゲーム作りで学習。
何か作りたい、今後の開発で役立てたい初心者におすすめ
- 【Unity C# ゲーム開発初心者レベルアップ】7つのトランプゲームを作っておぼえる!UnityとC#講座
トランプやボードゲーム作りに特化している講座。
テーブルゲームを作りたい人におすすめ。
- 【全行程を網羅!最初に学びたい総合学習】Unityワールド制作講座
Unityワールド制作の全工程を学習できる講座。
RPGを作りたい人におすすめ。
- Unity ゲーム開発:インディーゲームクリエイターが教える C#の基礎からゲームリリースまで【スタジオしまづ】
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に格納します。
参考:「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
ボタンやテキストなどの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)を
使用したデータ操作があります。