
ごきげんよう、Budding Lab.編集部のゆくすぃです!
ここでは、ゲーム開発に関する補足情報をお伝えしています!
例えば「サンプルゲーム:ランチ診断」の「診断結果」のように、表示に使うUIをテンプレート化して、データベースから必要なデータセットを読み込んで表示したい、といった場合に「Prefab」と「ScriptableObject」を組み合わせると、とても便利です。


そこで、今回は「PrefabとScriptableObjectを使ってカードを表示する(Unity)」を解説します!
Prefabとは
Prefabとは、ゲームオブジェクトの設計図のようなものです。量産したいゲームオブジェクトのテンプレートを一つ作り、それをPrefab化すると、PrefabをHierarchyウインドウやエディター画面にドラッグ&ドロップしたり、スクリプトで呼び出したりしてゲームオブジェクトを量産できるようになります。


Prefabを使うメリットは次の通りです。
- テンプレートを複製するので、同一GameObjectを何度も作る必要がない。
- Prefabに変更をかけると、Prefabを使ったすべてのGameObjectに変更が反映される。
つまり、データセットを読み込ませるUIテンプレートには、Prefabを使うと便利なのです!
ScriptableObjectとは
ScriptableObjectについて、Unity マニュアルでは以下のように説明されています。
ScriptableObject の主な使用例は以下のとおりです。
- エディターセッション中のデータの保存
- プロジェクトのデータをアセットとして保存し、ランタイムに使用
ざっくり言ってしまうと、エクセルで言う「テーブル」のようなデータベースを作って、保存しておける機能です。なので、ある決まったUIテンプレートを使い回して、表示する情報だけ差し替えたい場合の参照元として、ScriptableObjectを使うと便利なのです
ScriptableObjectを使ってデータベースを作成する
まずは、データベースの構造を定義します。
エクセルで列の項目名と表示形式(例「項目名:定価 / 表示形式:通貨」)を定義するようなものです。
ScriptableObject を利用してデータベース(ここでは「ResultBase」)の構造を定義する
1 using System.Collections;
2 using System.Collections.Generic;
3 using UnityEngine;
4
5 [CreateAssetMenu] // Projectウィンドウのコンテキストメニューからデータを作成できるようにする
6 public class ResultBase : ScriptableObject // データ構造を定義するクラス:ResultBase
7 {
8 [SerializeField] Sprite resultimage; // Sprite型の変数:resultimageを定義
9 [TextArea] // Inspectorで文字列を複数行入力できるようにする
10 [SerializeField] string resulttext; // string型の変数:resulttextを定義
11
12 public Sprite Resultimage { get => resultimage; } // プロパティを作成(getメソッド)
13 public string Resulttext { get => resulttext; } // プロパティを作成(getメソッド)
14 }
最後の「public Sprite Resultimage { get => resultimage; }」は「プロパティ(getアクセサ)」と呼ばれるもので、このスクリプト内で定義している「変数:resultimage」を「変数:Resultimage」を介して他のスクリプトからも見えるようにしています。


何故、そんな回りくどいことをするのかと言うと「変数:resultimage」は「SerializeField」として定義されているため、そのままでは他のスクリプトから見ることができません。
※他のスクリプトから誤って変更されるリスクを回避するため、敢えて見えないようにしています。
そこで「プロパティ(getアクセサ)」を使い「変数:resultimage」を直接見せずに「変数:Resultimage」を介して見せるようにしているわけです。
※プロパティの生成方法は後で解説しています。
データセットの変数とPrefabのTextMeshPro・Imageとの関係


ScriptableObjectで作成したデータベース


※[CreateAssetMenu] を記載することにより、UnityエディターのProjectウィンドウの「+」から、診断結果データ(Result Base)を作成できるようになります。
ScriptableObjectで作成したデータセットの一例


プロパティの生成方法
プロパティの生成方法は以下の通りです。
コンテキストメニューが開くので「クイックアクションとリファクタリング」を選択します。




プロパティ(get、set)が生成されるので、今回不要な「set」以下を削除します。
※参照(get)だけしたいので、変更(set)は削除しておきます。


データセットをPrefabに読み込ませる
データセットを作成したので、これをPrefabに読み込ませるスクリプトを作成します。
内容としては、Prefabの「変数:rimage(イメージ:ResultImage)」にはResultBaseの「プロパティ:ResultImage(変数:resultimageが代入されている)」を、Prefabの「変数:rtext(テキスト:ResultText)」にはResultBaseの「プロパティ:ResultText(変数:resulttextが代入されている)」を読み込ませる、というものです。


