WinFormsのButtonからUnityのGameObjectへ ― newとComponentの違いを比べてみよう

広告

前回の記事では、デザイナーが自動生成していた new Button()Click += を、自分で書いてみました。

Unityでも画面に「もの」を置いて動かしますが、作り方の考え方が少し違います。WinFormsでは完成した部品を作って追加しました。Unityでは土台に機能を組み合わせて作ります

この記事では、その違いを「ボタン」という同じ題材で比べてみます。

この記事の位置づけ

  1. GitHub Desktopで見てみよう ― WinFormsでボタンを置くと、裏では何が変わる?
  2. WinFormsで学ぶインスタンス ― Designerが書いたコードを自分で書いてみよう
  3. この記事:WinFormsの部品の作り方が、Unityではどう変わるかを整理する

この記事は「WinFormsからUnityへ ― C#で学んだことをUnityにつなげる学習ルート」のSTEP 3(橋3)にあたります。

1. WinFormsのおさらい ― 完成した部品を作って追加する

前回書いたコードを思い出しましょう。

Button button = new Button();      // ボタンを作る
button.Text = "押して";            // 文字を設定する
button.Click += Button_Click;      // クリックされたときの処理を登録する
Controls.Add(button);              // フォームに追加する

Button クラスには、ボタンに必要な機能が最初から全部入っています。

  • 画面に表示される
  • 文字を持っている(Text
  • 位置と大きさを持っている(LocationSize
  • クリックできる(Click

つまりWinFormsでは、「機能がそろった完成品」を new で作り、フォームに追加するという流れです。

2. Unityのボタンを分解してみよう

Unityでもボタンを作ってみます。

Hierarchyの「+」から UI > Button – TextMeshPro を選びます。(初回は TextMeshPro のインポート画面が出るので、「Import TMP Essentials」を押します。)

作られた「Button」を選択して、Inspectorを見てください。

UnityのButtonは、1つの完成品ではありません。「Button」という名前のGameObjectに、4つのComponentが付いたものです。

さらにHierarchyでButtonの左の▶を開くと、子として「Text (TMP)」があります。

WinFormsでは button.Text というプロパティでした。Unityでは、文字は別のGameObjectとして、ボタンの子に置かれています。

実験:Componentを無効にしてみよう

Inspectorで、ImageButton Componentを一時的に無効にして、Gameビューで変化を確かめてみましょう。Component名の左にあるチェックボックスを外すと、そのComponentが無効になります。(Rect Transform のように、チェックボックスがないComponentもあります。)

  • Image を無効にする → 背景が消え、文字だけが残る
  • Button を無効にする → 見た目はそのままでも、押しても反応しない

確かめたら、チェックを元に戻しておきましょう。

「見た目」と「押せる機能」が、別々の部品になっていることが分かります。

3. 考え方の違い

比べる点WinFormsUnity
部品の正体Button という完成品GameObject + 複数のComponent
機能の持たせ方クラスに最初から入っているComponentを追加する
文字Text プロパティ子のGameObject(TextMeshPro)
機能の分かれ方Button クラスにまとまっている複数のComponentに分かれている

ここで大切なのは、「Button → GameObject」と単純に置き換えて覚えないことです。

GameObjectそのものは、機能を入れておく「土台」のような存在です。GameObjectを作ると、位置・回転・大きさを表す Transform Componentが必ず付きます。そこに必要なComponentを追加することで、「表示する」「当たり判定を持つ」「物理的に動く」といった機能を持たせていきます。

GameObjectは土台で、「何ができるか」はComponentで決まる。

これがUnityの基本の考え方です。

4. 3Dオブジェクトでも同じ

ボタン以外でも確かめてみましょう。Hierarchyの「+」から 3D Object > Cube を作ります。

Cubeも「立方体」という完成品ではなく、Componentの組み合わせでできています。

ここで Inspector の Add Component から Rigidbody を追加して、再生してみてください。Cubeが落ちていきます。

Componentを1つ足しただけで、「重力で落ちる」という機能が増えました。これが、Unityで「できること」を作っていく方法です。

5. コードで比べてみよう

今度は、「オブジェクトを作って機能を持たせる」という流れを、Unityのコードでも見てみましょう。

using UnityEngine;

public class CubeMaker : MonoBehaviour
{
    void Start()
    {
        // 土台と基本のComponentを作る
        // (Transform・Mesh Filter・Mesh Renderer・Box Colliderが付く)
        GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube.name = "FallingCube";
        cube.transform.position = new Vector3(0, 3, 0);

        // 機能を追加する
        cube.AddComponent<Rigidbody>();
    }
}

空のGameObjectにこのスクリプトを付けて再生すると、Cubeが作られて落ちていきます。

同じものを対応させるのではなく、作り方の流れを比べると、次のようになります。

ボタンのクリックはどうなる?

WinFormsの Click += にあたるものも、Unityのボタンにあります。

using UnityEngine;
using UnityEngine.UI;

public class ButtonTest : MonoBehaviour
{
    [SerializeField] private Button button;

    void Start()
    {
        button.onClick.AddListener(OnButtonClick);   // WinFormsの Click += にあたる
    }

    private void OnButtonClick()
    {
        Debug.Log("押されました");
    }
}

ここで button の型になっている Button は、GameObjectではなく、GameObjectに付いている「Button」Componentのことです。クリックの機能はComponentが持っているので、そこに処理を登録します。

(クリック時の処理は、InspectorのButtonの「On Click ()」欄から登録することもできます。)

6. なぜ new PlayerController() と書かないのか

GameObjectは new GameObject() で作れます。では、自分で作ったスクリプトはどうでしょうか。

PlayerController pc = new PlayerController();   // ✕ これはしない

Unityで自分が作るスクリプト(MonoBehaviour を継承したクラス)も、Componentの一種です。Componentは必ずどれかのGameObjectに付いている必要があり、単独では存在できません。

そのため、new で作るのではなく、「このGameObjectに付けてください」とUnityに頼みます。

player.AddComponent<PlayerController>();   // ○ コードで付ける

Inspectorでスクリプトをドラッグ&ドロップしたり、Add Componentで追加したりする操作は、これと同じことを画面から行っているのです。

実際に new で作ろうとすると、Unityはコンソールに警告を出します。

7. 画面とファイルの対応

前回、GitHub Desktopで「デザイナーでボタンを置くと Form1.Designer.cs が変わる」ことを確認しました。Unityでも同じように対応させることができます。

WinFormsUnity
フォームデザイナーSceneビュー
プロパティウィンドウInspector
Formの中のControl(親子関係)Hierarchy(親子関係)
Form1.Designer.csシーンファイル(.unity

UnityでComponentを追加してシーンを保存すると、シーンファイルが変わります。シーンファイルがテキスト形式で保存されていれば(Project Settings > Editor の Asset Serialization が「Force Text」の場合)、GitHub Desktopで差分を見ることで、追加したComponentの情報が記録されていることを確認できます。

WinFormsでは「画面の操作がC#のコードになる」、Unityでは「画面の操作がシーンファイルのデータになる」という違いはあります。ただ、「画面で行った操作は、どこかのファイルに記録されている」という点は同じです。

8. 補足:継承で作るか、組み合わせで作るか

WinFormsの Button は、次のように継承で機能を積み上げて作られています。

一方、UnityのButtonは、Componentの組み合わせでできていました。

この「継承で作るか、組み合わせで作るか」という違いは、設計の考え方にもつながります。詳しくは継承の次に学びたい「コンポジション」で扱っています。

(ちなみにWinFormsにも Component という名前のクラスがありますが、UnityのComponentとは別物です。名前が同じなので注意しましょう。)

まとめ

  • WinFormsでは、機能がそろった完成品(Button)を new で作り、Controls.Add() で追加した
  • Unityでは、GameObjectという土台にComponentを組み合わせて「できること」を作る
  • GameObjectには必ず Transform が付き、それ以外の機能は必要なComponentを追加して持たせる
  • 自作スクリプトもComponentなので、new ではなく AddComponent<T>() やInspectorで付ける
  • 画面での操作がファイルに記録される点は、WinFormsもUnityも同じ

次は、Unityで使うC#が普通のC#とどう違うのかを見ていきましょう。

Unityで使うC#の特徴 ― 普通のC#と何が違うのか

その後は、次の順で進むと理解がつながります。

  1. C#スクリプトの作成とアタッチ(MonoBehaviourの基本)
  2. C#だけで「Unityの中身」を理解する
  3. 継承の次に学びたい「コンポジション」

確認問題

問題1. WinFormsの Button とUnityのButtonの違いを、「完成品」「組み合わせ」という言葉を使って説明してください。

解答例を見る

WinFormsの Button は、表示・文字・位置・クリックの機能が最初からそろった「完成品」で、new Button() で作って Controls.Add() でフォームに追加します。UnityのButtonは、GameObjectという土台に ImageButton などのComponentを「組み合わせ」たもので、文字も子のGameObject(TextMeshPro)として分かれています。

→ 見直すなら「1. WinFormsのおさらい」〜「3. 考え方の違い」

問題2. 空のGameObjectを作っただけでは、画面に何も表示されません。なぜでしょうか。

解答例を見る

空のGameObjectには Transform(位置・回転・大きさ)しか付いておらず、見た目を表示するためのComponentが付いていないからです。

→ 見直すなら「3. 考え方の違い」

問題3. Cubeに重力で落ちる機能を持たせるには、何をすればよいですか。

解答例を見る

Rigidbody Componentを追加します。InspectorのAdd Componentから追加するか、コードで AddComponent<Rigidbody>() と書きます。

→ 見直すなら「4. 3Dオブジェクトでも同じ」「5. コードで比べてみよう」

問題4. new PlayerController() と書かない理由を説明してください。

解答例を見る

MonoBehaviour を継承した自作スクリプトはComponentの一種で、GameObjectに付いていないと存在できないからです。new ではなく、AddComponent<PlayerController>() やInspectorでGameObjectに付けます。

→ 見直すなら「6. なぜ new PlayerController() と書かないのか」

訪問数 9 回, 今日の訪問数 10回

広告