【C#/Unity】初心者キラーのジェネリックについて知る【ゲーム開発】
Unityでゲームを作る以上、いずれはC#を使うことになります。
最近ビジュアルスクリプティングツールの「Bolt」が無償化されたのでプログラミングなしでゲームを作ることもできますが、そのBoltでもC#の考え方を知っていると快適に動きを組めます。
この記事で紹介したいのは型をパラメータとして渡すことができるジェネリックの機能です。
初心者の頃はこのジェネリックに面食らって「あれ……? C#ハードル高いな?」と恐怖を抱いたのですが、ちゃんと調べてみれば非常に便利な機能で今となっては手放せない機能に。特にジェネリックリストの機能は非常に便利なので、恐怖を抱いたあとはぜひ調べてみるところまでやってみましょう。コワクナイヨーホントダヨー。
ジェネリックとは
「ジェネリック」とはgenerics: 包括的な、全体的な、一般的なといった意味の言葉。
そのまま読むとジェネリクスになりそうですが、C#を作ったMicrosoft社の日本語ドキュメントで「ジェネリック」とされているのでこのように呼ぶことが多いです。
「ジェネリック」という言葉だけ聞いてピンとこない方でも、下のコードを見ればイメージがわくかもしれません。
GetComponent<Rigidbody>()の<>で囲んでいる部分はRigidbodyでもいいですし、AudioSourceでもいいですし、自分で作成したスクリプトのクラス名を入れることもあります。<>で囲んだ部分は型をパラメータとして渡していて、この例ではRigidbodyという型をパラメータとして渡しています。
型をパラメータとして渡す
型をパラメータとして渡すというのは最初はイメージしにくいかもしれません。私も最初はなかなかイメージが湧きませんでした。
そんな時は、メソッドに引数を渡すことを思い出してもらえれば良いかと思います。
例えば足し算をするメソッドを作って、引数で足し合わせる値を渡すとします。(このメソッドを作る意味があるかは置いといてください)
メソッドの中身では「1 + 2」のように値を直書きすることはなくて、引数として指定した変数を「a + b」のように足し合わせます。こうすることで、同じメソッドを使いながら渡す値によらず足し合わせる機能を使えます。
同じようにC#では「int」や「float」などの型をパラメータとして渡すことができるんです。
例えば複数の値をまとめたリストを作りたい時には、
のように<>で囲んだ中に型を指定することで同じようにリストを作ることができるんです。様々なクラスに対応した包括的な機能を提供するのでジェネリックと呼ばれています。
上で紹介したGetComponent<Rigidbody>()の例であれば、<>の中にコンポーネントの名前を入れることでGetComponentのメソッドを使い回すことができます。(コンポーネントも型)
ジェネリックなメソッドの場合、型パラメータを<T>と表すことが多いです。Tは「Type:型」の頭文字からきています。最初はこの<T>の書き方がよく分からなくて泣きそうになっていました<T><T>
ジェネリックの機能のメリット
ジェネリックの機能があることで、メソッドを抽象化することができます。要は型に対応したメソッドを個別に作らなくていいんです。
もしこのジェネリックの機能がなかったら、GameObjectのクラスを拡張して型に対応するGetComponentのメソッドをひとつひとつ作る必要があるかもしれません。Rigidbodyへの参照を取得するメソッド、AudioSourceへの参照を取得するメソッド……と追加していくのは地獄のような作業になりそうですね。
Unity組み込みの型ならUnityさんがやってくれるでしょうが、自分で作ったスクリプトをGetComponentする機会も多いですから、自分でGameObjectのクラスを拡張して……となると初心者にはきついですよね。慣れていてもやりたくありません。
なので、ジェネリックなメソッドを用意することで、型を指定するだけで同じ動きのメソッドを用意することができるんです。
ジェネリックの型パラメータに制約をつける
また、自分でジェネリックなメソッドを作る場合は、以下のように制約をつけることもできます。
上のコードの「where」で指定されている部分が制約で、型パラメータとして渡されるT型の条件を設定することができます。この場合だと「IComparable」というインタフェースを持っている型を渡してね、という意味になります。
インタフェースには「このメソッドを実装してね🧡」という情報が入っているので、インタフェースを実装しているということは、そのインタフェースに記載したメソッドがあることが保証されていることになります。
上のコードでは「IComparable」というインタフェースを実装していることが条件になっていて、このインタフェースに含まれている「CompareTo」という比較用のメソッドが含まれている型で使えるジェネリックなメソッドになっています。だから、型が決まっていなくても「CompareTo」というメソッドを使うことができるんですね。
このように制約を決めておくことで、意図しない型が渡された時にコンパイルエラーを表示して、変な動きを防ぐことができます。
まずはジェネリックリストを使ってみよう
ジェネリックの制約まで紹介しましたが、最初からここまで使うことはほとんどないですし、自分でジェネリックのメソッドを作るよりも、まずは存在しているジェネリックのメソッドを使うだけで十分です。
まずはジェネリックの恩恵を一番に受けられるジェネリックリストを使ってみると良いでしょう。
ジェネリックリストは上でも少し触れましたが、下のコードのように型を指定するリストです。
int型であっても、float型であっても、なんならGameObject型であってもリストとして保持することができます。複数の値をまとめて保持したり操作したりしたい場面は多いので、これを使いこなせるようになるだけでもかなりC#のプログラミングが楽になると思います。
リストや配列については以下の記事でも触れているのでよかったらご参照くださいな。
参考文献
Microsoft社のジェネリックに関するドキュメントです。
C#についてなにか調べたい時には『未確認飛行C』さんへぜひ。めっちゃ勉強になります。
まとめ
初心者キラーとして(私の中で)名高いC#のジェネリックについての紹介でした。
恥ずかしい話ですが、初心者の頃にUnityのチュートリアルをやっていたらこのジェネリックの機能が当たり前に出てきて「いったい何なんだこれは……」とテンパってました。たしか「2D Roguelike」というひげのおじさんがゾンビの蔓延るダンションを食糧を拾いながら駆け抜けていくローグライクのゲームを作るチュートリアルだったかと思いますが、手順の中で<T>がついたメソッドを作っていてビビった記憶があります。
書き方が特殊なので最初は戸惑うかもしれませんが、自分で手を動かしてスクリプトを書いて実際にゲームを動かしてみるとジェネリックの良さを感じることができます。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【C#】Listと配列の違いも意識して使いこなそう【ゲーム開発】 2020.11.02
-
次の記事
【C#/Unity】Dictionaryでforeachを使うときに詰まったこと【ゲーム開発】 2020.11.04
コメントを書く