こんにちは、Unityエンジニアのオオバです。

誰しも初心者の頃は 「ヤバい」 実装してしまいがちです。

別にこれは悪いことではなく、 成長過程の中で正規ルート と言っても過言ではありません。間違いを体験しながら少しずつ強くなっていくのがプログラミングだと思っています。

オオバも同様で、Unityを始めた頃は何が何やら分からずヤバい実装をしていたものです。

流石に今それをしてしまうと、おそらく仕事がなくなるでしょう(笑)。

ということで本記事ではUnity初心者がやってしまいがちなヤバい実装を8つお届けします。

いろんな実装方法を試して、自分の中で正解を見出していくで全然問題ないのですが、いかんせん時間がかかるのがネックですよね。さっさと答えを知って先に進めたらどんなに良いかと思う人も多いはず。

これから紹介するヤバい実装は 一見大丈夫そうに見えるかも知れませんが、実は本当にヤバい実装 です。ヤバい理由もセットで紹介していきます。もし同じような設計・実装をしていたらぜひ見返してみて、リスクはないかどうかを見直す良い機会になるかも知れません。

では早速紹介していきます。

👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

ヤバい実装1.Find系を使いまくっている

Find系関数とは FindFirstObjectOfTypeFindAnyObjectOfType といったHierarchy内のオブジェクトを全て検索する処理のこと。

Hierarchy内を全検索して便利ですが 負荷は高い です。

しかもFind系関数を Update関数 など高頻度呼び出すとパフォーマンスは大きく低下。しかもヒープメモリも確保してしまうため、GCも起きやすくゲームのカクつきに繋がり ゲーム体験が大きく損ないます。

Find系関数は使うとしてもシーンロード後の一発目くらいに抑えておくのが無難です。

また改善策は必要な参照はあらかじめ SerializeField や Inspector 経由で設定しておくこと。事前にキャッシュしておけば高速にアクセスできるのでぜひ意識してみてください。

ヤバい実装2.Get系関数を使いまくっている

Get系関数、具体的には GetComponentGetComponentInChildren などです。Get系関数も使いすぎに要注意です。特に Update 内で毎回呼び出すのは非効率。比較的負荷のかかる処理なのでパフォーマンスが低下します。

パフォーマンス以上に良くないのが オブジェクトの構造変化に激弱 だということです。

GameObjectの階層変化、アタッチするコンポーネントの場所が変わるとGet系関数を使っている箇所は全滅します。つまり 防御力の低い設計 なのです。

より安全でメンテナンスしやすいのは SerializeField を使いInspectorで参照をあらかじめ指定しておくこと。

これにより コードの堅牢性 が増し階層変更に強くなります。

ヤバい実装3.SendMessageを使いまくっている

SendMessage は文字列でメソッドを呼び出せる特殊な関数。一見めちゃくちゃ便利そうに見えますが罠です。

なぜならSendMessageはタイピングミス、メソッド名の変更に非常に弱いです。

なぜなら次のようにSendMessageの引数に文字列を指定しているため、エディタのリファクタリング機能が使えません。

SendMessage("TestMethod");  

文字列ではなく実メソッドにアクセスしていたらリネームは簡単なのです。

つまり SendMessageリファクタリングの大敵

しかもSendMessageは呼び出し先が追いづらいです。Visual StudioやRiderなどのコードエディタは任意の関数がどこから呼び出されているかリストアップする機能があります。

しかし文字列指定しているためこの便利機能が使えません。この関数は使っているのか、使っていないのか分からない。わからないから使っていないのに放置せざるを得ない。どんどんコードが汚くなる。

このような負の連鎖に入りがちです。

「基本的にSendMessageは使わない」 をオススメします。

ヤバい実装4.アニメーション全てをAnimationClipで実装している

AnimationClipは Unityが提供するアニメーションの仕組み。

Unityが提供しているから安心!何でもかんでもAnimationClipでアニメーションを作成すると後で 地獄を見るかも 知れません。

というのもAnimationClipには大きな弱点があります。

それは GameObjectの階層変化に激弱 だということです。

AnimationClipで操作している対象のGameObjectの階層が変わると動作しなくなります。しかもGameObjectの名前を変えるのも禁止。全滅します。

ちなみに ゲーム開発中はGameObjectの階層変更ってしょっちゅう発生 します。つまりAnimationClipでアニメーションを作っても開発の過程で動かなくなりゴミとなってしまうのです。

もちろんAnimationClipは非常に便利。 大事なのは使い分け です。

という使い分けです。

1点ものの場合は、修正が入ったとしてもそこまで影響範囲は大きくありません。しかし、再利用が想定されるアニメーションにAnimationClipを使うと全てのオブジェクトに対して修正をいれる必要が出てくる可能性があります。

この辺りは以前、勉強会で発表したこちらの記事を参考にしてみてください。

Unity初心者がやりがちなヤバい実装8選_0
LT版クリエーターとUnityエンジニアの狭間でUIアニメーションを設計する3つのTips

ちなみにスクリプトでアニメーションを実装する際は DOTweenがおすすめ 。DOTweenは以前オオバが執筆したDOTweenの教科書が参考になります。書籍全体は有料ですが、 無料部分を読むだけでDOTweenが使えるようになります。 ぜひご一読を。

Unity初心者がやりがちなヤバい実装8選_1

DOTweenの教科書

ヤバい実装5.あまり理解していないのに外部ライブラリを使っている

