退廃的ソースコードコンテナ

ゲームシステムを組むのが好きな人の備忘録

【Unity】システム作るならオーバーロードを有効活用したい

こんにちは。

ゲーム作ってたら、いつの間にか裏方のシステム周りばっかりを担当することになっていた人間です。

 

ところで、Unity標準として用意されている関数を使っていたら、こんなウィンドウが現れたのを見たことがないですか?

 

 

……これ自体は何の変哲もない関数の説明なのですが、気になるのは左側の1/2です。

 

では、そのウィンドウをクリックしてみてください。



なんか説明が変わった!?

 

……そう、この不思議の正体が関数のオーバーロードです。

実は、オーバーロードが存在するか確かめる方法は他にもあります

 

関数にマウスを合わせると……

 

+1 オーバーロードって表示される!!!

 

……これ自体は何度も見たことがありますよね。

問題は、+1 オーバーロードが何なのかって話です。

 

実はこれ、Debug.Log関数には2件の定義が存在する、という意味です。

現在表示している定義に加えてもう1件あるので、+1という表記なのです。

 

それでは、オーバーロードが何なのかと使い方を説明していきます。

よければ、最後までお読みください。

 

目次

 

そもそもオーバーロードとは?

C#の機能としてのオーバーロード

Unityでのゲーム制作で使われているオーバーロードは、元はC#の機能として用意されているものです。もっとも、C#の派生形(ご先祖様?)言語であるC++Javaにも用意されている機能で、C#独自のものではありません。

 

オーバーロードの主な機能としては、引数を変えることで関数の型や処理を変えることができるというもの。あまり深いところまで突っ込むと沼から帰ってこれなくなるので、今はこれくらいの認識で大丈夫です。

 

同じ名前だけど中身が違うやつがいる!!!

 

はい。では、その同じ名前のやつをどうやって判別するのか、それが引数の違いです。

 

以下のプログラムをご覧ください

同じ名前の関数だと構文エラーが出る

同じ名前の関数だとエラーが出てしまう、これはコピペミスなどでありがちです。

 

では、次にこちらのプログラムをご覧ください

定義を変えても構文エラーが出てしまう

関数定義(関数の中身)を変えても、構文エラーが出てしまいます

これは何故なのかというと、関数を呼び出す際に2つある関数のうち、どちらを呼び出せば良いのか分からないからです。

 

では、どうやって区別をつければいいのか。

以下のプログラムをご覧ください

なんと、ついにエラーが出なくなった

ついに構文エラーが出なくなったぞ!!!

 

何故かというと、引数を増やして同じ名前の関数でも区別がつくようになったからです。

これで、定義の違う関数が、同じ名前で共存できるようになりましたね。

めでたしめでたし。

 

オーバーロードのメリット

ここまで読んできたの中には、こんなことを思い浮かべた方もいらっしゃるかと思われます。

 

「いや、最初から関数名変えればいいだけじゃん」

 

うーん、それはごもっともな意見すぎて反論の余地がありません。

では、実際にそれでプログラムを打ってみましょう

何のエラーもでない

……先程のエラーが出ていたプログラムの関数に2を追加して別の名前に変えただけですが、見事にエラーが出ません

それもそのはず、HelloWorldHelloWorld2は名前の違う全く別の関数です。ちょっと名前が似ているだけの他人です。

 

では、何故にしてオーバーロードなんて機能が用意されているのか

それは、似たような機能を持つ同じ関数の判別がしやすいからです。

 

「似たようなランダムな値を返す関数なんだけど、標準的なものとカスタマイズしたものと二種類用意したい」、そんなときにオーバーロードならば関数を一括りにできます。名前が別の関数ならもう一つの関数も調べて使い方を見ないといけないのに対して、オーバーロードは一つの関数でパターン違いも対応できます。便利ですね!

 

……ちょっと少しゴリ押し気味になってしまいましたが、オーバーロードのメリットは

 

一つの関数で何パターンにも対応できるから使いやすい

 

と覚えてみてください。

 

オーバーロードの使い方

よく使う関数で役立つオーバーロード

今回は、以前の記事で使用したサウンドマネージャーを例に解説します。

 

islingtonsystem.hatenablog.jp

 

