www.shibuya24.info ↑↑2016年版はコチラ↑↑
少し視点を変えてみた記事を投稿しました。
環境
- Unity5.1.2
- 環境
- 1. Find系関数は極力使わない
- 2.Prefab名とコンポーネント名は出来る限り同じにする
- 3.SendMessageは使わない
- 4.Shurikenを動的生成するときはEmissionタイプに気をつける
- 5.Updateで非同期処理をしない
- 6.Consoleは色分けすると便利
- 7.Resourcesフォルダには最低限のものを入れる
- まとめ
そろそろUnityを触りだして1年半。今までのUnity開発経験を元に気をつけておきたいことを7つまとめてみました。
1. Find系関数は極力使わない
Unityにはオブジェクトを探す関数がいくつか提供されています。
TransformクラスのFind系関数
- UnityEngine.Transform.Find
- UnityEngine.Transform.FindChild
まずこの2つの関数は、GameObjectの名前 (nameプロパティ、またはパス) を引数にして、Transformコンポーネントを取得します。
transform.Find ("Hoge/Piyo");
ゲーム開発中またはリリース後のデザイン変更における GameObject同士の階層変更 はよく発生する作業で、ソースコードで相対パスを指定をし、Transformコンポーネントを取得するのは非常にリスキーだと私は考えています。
なぜならGameObject階層を変更する度にソースコードも変更が必要になるからです。
またパス指定の方法だと、Unityエディタとソースコードを行ったり来たりしなければ内容を追うことは出来ず、とても非効率です。
GameObjectクラスのFind系関数
- GameObject.Find
- GameObject.FindWithTag;
- GameObject.FindGameObjectWithTag;
- GameObject.FindGameObjectsWithTag;
こちらのGameObjectクラスメソッドのFind系関数も同様、文字列指定でGameObjectを検索します。
先のTransformクラスの説明と重複しますが、GameObject名は開発中・運用中どちらも変更がよく入ります。
その度にソースコードを更新し、テストをするという非効率な状態になってしまうため、出来る限り使いたくありません。
解決案としては、public変数、もしくはSerializeFieldアトリビュートをつけた変数を定義し、オブジェクトを予めアタッチしておくのが良いと思います。
メリットはアタッチしたGameObjectの階層構造が変わったとしても、参照が切れず、追いかけてくれるということです。
これにより、View周りがより柔軟に対応できるようになります。
★この方法には更にメリットがあります。
[SerializeField] private Transform _hoge; [SerializeField] private GameObject _foo;
このようにFindを使わず SerializeField アトリビュートで定義しておけば、_hoge、_foo変数が再生直後からnullではないということが 後から見返した時にわかりやすくなります。
ObjectクラスのFind系関数
- UnityEngine.Object.FindObjectOfType<T>
- UnityEngine.Object.FindObjectsOfType<T>
最後にこの2つ。
すでにUnityのヒエラルキーに指定したコンポーネントが存在すれば、それを取得できる便利な関数です。文字列指定ではない分、タイポなどは起きづらいので、まだ使えます。しかしヒエラルキー全てを走査するという非常に重い処理ですので、あまり頻繁に使いたくありません。一度取得したらキャッシュするなど工夫をすれば便利な関数だと思います。
2.Prefab名とコンポーネント名は出来る限り同じにする
このコンポーネントはどのPrefabにアタッチされているのか、という状況はよく起きます。
こんな時Prefab名とコンポーネント名が全く違っていたら、非常に探しづらいです。Projectブラウザに検索窓がありますが、こちらにコンポーネント名で検索しても、それがアタッチされたPrefabは引っかかりません。ここはUnity側に改善してもらいたい点でもあります。
3.SendMessageは使わない
使い方次第ではとても便利なメソッドですが、ルールが定まっていないとカオスになります。
SendMessageは引数に文字列で実行したいメソッド名を渡します。
SendMessage ("foo");
参照ではなく文字列を渡していることが厄介の元で、fooメソッドをどこから呼んでいるのか、たどり着くのが非常に大変になり、ソースコードを読む難易度が一気に跳ね上がります。
SendMessageで全文検索し、fooを使っているかどうかを調べるくらいしか方法はありません。
だったら、fooメソッドを持つ参照を予め取得して実行したほうが効率的だと考えます。
特に複数人で開発している時には最初にルールを作っておかないと事故の元です。
4.Shurikenを動的生成するときはEmissionタイプに気をつける
ShurikenのEmissionタイプには、Time と Distance が存在します。Timeは、各フレームに排出するパーティクルの数を指定しますが、Distance にするとShuriken自体が移動する度にパーティクルが排出されます。(デフォルトTimeです)
生成して使い終わったら削除するやり方ならどちらでも問題ありません。
キャッシュして削除せず再利用する場合にDistanceだと問題が発生します。
なぜなら、DistanceのShurikenを生成して、使い終わった座標が仮に(0, 10, 0)とし、その後、(2, 3, 5)の座標から使用したいとすると、(0, 10, 0) 〜 (2, 3, 5)までの移動時のパーティクルが表示されてしまいます。
この問題の解決方法が分からず、DistanceのShurikenキャッシュを断念しています。だれか、解決方法を知っている方、共有していただけると嬉しいです。
5.Updateで非同期処理をしない
Updateは、Startメソッド実行後、毎ターン実行されるメソッドです。このメソッドで非同期処理をするとどうしても、インスタンス変数が増え、一つの処理に対して、記述が分散しまう場合が多いです。
コルーチンが使えるならコルーチンで対応したほうが良いと考えます。また、最近はUniRxという選択肢もありますので、出来る限りUpdateで非同期処理はしない方が良いと考えます。
最近非同期周りはUniRxで処理したほうが良いのではないかと検証しています。(勉強中です)
6.Consoleは色分けすると便利
Unityのコンソールには色付けすることが出来ます。
Debug.Log ("<color=yellow>黄色のログ</color>"); Debug.Log ("<color=#0033ff>カラーコードのログ</color>");
こんな風にコンソール出力すると、こうなります。
通信系の処理はピンク、ゲームループ処理は黄色など、各機能でログの色を分けると処理の内容、流れを理解しやすくなると思います。
7.Resourcesフォルダには最低限のものを入れる
最後にResourcesフォルダにファイルを沢山入れると、スプラッシュ画面 (起動画面) のロードが時間かかります。極力Resourcesフォルダに何も入れないようにしたいところです。
まとめ
以上7つ紹介してきました。どれも開発初期からやっておかないと、後から変更しようとすると結構時間が取られそうな内容だったのではないでしょうか。最初から設計、ルールをある程度明確化しておくことで、運用を含めた開発の効率化につながるのではないかなと思います。