アセットストアやGitHubには便利なUnityのライブラリが数多くあります。それらを使うことで大きく開発スピードが上がるのも確かです。

しかし仕組みをあまり理解せずに使うと後で大変なことになります。

というのは実装内容を知らないがために不具合が起きた際、 原因が分からず詰みます。

オオバが過去に実際に経験した失敗は以下。

使用するライブラリがゲームの一部であれば、捨てて作り直せばよいですが 根幹システムに根付く場合はマジでヤバイ です。

「捨てる = 作り直し」

こうならないためにも、ゲームの根幹に使うライブラリはしっかり勉強、検証したうえで導入することをオススメします。

ちなみにオオバが必ず新規でゲームを作る時に必ず導入するアセットをYouTubeで紹介しているのでぜひ参考にしてみてください。

Unity初心者がやりがちなヤバい実装8選_2
Unityプロジェクトを新規作成したら必ず導入すべきアセット6選

ヤバい実装6.テクスチャを圧縮していない

Unity初心者はテクスチャ、つまり画像の メモリに対するインパクトを軽視しがち です。

画像はメモリをガッツリ使用 します。メモリはよく机の広さと例えられますが、この机からモノがあふれるとアプリはクラッシュします。

つまり、 机(メモリ)使用量を節約しながらゲームを作る必要がある ということです。

ここでテクスチャの話に戻りますが、通常テクスチャは圧縮して利用します。

「圧縮すると画像が汚くなるんじゃない?」

って思いますよね。結論汚くなります。しかし実機でみるとほぼ気づかないレベルのクオリティです。

Unity初心者がやりがちなヤバい実装8選_3
こちらは非圧縮では9MBメモリを占有する画像です。

Unity初心者がやりがちなヤバい実装8選_4
圧縮すると1MBと1/9まで削減できます。

このようにテクスチャの圧縮は必須です。今のUnityはデフォルト状態で圧縮されるようになっていますが、確実に圧縮がかかっているかチェックすることをオススメします。

ヤバい実装7.シングルトンの乱発

シングルトンを乱発するとソースコードの健全度は下がっていきます。シングルトンとは、プログラミングにおけるデザインパターンの1つ 「シングルトンパターン」 の事。

シングルトンとは 「どこからでもアクセスできるただ1つのオブジェクト」 という設計で一見めちゃくちゃ便利ですが、便利なものには 「毒」 がつきもの。

便利だと思ってシングルトンを使いすぎると、あらゆる処理がグローバルにアクセス可能となります。

「どこからでもアクセスできた方が便利じゃない?」

と思うかも知れませんが 実は逆 です。どこからでもアクセスできるということは、 アクセスしてはいけない場所からもアクセスできてしまうということ。

料理に例えるなら、 「手を洗ってから料理を作るべきですが、手を洗わない状態でも料理を作れるようなもの」 です。

つまり処理の責任範囲が広がり、問題が起きた際に誰の責任なのかが分かりづらくなることが問題なのです。

食中毒が起きても誰の原因かたどりづらく、不具合の修正までに時間がかかってしまいます。

シングルトンを使うなとは全く思っていませんが、必要最小限にとどめることをオススメします。

ヤバい実装8.Instantiateしまくっている

実はUnity ObjectのInstantiateは恐ろしく負荷の高い処理です。C#ではたったの一行ですが Unity内部では様々な処理が行われています。

ゲームプレイ中、頻繁にInstantiateを繰り返すとヒープメモリを確保しまくり、GC(ガベージコレクション)が発生してフレームがカクつきます。

特に弾やエフェクトなど大量に発生するオブジェクトでやりがちです。

「Instantiateしなかったらどうすればよいの?」

って思いますよね。

対策は「オブジェクトプーリング」。あらかじめInstantiate(生成)しておき、使い回すことでGCの発生を抑え快適なプレイ感を維持できます。

「全くInstantiateしない」は流石にゲームづくりしづらいため、 最小限の利用に抑える というのがベストプラクティスです。

まとめ

今回は初心者がやりがちなUnityのヤバい実装8つを紹介してきました。

総じて言えることは 「大丈夫そうに見えて長期的に破綻するもの」 が多いことです。

ゲームは動かすだけなら簡単ですが、保守や拡張、クオリティを考えると必ず壁にぶつかります。安心してほしいのは ゲーム開発者全員がぶつかる壁 だということ。あなただけではありません。

この記事をきっかけに、もし自分のコードが今回紹介した事例に当てはまっていないか一度見直してみるのが良いかも知れません。

そのままでも大丈夫そうならOKですし、長期目線的にリスクになりそうなら修正が必要です。

自分が書いたコードを時間経過した後に見直すと、粗が見えてくることは多いです。なぜなら 時間経過であなたが成長しているから です。

つまり過去の自分のコードに粗が見えるようになったということは非常に良いことなんですよね。

「粗を見つける→修正する→時間経過→再び粗を見つけ修正する」

この繰り返しが中級者・上級者への第一歩になるとオオバは思っています。

一緒に頑張っていきましょう。

Unityオブジェクトの描画順の制御って難しいですよね。
この度、Unityの描画順を体系的に学べる「Unity描画順の教科書」を執筆しました。

Unityの描画順を基礎から学びたい方はぜひ確認してみてください!
Unity描画順の教科書

最後まで読んでいただきありがとうございました!
すばらしいゲーム開発ライフをお過ごしください。

オススメ記事