SoundManager.cssのうち、オーバーロードを使用している箇所がこちら。

同じ名前のPlaySE関数が3つ存在している

長いので関数定義は省略しましたが、同じ名前の関数が3つ存在しています。

これらはどのように使い分けているのでしょうか?

 

詳しいことは元の記事に書いてあるので省きます(宣伝)が、一番上の関数は処理内容を自分のクラス内で自己完結、二番目の関数は処理の内容を引数渡しで返すという明確な違いがあります。三番目はstring型(文字列)ではなく数字で指定するという形の関数です。

 

この3つはどれも同じ、Unity上で音楽データを再生するための関数で役割は同じです。しかし、この3つを別の名前の関数として扱ったらどうでしょうか?状況に応じて、3つの関数の名前を思い出して使い分けること自体が面倒です。

 

しかし、オーバーロードを使えば「音楽を再生するときはPlaySEを使ってね!」の一言で済みます。便利ですね、親切設計ですね。

 

また、Visual Studioなどエディター上では、関数の説明をオーバーロードされている関数の数だけ表示することができます。つまり、ドキュメントコメントで引数違いの関数の説明を書いておけば、本当に「これ使ってね!」で完結してしまいます。

 

システム系統のクラスはオーバーロードが有効

さて、ここまででオーバーロードの良さは説明してきましたが、実際どんな場所で使えるのでしょうか?

自分が思うのは、サウンド管理やエフェクト管理などのシステム系統のクラスです。

 

システム系統のクラスはよく使うため様々なニーズが生まれますが、できるだけ単純明快で分かりやすい設計にしたいものです。そんなときに有効なのが、オーバーロード

 

引数の違いで違う処理ができるため、一つの関数でありながら複数のケースに対応できる。まさに、システム系統のクラスにとっては一石二鳥の展開です。

 

先ほど紹介したサウンドマネージャーのように、場面に応じて引数を変えることで細かい調整を行うことができます。つまり、分かりやすさと対応力が求められるシステム系統の開発に際しては、オーバーロードで機能を絞りつつ対応力を上げることが重要なのです。

 

オーバーロードは型だって変えられる

int型とfloat型の同じ関数が存在する

オーバーロードの特徴として、戻り値の型も変えることが可能なことが挙げられます。

int型string型(文字列)のような役割が大きく違う型の違いは混乱の元となるのでおすすめはしませんが、int型float型や、vector2型vector3型のような役割が似た型の場合、引数に応じて戻り値を変更することが有用かもしれません。

 

積極的に使える機能ではないかもしれませんが、オプションの一つとして用意しておくと良いかもしれません。

 

オーバーロードの注意点

ここまでオーバーロードの便利なポイントばかりを紹介してきましたが、主な注意点としては一つ挙げられます。それは大幅な処理の違いは混乱を招くということです。

 

例えば、同じサウンド再生の関数なのに、音楽の再生のみの関数と、音楽の再生とともにプレイヤーにダメージを与える関数が混在していたらどうなるでしょうか?どちらを使えば良いのか分からず、誤作動や混乱の元となりかねません。

 

このような自体を避けるためには、オーバーロードは似た機能や派生形に留めておくことが重要です。その関数本来の目的である処理と、イレギュラーに対応した派生形の処理を用意しておく程度に留めるのが、一番分かりやすいオーバーロードの使い方だと思います。

 

また、コメントはきちんと書きましょう何が何だか分からない派生形の関数が用意されているのは設計者以外にとっては恐怖以外の何物でもないため、ドキュメントコメントでコメントを書くか、リファレンスを別途用意することが重要です。設計を忘れた自分が焦る羽目に陥ることも防げるため、利用者のことを考えてコメントを残すことは大切です。

 

最後に

最初は、オーバーロードの使いやすさについて軽く語ろうかと思って書き始めた記事ですが、熱く語るうちに長くなってしまいました。

最後まで読んでここまで来た方ならオーバーロードの特徴も理解して頂けたかと思いますが、実際のゲームプログラミングでも有効活用していただければ、大変嬉しい限りに思います。

 

最後までお読み頂き、ありがとうございました。

よければ、当ブログの他の記事もお読みください。