Unityのためのベストプラクティス集としては、
Unity開発に関する50のTips 〜ベストプラクティス〜
といったものが有名だろう。
これはUnity全体に関するTipsである。
またUnityのスクリプト言語であるc#に絞った場合、c#言語自体のTipsとしては、
"Effective C# 4.0"、"More Effective C#"が有名だろう。
しかしEffective C# 4.0はc#言語自体のTipsでありUnityのスクリプト言語であるところのc#とは若干異なる部分もあるので(c#のバージョン自体も異なるのだが、その部分は今回は除外する)、その点について思いついたことを書き連ねてみた。
以下にEffective C#のトピック。
##1.publicメンバの代わりに常にプロパティを使用する
UnityではUnityがシリアライズ可能な型を持つフィールドは、public指定、または適切なアトリビュート指定をすることによってシリアライズがおこなわれ、Inspecter上での編集が可能になる。
フィールドに対してpublic指定を使わないというEffective C#のプクティスに従うなら、それでもUnityのシリアライズはおこないたいだろうから、追加の指定が必要だ。
下に示すのはシリアライズをおこないInspecterに表示("Test"として)し、かつクラス外からは読み出しのみ可能なプロパティ"Test"。
[SerializeField]
bool m_Test = false;
public bool Test {get {return m_Test;}}
このような指定によって、プロパティ"Test"はUnityのInspecterからか同一クラス上からしか書き換えすることはできなくなるため、プログラムのバグ等によって不用意に破壊されることは無くなる。
また以下のようにアトリビュート指定を変更すればシリアライズはおこなわれるが、Inspecterには表示されない。
[SerializeField,HideInInspector]
bool m_Test = false;
public bool Test {get {return m_Test;}}
これは同一クラス上からしか書き換えすることはできない。
このようにUnityのフィールドはプログラムへの公開レベルに加えて、シリアライズするかどうか、UnityエディタのInspecterへの公開レベルを別途制御可能である。
##2.constよりもreadonlyを使用する
c#にはコンパイル時定数であるconstと、実行時定数であるreadonlyがある。
readonlyはコンストラクター、スタティックコンストラクター実行時には初期化可能である。
readonly List<int> a=new List<int>();
このように初期化されたフィールド"a"はreadonlyなため、実行中、決してnullになることは無いためnullをチェックする必要が無い(しかしリスト"a"自体は自由に書き換えて使用できる。readonlyなのはフィールド"a"なのであってそのポインタによって指し示されたリスト"a"は書き換え可能だからだ)。
ただしUnityではreadonlyなフィールドは決してUnityシリアライズされないため、意識せずに使用していると思ったような挙動にならないことがある。
またUnityEditor上では実行中でもホットリローディングによって、フィールドに対してコンストラクターが実行され初期化がおこなわれる可能性がある。
このためreadonlyなリストの中身は破壊されてしまい継続実行することはできなくなる。
##6.さまざまな同値性メソッドの関係を把握する
object.ReferenceEquals(someObject, null) の方が someObject == null より早い…が、盲目的に使うのは危険
にあるようにUnityは同値性メソッドは同値性メソッドを上書いているようだ。
##12.割り当て演算子よりもメンバ初期化子を使用すること
自動実装プロパティでは使えない。
しかしそもそも、Unityでは自動実装プロパティを使うよりも、明示的にバッキングストアを書いて、それをインスペクターに反映すべき。
なお、
int Test;
int test;
int _test;
int m_test;
いすれもがUnity Inspector上ではTestの表記となる。
##13.staticメンバは適切に初期化する
Unityではstaticメンバはシリアライズされないため、あまりうまく活用できない。
staticメンバはシングルトンのinstance内でのみ使う変数、readonlyな値型などにだけ使うのが好ましく、残りはUnityシングルトンのフィールドとして定義して、プロパティをstaticとして公開すべきたろう。
##20.値型は不変かつアトミックにすること
バッキングストアをreadonlyにしてコンストラクタで初期化をおこなう方法が紹介されているが。
UnityはreadonlyをInspectorに表示できないので、前述のプロパティとしてgetのみを定義する方法がいいだろう(ただし、これだとInspecterには表示できるが、同一クラス内からなら書き換えも可能になってしまうが)。
##33.親クラスの変更に応じる場合のみnew演算子を使用すること
UnityにおいてはAwake()、StartUp()、Update()関数を再定義するためにはnewを使うといいだろう。
new void Awake(){
base.Awake();
}
newを使わないと、基底クラスを書き換えてvirtual宣言を追加してやる必要があるし、これらの関数はユーザーが直接呼び出すこともないからEffective C#にあるような意図しない結果になることも無いからた。