LessonEX モンスター図鑑
EX-1 ベースの作成
ここでは簡易的なモンスター図鑑の実装方法を解説していきます。モンスター図鑑を実装する予定のないゲームでも、キャラクター図鑑やアイテム欄、ステージセレクトなどにも応用できる機能なのでぜひ使ってみてください。手順自体は多いですが、プログラム量は少なめです。
制作前にざっくりと画面のレイアウトを考えておきましょう。左側にモンスターの画像を並べて、クリックされると右側の情報表示用ウィンドウに画像や説明文が表示されるようにします。

プロジェクトを開いて、使用するモンスター画像とウィンドウの素材をインポートしてください。使用する素材は自由ですが、モンスター画像は縦と横のサイズが同じでないとアスペクト比がおかしくなってしまうので注意してください。
【サンプルで使用しているサイト(モンスター画像)】 Rド 様
※ 配布されているのはbmp形式なので、サンプルでは手動でpng形式に透過しています
【サンプルで使用しているウィンドウ画像】
画面のアスペクト比はサンプルでは16:10にしておきます。制作中のゲームに実装する人は変更しなくても構いません。


まずはウィンドウを配置します。
ウィンドウを作成するときはSpriteEditorのSlice機能を使うと便利です。普通にウィンドウを変形させると左の画像のように四隅が歪んでしまいますが、この機能を使うと右の画像のように四隅の大きさを固定したままウィンドウの大きさを変えることができます(2Dランゲーム編Lesson4参照)


ウィンドウのスプライトの設定を変更します。SpriteModeをMultipleに、MeshTypeをFull Rectに変更してください。変更したら右下のApplyボタンをクリックします。
Applyできたら「Sprite Editor」を開いてください。

2Dゲームの場合はそのままSpriteEditorを開けますが、3Dゲームの場合はパッケージのインストールが必要になります。

「Window」→「Package Manager」を選択してください。

左上のPackagesを「Unity Registry」に変更して、右上の検索欄に「Sprite」と入力しましょう。「2D Sprite」が左側に表示されるのでそれを選択して、右下のInstallボタンをクリックしてください(参考)

Unity Tips!

SpriteEditorを開いたら、まずはウィンドウ画像全体を囲うようにドラッグしてください。
画像を選択できたらSpriteウィンドウのBorderの値を変更して、四隅を切り取れるようにウィンドウを分割してください。サンプルでは各項目20に設定しています。
設定できたら右上のApplyを選択してください。

「UI」→「Image」を追加して、Source Imageにウィンドウ画像をドラッグ&ドロップしてください。ウィンドウ画像が反映されます。

モンスター画像を表示するためのウィンドウ画像を配置してください。Scaleもお好みで調整しましょう。

2つ目のウィンドウを追加して、モンスターの情報を表示するウィンドウも配置してください。

情報表示用ウィンドウの中に仮のUIを配置していきます。中身はスクリプトで差し替えるので適当で構いません。
まずはTextMeshProと日本語のフォントを導入しておきます。3Dアクションゲーム編Lesson4を参考にしてください。日本語導入はこちらから。
導入が面倒な方はレガシー版のTextを使用してください。
情報表示ウィンドウの子オブジェクトにモンスターの名前、説明文、画像を追加してください。場所や大きさはお好みでOKです。
【サンプルで使用しているフォント】https://logotype.jp/font-corpmaru.html

次は左側のウィンドウに表示するボタンを実装します。「UI」→「Button」を選択してボタンを追加してください。
今回は画像を直接ボタンにするので、子オブジェクトのTextは削除しておきます。Source Imageはスクリプトから差し替えるので、適当でOKです。

次はボタンにアタッチするスクリプトを作成します。
MonsterButtonスクリプトを作成して、以下のように入力してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // 忘れないように注意!
public class MonsterButton : MonoBehaviour
{
// モンスター番号
int m_monsterNo = -1;
// 初期化用の関数
// モンスターの番号、画像、登録済みかどうかを指定する
public void Set(int no, Sprite monsterImage, bool interactable)
{
// モンスター番号を登録する
m_monsterNo = no;
// モンスター画像を変更する
GetComponent<Image>().sprite = monsterImage;
// 押せるかどうか設定する
GetComponent<Button>().interactable = interactable;
}
// ボタンが押された時に呼び出す関数
public void ButtonDown()
{
// 情報表示用ウィンドウを更新(後で実装)
}
}
コードが書けたら保存して、先ほど作成したボタンにアタッチしておいてください。
Buttonコンポーネントには自身がクリックされた時に実行する関数を設定することができます(実行する関数はpublicである必要があります)
ButtonコンポーネントのOnClick()内にあるプラスボタンをクリックしてください。

