【補足】PrefabとScriptableObjectを使ってカードを表示する(Unity編)

prefabとScriptableObject
ゆくすぃ

ごきげんよう、Budding Lab.編集部のゆくすぃです!
ここでは、ゲーム開発に関する補足情報をお伝えしています!

例えば「サンプルゲーム:ランチ診断」の「診断結果」のように、表示に使うUIをテンプレート化して、データベースから必要なデータセットを読み込んで表示したい、といった場合に「Prefab」と「ScriptableObject」を組み合わせると、とても便利です。

Prefab=UIテンプレート、ScriptableObject=データベース

そこで、今回は「PrefabとScriptableObjectを使ってカードを表示する(Unity)」を解説します!

目次

Prefabとは

Prefabとは、ゲームオブジェクトの設計図のようなものです。量産したいゲームオブジェクトのテンプレートを一つ作り、それをPrefab化すると、PrefabをHierarchyウインドウやエディター画面にドラッグ&ドロップしたり、スクリプトで呼び出したりしてゲームオブジェクトを量産できるようになります。

Prefabは設計図

Prefabを使うメリットは次の通りです。

  • テンプレートを複製するので、同一GameObjectを何度も作る必要がない
  • Prefabに変更をかけると、Prefabを使ったすべてのGameObjectに変更が反映される

つまり、データセットを読み込ませるUIテンプレートには、Prefabを使うと便利なのです!

ScriptableObjectとは

ScriptableObjectについて、Unity マニュアルでは以下のように説明されています。

ScriptableObject – Unity マニュアル

ScriptableObject の主な使用例は以下のとおりです。

  1. エディターセッション中のデータの保存
  2. プロジェクトのデータをアセットとして保存し、ランタイムに使用

参考URL:ScriptableObject(Unity マニュアル)

ざっくり言ってしまうと、エクセルで言う「テーブル」のようなデータベースを作って、保存しておける機能です。なので、ある決まった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」を介して他のスクリプトからも見えるようにしています。

プロパティ(getアクセサ)

何故、そんな回りくどいことをするのかと言うと「変数:resultimage」は「SerializeField」として定義されているため、そのままでは他のスクリプトから見ることができません。
※他のスクリプトから誤って変更されるリスクを回避するため、敢えて見えないようにしています。

そこで「プロパティ(getアクセサ)」を使い「変数:resultimage」を直接見せずに「変数:Resultimage」を介して見せるようにしているわけです。
※プロパティの生成方法は後で解説しています。

データセットの変数とPrefabのTextMeshPro・Imageとの関係

ScriptableObjectで作成するデータセットとPrefabとの関係

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

ProjectウィンドウからResultBaseを作れるようになる

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

ScriptableObjectで作成したデータセットの一例

ResultBaseで作成したデータセットの例

プロパティの生成方法

プロパティの生成方法は以下の通りです。

STEP
[SerializeField] Sprite resultimage; の resultimage の上で右クリックする

コンテキストメニューが開くので「クイックアクションとリファクタリング」を選択します。

プロパティの作り方
STEP
コンテキストメニューから「フィールドのカプセル化」を選択する
フィールドのカプセル化
STEP
プロパティが生成されるので不要な部分を削除する

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

不要なsetメソッドを削除する

データセットをPrefabに読み込ませる

データセットを作成したので、これをPrefabに読み込ませるスクリプトを作成します。

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

スクリプト:ResultBase

データセットを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」をセット
Prefubにスクリプト:Resultをアタッチする
ゆくすぃ

以上で、データセットの値を反映したカード(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ウインドウに以下のように設定します。

ResultGeneratorのInspectorウインドウ
  • Result Bases:データセットの個数
  • Element 0~:データセットをすべて参照
  • Resultprefab:UIテンプレートとして利用するPrefabをセット
  • Spawnplace:Canvasをセット

空のゲームオブジェクト:ResultGeneratorのInspectorウインドウ

空のゲームオブジェクトにスクリプト:ResultGeneratorをアタッチ

この「スクリプト:ResultGenerator」を参照し、データセットの番号を引数として渡したうえで「メソッド:Spawn」を実行すれば、指定したデータセットのカード(Prefab)を表示させることができます!

Prefabにデータセットの内容を表示させる

ここでは、あるボタンのOnClickイベントに「スクリプト:ResultGenerator」の「メソッド:Spawn」をセットする、という設定で解説を進めます。

Hierarchyウインドウでボタンを選択し、InspectorウインドウのOnClickイベントを下図のように設定します。

OnClickイベント
  1. 空のゲームオブジェクト:ResultGenerator」をセット
  2. 「スクリプト:ResultGenerator」の「メソッド:Spawn」をセット
  3. 表示させたいデータセットの番号を入力
ボタンのOnClickイベントを設定する

以上で、ボタンをクリックすると、指定したデータセットの内容を反映したカード(Prefab)が表示されます。

まとめ

Prefabも、ScriptableObjectを利用したデータベースも、それぞれを作るのは簡単です。ただ、この二つを連携させる作業が、慣れるまではちょっとややこしいですよね。ゆくすぃはそうでした。
でも、これができると、ゲーム構成の幅が広がること間違いなし!なので、諦めずに、少しずつ理解を深めていきたいところです。一緒に頑張りましょう!

以上、最後まで読んでいただき有難うございました!

よかったらシェアしてね!
  • URLをコピーしました!
目次