【Unity】UIをコントロールするスクリプトの配置

2024年4月17日


UnityでUIを作る際、「コンポーネント思考」が鍵です。画面の各部分を独立した要素として扱い、それぞれに特定の役割を持たせることで、直感的に理解しやすく、再利用しやすい設計が可能になります。また、MVCやMVVMといった設計パターンを取り入れることで、見た目とロジックを上手く分離し、より整理されたコードに。UnityのCanvasを賢く使ってパフォーマンスも抜群に!イベントシステムを活用すれば、ユーザーの操作に対する反応もスムーズに。さらに、コードは役割ごとに分けて、命名も工夫することで、プロジェクトの管理がぐっと楽に。これらのアプローチは、Unity UI開発の基礎を固め、より魅力的なインターフェースを作る第一歩です。

Unity UI開発のベストプラクティス: コンポーネント化から設計パターンの適用まで

  1. コンポーネント化思考: Unityでは、UI要素をコンポーネントとして考えることが重要です。ボタン、テキスト、イメージなど、各UI要素に特定のスクリプトを割り当てることで、その振る舞いを制御します。これは、オブジェクト指向プログラミングの原則と似ています。
  2. MVC (Model-View-Controller) や MVVM (Model-View-ViewModel) などの設計パターンを利用する: UIのコントロールとロジックを分離するために、これらのデザインパターンを利用します。
  3. Canvasをうまく利用する: UnityのUIシステムは、Canvasオブジェクト上に構築されます。適切なCanvasの設定と利用は、パフォーマンスを保ちつつ、求めるUIを実現する上で重要です。例えば、UI要素が動的に変更されない場合は、Canvasを静的に設定してパフォーマンスを向上させることができます。
  4. イベントシステムの活用: Unityのイベントシステムを使って、ユーザーのアクション(クリックやタッチなど)に反応するスクリプトを作成します。これにより、コードの可読性と保守性が向上します。
  5. スクリプトの責務を明確にする: 各スクリプトが担当する責務を明確にし、1つのスクリプトに多くの機能を盛り込まないようにします。これは、プログラムの可読性と保守性を高めるために重要です。
  6. 再利用可能なコンポーネントを作成する: よく使用するUIパターンや機能については、再利用可能なコンポーネントとして作成します。これにより、開発の効率化と一貫性のあるUIデザインが実現されます。
  7. 適切な命名規則を守る: スクリプトやUI要素の命名に一貫性を持たせることで、プロジェクトが大きくなった際の管理が容易になります。

UnityでのUI開発に関しては、プログラミングの基本的な理解があれば、独学でも十分に学ぶことが可能です。特に、既にプログラミングの経験がある方は、C#の基本的な文法を理解した上で、これらのコツを活かしながら学習を進めることができるでしょう。

UnityにおけるMVCパターンを用いたUI開発のサンプル

UnityプロジェクトでのUIコントロールを目的としたサンプル構成を紹介します。この構成は、UnityとC#の基本を学びつつ、実際にUI要素を制御する方法を理解するのに役立ちます。

UnityでのUI開発にMVC(Model-View-Controller)デザインパターンを適用することは、アプリケーションの構造を整理し、メンテナンスと拡張を容易にする良い方法です。以下は、UnityプロジェクトにMVCパターンを適用したUI制御のサンプル構成です。

MVCの基本概念:

  • Model: アプリケーションのデータとビジネスロジックを担います。UIに表示されるデータやそのデータに対する操作の定義が含まれます。
  • View: ユーザーに表示されるUIの要素です。Modelからのデータを表示し、ユーザーのアクションをControllerに伝達します。
  • Controller: ユーザーの入力に基づいてModelを更新し、その変更をViewに反映させます。

サンプル構成:

  1. Model
    • ScoreModel.cs: スコアやゲーム状態のデータを管理します。
  2. View
    • ScoreView.cs: スコア表示のUI要素(テキストなど)を制御します。
  3. Controller
    • GameController.cs: ゲームの主要なロジックを担当し、ModelとViewの間の通信をコーディネートします。

サンプルスクリプト:

Model: ScoreModel.cs

public class ScoreModel
{
    public int Score { get; private set; } = 0;

    public void AddScore(int amount)
    {
        Score += amount;
    }

    public void ResetScore()
    {
        Score = 0;
    }
}

View: ScoreView.cs

using UnityEngine;
using TMPro; // TextMeshProのネームスペースを使用