クリック時に実行したいコンポーネントをドラッグ&ドロップしてください。今回はMonsterButtonコンポーネントです。

実行したい関数を選択します。「No Function」→「MonsterButton」と選択して、先ほど記述したButtonDown関数をクリックしてください。この関数は今は中身がありませんが、後で書くので気にしないでください。


ここまでできたら、このボタンを量産できるようにプレハブ化しましょう。ボタンをプロジェクト内にドラッグ&ドロップしてください。
プレハブ化できたらシーン内にあるボタンは不要なので削除しておきます。

EX-2 モンスター一覧の実装
モンスター一覧を実装していきましょう。
Canvas内に「UI」→「Scroll View」を追加してください。

Scroll Viewの位置と大きさがそのままボタンが表示される範囲になるので、ウィンドウに合うように調整してください。
ColorのA(不透明度)を0にして、背景を透明にしておきましょう。

今回スクロールしたいのは上下のみで、左右のスクロールは不要なのでScroll RectのHorizontalのチェックを外してください。
また、Scroll View内のScrollbar Horizontalも削除してください。


Scrollbar VerticalのBottomを0にして、スクロールバーの縦幅を調整してください。

Scroll View→Viewportの子オブジェクトであるContentに、AddComponentから「Grid Layout Group」と「Content Size Fitter」を追加してください。これらはスクロールではなく、ボタンを綺麗に並べるためのコンポーネントです。


ContentSizeFitterの2つの要素を「Preferred Size」にしましょう。
Grid Layout Groupのパラメータをお好みに調整してください。CellSizeでボタンの大きさを、Constraintで列の数を指定することができます。他にも列の間隔や並べ方も指定できます。
実際の表示をプレビューしたい場合は、Contentの子オブジェクトにボタンのプレハブを複数追加してください。実行することでスクロールの確認もできます。ボタンは設定が終わったら削除しましょう。


設定が終わったらContent下のボタンは削除して、ボタンを生成するスクリプトを準備しましょう。
新しい空オブジェクトZukanObjectを作成して、新しいスクリプトZukanSystemを作成してアタッチしてください。


ZukanSystemスクリプトを開いて、以下のように入力してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // 忘れないように注意!
using TMPro; // TextMeshProを使っている場合は必要
public class ZukanSystem : MonoBehaviour
{
// これをつけると構造体をインスペクターで編集できるようになる
[System.Serializable]
// モンスター情報の構造体
public struct Monster
{
public Sprite MonsterSprite; // 画像
public string Name; // 名前
// これをつけるとインスペクター内でのString入力で6行表示できる
[SerializeField, MultilineAttribute(6)]
public string Setumei; // 図鑑説明
public bool Register; // 登録フラグ
}
// これをつけるとprivateな変数をインスペクターで編集できるようになる
[SerializeField]
// Monster構造体を用いたモンスターデータ
Monster[] MonsterData;
// 生成するボタンのプレハブ
[SerializeField]
GameObject Button;
// ボタンを追加するオブジェクト
[SerializeField]
GameObject Content;
// 表示用データ
[SerializeField]
GameObject Data_Sprite, Data_Name, Data_Setumei;
void Start()
{
// 最初は非表示にしておく
Data_Sprite.SetActive(false);
Data_Name.SetActive(false);
Data_Setumei.SetActive(false);
// ボタンを生成
for (int i = 0; i < MonsterData.Length; i++)
{
// プレハブからボタンを生成
GameObject button = Instantiate(Button);
// 作成したオブジェクトを子オブジェクトにする
button.transform.SetParent(Content.transform);
// 子オブジェクトにしたことでサイズが変わるので元に戻す
button.transform.localScale = Vector3.one;
// コンポーネントを取得
MonsterButton monsterButton = button.GetComponent<MonsterButton>();
// 生成したボタンに番号や画像を設定する
monsterButton.Set(i, MonsterData[i].MonsterSprite, MonsterData[i].Register, this);
}
}
// 引数に入力された値のデータを表示する
public void ZukanSet(int no)
{
// アクティブにする
Data_Sprite.SetActive(true);
Data_Name.SetActive(true);
Data_Setumei.SetActive(true);
// 更新する
Data_Sprite.GetComponent<Image>().sprite = MonsterData[no].MonsterSprite;
Data_Name.GetComponent<TextMeshProUGUI>().text = MonsterData[no].Name;
Data_Setumei.GetComponent<TextMeshProUGUI>().text = MonsterData[no].Setumei;
// ※ TextMeshProではなくレガシーのTextを使用している場合は以下の書き方
// Data_Name.GetComponent<Text>().text = MonsterData[no].Name;
// Data_Setumei.GetComponent<Text>().text = MonsterData[no].Setumei;
}
}
Set関数の部分で存在しない第四引数を指定しているためエラーになりますが、後で追加するため今は気にしなくても構いません。

