プレハブ とは、シーン内で複数回インスタンス化できる、あらかじめ作成されたゲームオブジェクトのことです。プレハブは、再利用可能なコンポーネントの作成に役立ちます。UI Toolkit のビジュアル要素はゲームオブジェクトではないので、プレハブには当てはまりません。ただし、要素の特定の階層をロジックでカプセル化する再利用可能な UI コンポーネントとして カスタムコントロールを作成 することが可能です。UI Toolkit では、UI をゲームやアプリケーションのコードから分離することが推奨されるため、UXML を使用して構造を定義し、USS を使用して見た目を定義し、C# を使用してカスタムコントロールのロジックを定義することができます。
再利用可能な UI コンポーネント - 例えば、以下のような、キャラクターの画像と体力と攻撃力を表示するカードの、CardElement
と呼ばれるカスタムコントロール - を作成するとします。
これは、以下のおおまかな手順で作成できます。
C# で、CardElement という カスタム要素タイプ を宣言します。
UXML で、カスタムコントロールの階層を定義します。2 つのアプローチが使用可能です。どちらのアプローチでも、C# と親 UXML で CardElement
をインスタンス化できます。
カスタムコントロールの子要素への参照を見つけます。
プロパティとメソッドを公開し、(C# のクラスの場合と同様に) カスタムコントロールのロジックをカプセル化します。
カスタムコントロールをゲームやアプリケーションのコードに接続します。イベントコールバックを登録してユーザーとのインタラクションを実装することもできます。
この方法では、カスタム要素 CardElement を階層 UXML ドキュメントに含め、その直下で子要素を宣言し、階層 UXML ドキュメントを テンプレート として使用します。この方法では、階層 UXML ドキュメント内の UI 構造が固定された、よりシンプルなソリューションが提供されます。
以下の C# と UXML の例は、UXML 優先のアプローチを使用して再利用可能な UI を作成する方法を示すものです。
CardElement カスタムコントロールを定義する C# スクリプトを作成します。カスタムコントロールクラスは CardElement に画像とバッジの値を割り当てます。
using UnityEngine;
using UnityEngine.UIElements;
// Define the custom control type.
public class CardElement : VisualElement
{
// Expose the custom control to UXML and UI Builder.
public new class UxmlFactory : UxmlFactory<CardElement> {}
private VisualElement portraitImage => this.Q("image");
private Label attackBadge => this.Q<Label>("attack-badge");
private Label healthBadge => this.Q<Label>("health-badge");
// Use the Init() approach instead of a constructor because
// we don't have children yet.
public void Init(Texture2D image, int health, int attack)
{
portraitImage.style.backgroundImage = image;
attackBadge.text = health;
healthBadge.text = attack;
}
// Custom controls need a default constructor.
public CardElement() {}
}
CardElement の階層構造を定義する UXML ドキュメント (CardElement.uxml
) を作成します。この例では、USS ファイルを使用して CardElement のスタイルを設定します。
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<Style src="CardElementUI.uss" />
<CardElement>
<ui:VisualElement name="image" />
<ui:VisualElement name="stats">
<ui:Label name="attack-badge" class="badge" />
<ui:Label name="health-badge" class="badge" />
</ui:VisualElement>
</CardElement>
</ui:UXML>
カスタムコントロールをゲームに接続するには、以下を行います。
CardElement.uxml
を親 UXML ドキュメント内でインスタンス化します。UI Builder では、階層 UXML とこの UXML ドキュメントを 行き来する ことができます。CardElement
を含む CardElement.uxml
をインスタンス化します。CardElement をシーンに追加する前に、UQuery を使って CardElement を見つける必要があります。カスタムコントロールをシーンに追加した後で Init()
を呼び出します。
また、要素とのインタラクションを行うためのクリックイベントなど、ゲームプレイに関連するアクションを追加することもできます。
親 UXML の内部でインスタンス化する
以下は、UXML でのインスタンス化の例です。
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<ui:Template name="CardElement" src="CardElement.uxml"/>
<ui:Instance template="CardElement"/>
<ui:Instance template="CardElement"/>
<ui:Instance template="CardElement"/>
</ui:UXML>
ゲームで UXML ドキュメントをレンダリングする方法については、ゲームビューでの UI のレンダリングに関するドキュメント 参照してください。
C# で直接インスタンス化する
ノート: このページ内のサンプルコードは学習目的のため、Resources フォルダー を使用する方法で UXML ファイルを読み込んでいます。ただし、この方法はあまり拡張性がありません。製品版プロジェクトでは 他の方法 で参照を読み込むことをお勧めします。
以下は、C# でのインスタンス化の例です。
using UnityEngine;
using UnityEngine.UIElements;
public class UIManager : MonoBehaviour
{
public void Start()
{
UIDocument document = GetComponent<UIDocument>();
// Load the UXML document that defines the hierarchy of CardElement.
// It assumes the UXML file is placed at the "Resources" folder.
VisualTreeAsset template = Resources.Load<VisualTreeAsset>("CardElement");
// Create a loop to modify properties and perform interactions
// for each card. It assumes that you have created a function
// called `GetCards()` to get all the cards in your game.
foreach(Card card in GetCards())
{
// Instantiate a template container.
var templateContainer = template.Instantiate();
// Find the custom element inside the template container.
var cardElement = templateContainer.Q<CardElement>();
// Add the custom element into the scene.
document.rootVisualElement.Add(cardElement);
// Initialize the card.
cardElement.Init(card.image, card.health, card.attack);
// Register an event callback for additional interaction.
cardElement.RegisterCallback<ClickEvent>(SomeInteraction);
}
}
private void SomeInteraction(ClickEvent evt)
{
// Interact with the elements here.
}
}
このアプローチでは、階層 UXML ドキュメントに子要素のみを含め、C# を使用して CardElement クラス定義に 階層 UXML ドキュメントをロード します。このアプローチでは、カスタムコントロールに柔軟な UI 構造が提供されます。例えば、特定の条件に応じて異なる階層 UXML ドキュメントをロードできます。
以下の C# と UXML の例は、要素優先のアプローチを使用して再利用可能な UI を作成する方法を示すものです。
CardElement カスタムコントロールを定義する C# スクリプトを作成します。CardElement に画像とバッジの値を割り当てるコンストラクターを定義 するのに加え、このカスタムコントロールは、クラス定義内に階層 UXML ドキュメントをロードします。
using UnityEngine;
using UnityEngine.UIElements;
// Define the custom control type.
public class CardElement : VisualElement
{
// Expose the custom control to UXML and UI Builder.
public new class UxmlFactory : UxmlFactory<CardElementA> {}
private VisualElement portraitImage => this.Q("image");
private Label attackBadge => this.Q<Label>("attack-badge");
private Label healthBadge => this.Q<Label>("health-badge");
// Custom controls need a default constructor. This default constructor
// calls the other constructor in this class.
public CardElement() {}
// Define a constructor that loads the UXML document that defines
// the hierarchy of CardElement and assigns an image and badge values.
public CardElement(Texture2D image, int health, int attack)
{
// It assumes the UXML file is called "CardElement.uxml" and
// is placed at the "Resources" folder.
var asset = Resources.Load<VisualTreeAsset>("CardElement");
asset.CloneTree(this);
portraitImage.style.backgroundImage = image;
attackBadge.text = health.ToString();
healthBadge.text = attack.ToString();
}
}
ノート: パフォーマンスに関して懸念がある場合は、遅延初期化を使用して、フィールドで参照をキャッシュし、クエリの再評価を頻繁に行いすぎないようにします。
CardElement の子要素の階層を定義する UXML 文書 (CardElement.uxml
) を作成します。この例では、USS ファイルで CardElement のスタイルを設定しています。
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<Style src="CardElementUI.uss" />
<ui:VisualElement name="image" />
<ui:VisualElement name="stats">
<ui:Label name="attack-badge" class="badge" />
<ui:Label name="health-badge" class="badge" />
</ui:VisualElement>
</ui:UXML>
以下を行うことで、カスタムコントロールをゲームに接続できます。
CardElement.uxml
をインスタンス化します。UI Builder では、子要素が C# からロードされるため、階層 UXML とこの UXML ドキュメントを行き来することはできません。CardElement
を格納する CardElement.uxml
をインスタンス化します。カスタムコントロールをシーンに追加する前にコンストラクターを呼び出します。
また、要素とのインタラクションを行うためのクリックイベントなど、ゲームプレイに関連するアクションを追加することもできます。
親 UXML の内部でインスタンス化する
以下は、UXML でのインスタンス化の例です。
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<CardElement />
<CardElement />
<CardElement />
</ui:UXML>
ゲームで UXML ドキュメントをレンダリングする方法については、ゲームビューでの UI のレンダリングに関するドキュメント 参照してください。
C# で直接インスタンス化する
以下は、C# でのインスタンス化の例です。
using UnityEngine;
using UnityEngine.UIElements;
public class UIManager : MonoBehaviour
{
public void Start()
{
UIDocument document = GetComponent<UIDocument>();
// Create a loop to modify properties and perform interactions
// for each card. It assumes that you have created a function
// called `GetCards()` to get all the cards in your game.
foreach(Card card in GetCards())
{
var cardElement = new CardElement(card.image, card.health, card.attack);
// Register an event callback for additional interaction.
cardElement.RegisterCallback<ClickEvent>(SomeInteraction);
// Add the custom element into the scene.
document.rootVisualElement.Add(cardElement);
}
}
private void SomeInteraction(ClickEvent evt)
{
// Interact with the elements here.
}
}
プロジェクトの UI が複雑になる場合は、ロジックをより高いレベルで分離することが推奨されます。そうすることで、ゲームやアプリケーションの他の部分の UI 構成が行いやすくなります。
このページで紹介されている概念を応用すれば、小さな汎用的なコンポーネントから、徐々に、より特化したコンポーネントを作成できます。例えば、メインのタイトル画面を作成し、そこからユーザーがオプションメニューと About セクションにアクセスできるようにするには、3 つの異なる子 UXML ドキュメントを持つ TitleScreenManager 要素を作成します。それぞれの子が独自の要素 (Title、Options、About) を定義するようにします。
詳細は こちら の YouTube 動画を参照してください。