public class ScoreView : MonoBehaviour
{
    [SerializeField]
    TextMeshProUGUI scoreText; // TextMeshProUGUIタイプの変数に変更

    public void UpdateScore(int score)
    {
        scoreText.text = $"Score: {score}";
    }
}

Controller: GameController.cs

using UnityEngine;

public class GameController : MonoBehaviour
{
    public ScoreModel scoreModel; // ScoreModelの参照
    public ScoreView scoreView;

    void Start()
    {
        // ScoreModelのインスタンスを作成
        scoreModel = new ScoreModel();
    }

    public void AddScore(int amount)
    {
        // スコアを増やす
        scoreModel.AddScore(amount);
        // スコアの更新をViewに反映させる
        scoreView.UpdateScore(scoreModel.Score);
    }

    // 他のゲーム管理メソッド...
}

このサンプルでは、スコアの更新を例にMVCパターンを適用しています。ゲーム内で何らかのイベントがスコアを変更すると、GameControllerScoreModelを更新し、その変更をScoreViewに通知してUIを更新します。このパターンを使用することで、データロジックとUIの更新を分離し、ソフトウェアの設計をよりクリーンに保つことができます。

各スクリプトの配置

UnityプロジェクトにおいてMVCパターンを適用した場合、各スクリプトをどのようなゲームオブジェクトにアタッチするかは、その機能と役割によって異なります。以下に、一般的な指針を示します。

Model: ScoreModel.cs

  • Modelスクリプトは通常、直接ゲームオブジェクトにアタッチされることはありません。これらはデータとビジネスロジックを扱うため、他のコンポーネント(主にコントローラー)によってインスタンス化され、使用されることが多いです。
  • ただし、シングルトンパターンを使用してゲーム全体で1つのインスタンスのみを持つ場合など、特殊なケースではゲームオブジェクトにアタッチされることもあります。

View: ScoreView.cs

  • Viewスクリプトは、その表示内容を担当するUI要素を持つゲームオブジェクトにアタッチされます。たとえば、ScoreView.csはスコアを表示するUIテキストを含むCanvas内のゲームオブジェクト、またはその子オブジェクトにアタッチします。
  • 具体的には、ScoreView.csをスコア表示用のTextコンポーネントがアタッチされたゲームオブジェクト(たとえば、"ScoreText"という名前のオブジェクト)にアタッチするのが適切です。

Controller: GameController.cs

  • Controllerスクリプトは、ゲームのメインロジックやシーンのコントロールを担うため、シーン内の中心となるゲームオブジェクトにアタッチされます。これはしばしば、ゲーム全体を管理するための空のゲームオブジェクト(たとえば、"GameManager"や"GameController"という名前のオブジェクト)です。
  • このゲームオブジェクトは通常、ゲーム開始時から存在し、シーン間で持続する必要がある情報を管理します。UnityのDontDestroyOnLoadメソッドを使用して、シーンのロード時に破棄されないように設定することが一般的です。
コントローラー名のついて

GameControllerという名前が適切かどうかは、スクリプトの担う責務とプロジェクトの全体的な構成によります。GameControllerがゲーム全体の流れや状態を管理し、プレイヤーのスコア更新だけでなく、他のゲームイベント(例えばゲーム開始、終了、リスタートなど)の制御も担っているのであれば、「GameController」という名前はその役割を適切に反映しています。

しかし、スクリプトが主にスコアの管理や表示に関わる機能のみを担っている場合、より具体的な名前を選択することで、そのスクリプトの役割を明確に表現することができます。例えば、スコアの更新と表示に特化しているならば、「ScoreManager」や「ScoreController」といった名前がその機能をより正確に反映するでしょう。

名前を変更する理由:

  • 役割の明確化: スクリプトの名前からその機能を直感的に理解できるようにすることで、プロジェクトの可読性と保守性が向上します。
  • 拡張性の向上: プロジェクトが成長するにつれて、さまざまな機能を持つ複数のコントローラーやマネージャーが必要になることがあります。各スクリプトに具体的な名前を付けることで、プロジェクトの構造を柔軟に拡張できます。

名前の変更例:

  • ScoreController: スコアに関するロジックのみを担当する場合。
  • UIController: UI要素全般の制御に特化している場合。
  • GameManager: ゲームの全体的な流れや状態の管理を行う場合。