データセットをPrepabに読み込ませる
1 using System.Collections;
2 using System.Collections.Generic;
3 using UnityEngine;
4 using UnityEngine.UI; // ImageやText、ButtonといったUIを扱う場合に必要な名前空間
5 using TMPro; // TextMeshProのテキストなどを扱う場合に必要な名前空間
6
7 public class Result : MonoBehaviour
8 {
9 [SerializeField] Image rimage; // Image型の変数:rimageを定義、画像を表示するUIとの接続
10 [SerializeField] TextMeshProUGUI rtext; // TextMeshProUGUI型の変数:rtextを定義、テキストを表示するUIとの接続
11
12 public void Set(ResultBase resultBase) // 引数:ResultBaseを持つメソッド:Set
13 {
14 rimage.sprite = resultBase.Resultimage; // 変数:rimageへ、引数と対応したResultImageを代入
15 rtext.text = resultBase.Resulttext; // 変数:rtextへ、引数と対応したResultImageを代入
16 }
17 }
※データセットを番号で指定して読み込ませるため「メソッド:Set」に「引数:ResultBase」を持たせています。
このスクリプトをPrefabにアタッチします。すると、PrefabのInspectorウインドウに「rImage」と「rtext」を指定する欄ができるので、以下のように設定します。
- rimage:Prefabの「ResultImage」をセット
- rtext:Prefabの「ResultText」をセット





以上で、データセットの値を反映したカード(Prefab)を表示させる準備が整いました!
Prefabを実体化(インスタンス化)するスクリプトを作成する
最後に、データベースの中から番号でデータセットを指定し、その内容を読み込んだPrefabを実体化(インスタンス化)するスクリプトを作成します。
1 using System.Collections;
2 using System.Collections.Generic;
3 using UnityEngine;
4
5 public class ResultGenerator : MonoBehaviour
6 {
7 [SerializeField] ResultBase[] resultBases; // データセットの配列
8 [SerializeField] Result resultprefab; // インスタンス化するPrefabを指定する変数
9 [SerializeField] Transform spawnplace; // インスタンス化する場所を指定する変数
10
11 public void Spawn(int number) // 引数:numberに対応するprefabを生成
12 {
13 Result result = Instantiate(resultprefab,spawnplace,false); // 引数に生成するPrefab、生成場所を指定
14 result.Set(resultBases [number]); // 生成したPrefabに引数:numberに応じたデータセットを反映
15 }
16 }
「メソッド:Spawn」の「引数:number」に、データセットの「配列:ResultBases」の番号を渡すことで、指定したデータセットの値をPrefabに表示させています。
最後に、空のゲームオブジェクト(今回は「ResultGenerator」)を作成し、この「スクリプト:ResultGenerator」をアタッチします。
「スクリプト:ResultGenerator」をアタッチしたら、Inspectorウインドウに以下のように設定します。
- Result Bases:データセットの個数
- Element 0~:データセットをすべて参照
- Resultprefab:UIテンプレートとして利用するPrefabをセット
- Spawnplace:Canvasをセット
空のゲームオブジェクト:ResultGeneratorのInspectorウインドウ


この「スクリプト:ResultGenerator」を参照し、データセットの番号を引数として渡したうえで「メソッド:Spawn」を実行すれば、指定したデータセットのカード(Prefab)を表示させることができます!
Prefabにデータセットの内容を表示させる
ここでは、あるボタンのOnClickイベントに「スクリプト:ResultGenerator」の「メソッド:Spawn」をセットする、という設定で解説を進めます。
Hierarchyウインドウでボタンを選択し、InspectorウインドウのOnClickイベントを下図のように設定します。
- 「空のゲームオブジェクト:ResultGenerator」をセット
- 「スクリプト:ResultGenerator」の「メソッド:Spawn」をセット
- 表示させたいデータセットの番号を入力


以上で、ボタンをクリックすると、指定したデータセットの内容を反映したカード(Prefab)が表示されます。
まとめ
Prefabも、ScriptableObjectを利用したデータベースも、それぞれを作るのは簡単です。ただ、この二つを連携させる作業が、慣れるまではちょっとややこしいですよね。ゆくすぃはそうでした。
でも、これができると、ゲーム構成の幅が広がること間違いなし!なので、諦めずに、少しずつ理解を深めていきたいところです。一緒に頑張りましょう!
以上、最後まで読んでいただき有難うございました!