【プログラムの解説】
・[System.Serializable]や[SerializeField]はアトリビュートと呼ばれるもので、変数やクラスに属性を付与することができます。詳しくはEXや3D脱出ゲーム編などで解説しているためここでは省略します。
・transform.SetParent(親オブジェクトのtransform); は、親オブジェクトを変更する関数です。ここで生成したボタンをContentの子オブジェクトにしています。
MonsterButtonスクリプトを開いて、赤い部分のコードを追加してください。
ZukanSystemとMonsterButtonはお互いに干渉する形になっているので、両方のコードを見てどのように処理が流れているか確認してみてください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // 忘れないように注意!
public class MonsterButton : MonoBehaviour
{
// モンスター番号
int m_monsterNo = -1;
// 図鑑システム
ZukanSystem m_zukan_System;
// 初期化用の関数
// モンスターの番号、画像、登録済みかどうかを指定する
public void Set(int no, Sprite monsterImage, bool interactable, ZukanSystem zukan_System)
{
// モンスター番号を登録する
m_monsterNo = no;
// モンスター画像を変更する
GetComponent<Image>().sprite = monsterImage;
// 押せるかどうか設定する
GetComponent<Button>().interactable = interactable;
// 図鑑システムを登録する
m_zukan_System = zukan_System;
}
// ボタンが押された時に呼び出す関数
public void ButtonDown()
{
// モンスター番号を渡すと表示が更新される
m_zukan_System.ZukanSet(m_monsterNo);
}
}
複雑な処理に見えますが、ボタン生成時にボタンにモンスター番号を教える→ボタンが押されたらそのモンスター番号を使って表示を更新する…といった流れになっています。

ZukanObjectのインスペクターで、 モンスターの画像や名前、説明文を自由に設定してください。登録フラグ(Register)がfalseだとボタンが半透明になり押せなくなります。

生成するボタンのプレハブ、Content、情報表示用の要素をそれぞれ設定してください。

これで図鑑機能の完成です。
左側の画像をクリックすると、右側のウィンドウに情報が表示されます。

今回はクリック時に右側のウィンドウを更新していましたが、例えばここでシーンを切り替えればステージセレクト画面にもなります。Scroll Viewを自作するとなるとかなり大変だと思いますので活用していきましょう。
モンスター情報の管理も、実際の開発を想定するなら配列ではなくScriptableObjectを使うと良いでしょう(3D脱出ゲーム編2-2参照)
また、今回使用したScroll ViewやGrid Layout Groupは一覧を表示するだけでなく他の使い方もあります。あくまで今回使用例として図鑑を作っただけなので、誤解しないようにしましょう。
Scroll Viewは、Contentの子オブジェクトにサイズの大きい画像を入れることで、スクロールすることもできます。Contentのサイズがそのままスクロールできる範囲になります。


Scroll Viewは大きな画像を狭い範囲に表示したい時に役立ちます。例えば、巨大な地図を表示したい時に役立つでしょう。
デフォルトのスクロールバーの見た目は味気ないですが、画像も差し替えられます。

Grid Layout Groupも一覧に限らず、大量のオブジェクトを一定間隔で整列させたい時に役立ちます。UIの機能を活用して、ぜひ様々なUIを実装してみてください。
Unity Tips!