最終的には、スクリプトの責務とプロジェクト内での役割を最もよく表す名前を選択することが重要です。また、プロジェクトの他のメンバーともその名前についてコンセンサスを取ることで、チーム全体の理解と協力を促進することができます。

このようにスクリプトを配置することで、MVCパターンの各コンポーネントがその責務に応じて適切に機能し、コードの整理と再利用が容易になります。特にUnityとC#に慣れていない中途採用者にとって、このような構成はプロジェクトの理解を深めるのに役立つでしょう。

ヒエラルキーの構成例

UnityプロジェクトにおけるMVCパターンを適用した際の典型的なヒエラルキー構成を例示します。この構成は、ゲーム内のスコアを表示・更新するシンプルなシステムを例にとります。以下の構成は、Unityエディター内でのゲームオブジェクトの配置と、それに紐づくコンポーネント(スクリプトなど)を想定しています

- GameController (GameObject)
  - [GameController.cs]
  - [任意の他のゲーム管理用スクリプト]

- Canvas (GameObject)
  - ScoreDisplay (GameObject)
    - [Text] (UI Textコンポーネント)
    - [ScoreView.cs]

- [その他のUIコンポーネントやゲームオブジェクト]
  • GameController: これはシーンの根幹となるゲーム管理を担うゲームオブジェクトです。GameController.cs(ゲームの主要なロジックを担うコントローラースクリプト)がアタッチされています。必要に応じて、他のゲーム全体の管理を行うスクリプトもここに含まれることがあります。
  • Canvas: UI要素を格納するためのゲームオブジェクトです。UnityのUIシステムでは、Canvasコンポーネントがアタッチされたゲームオブジェクトの下にあるUI要素が描画されます。このCanvasは、スコア表示やボタンなど、ゲーム内の全てのUI要素を保持します。
  • ScoreDisplay: スコアを表示するためのゲームオブジェクトで、Canvasの子オブジェクトとして配置されます。Textコンポーネント(UnityのUIテキストを表示するためのコンポーネント)がアタッチされ、スコアの具体的な数値を表示します。ScoreView.csスクリプトもこのゲームオブジェクトにアタッチされ、スコアの表示を更新するためのロジックを担います。

Unityエディターでの設定手順:

  1. GameControllerオブジェクトの作成: 空のGameObjectを作成し、GameControllerと名付けます。GameController.csスクリプトをアタッチします。
  2. CanvasとScoreDisplayの設定: UIを作成するためにCanvasオブジェクトを追加し、その子としてスコア表示用のTextMeshProUGUIコンポーネントを持つScoreDisplayオブジェクトを作成します。ScoreView.csスクリプトをScoreDisplayオブジェクトにアタッチし、スクリプトのscoreTextフィールドにTextMeshProUGUIコンポーネントを関連付けます。

この構成では、GameControllerがゲームの状態を管理し、必要に応じてScoreModel(ここでは示されていませんが、スコアのデータを保持するモデルクラス)のスコアを更新します。スコアが更新されると、GameControllerScoreView.csを通じてScoreDisplayのテキストを更新し、プレイヤーに現在のスコアを表示します。

このようなヒエラルキー構成は、プロジェクトの構造を整理し、各コンポーネントの責任を明確にするのに役立ちます。また、将来的にUI要素やゲームの機能を拡張する際の基盤としても機能します。

利用方法

上記MVCモデルでプレイヤーがスペースキーを押すとスコアが増えるサンプルを作ってみましょう

PlayerControllerの追加

プレイヤーの入力を管理するPlayerControllerクラスを作成します。このクラスはプレイヤーからの入力(この場合はスペースキー)を監視し、適切なアクション(スコアの追加)をGameControllerに指示します。

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public GameController gameController;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // スペースキーが押されたらGameControllerを通じてスコアを増やす
            gameController.AddScore(1);
        }
    }
}

PlayerControllerの設定: 新しくPlayerControllerスクリプトを作成し、プレイヤーを表すGameObjectにアタッチします。UnityエディターのInspectorウィンドウで、PlayerControllerスクリプトのgameControllerフィールドに、GameControllerスクリプトがアタッチされたGameObjectをドラッグ&ドロップして関連付けます。

実践エディター構成画面

実際の画面の構成になります

UIの構成

TextMeshProを使ったTextを追加します

名前をScoreDisplayに変更し、ScoreViewをアタッチします
次にTextMeshPro-TextをD&Dでアウトレット接続します

GameController

Player

クラス図

Unity

Posted by hidepon