こんにちは。迷走中の一回生、TZと申します。UnityというゲームエンジンでC#という言語を使ってゲームを作っています。今回はよみやすいコードが書きたいということでなんかやります。

 クラス設計とか大枠のことではなく、名前付けとか関数の中でどうするかという、細かなところをちまちまやっていきます。一応C#の例示が付きます。よろしくどうぞ。

 なお、私はUnityを通さずにC#書いたことがないので今回もUnityを通して書きます。ちょっと変なとこがあってもスルーしてください。

わかりやすい名前つけよう

 ある人は言いました。『名前なんてどうでもいい』と。変数名a,b,c,d……。関数名ProcessA,ProcessB……。みたいな名前、つけていませんか??

 辞めましょう。

 絶対にわからなくなります。本当にわからなくなります。自分ひとりで書いてるから大丈夫?

 三日前の自分は他人です。

 見ただけで用途が分かるクラス名、変数名、関数名を付けることと、その用途を守ることは非常に大事です。変数aが毎秒引かれているより、変数timerが毎秒引かれている方がわかりやすいし、int ProcessA()なんて謎な関数の返り値を変数に代入するより、int GetAge()として年齢が代入されたんだな、ってわかる方がよくないですか?

 また、名前を付けるときに適切な名前を付けようと考えることで、無駄な機能の抱え込みも防げます。クラス名を付けるとき、GameManager,PlayerManagerとか曖昧な名前や、PlayerAttackAndMoveGetItemみたいな明らか機能の盛り込みすぎな名前を付けそうになった時、踏みとどまることで神クラスの生成を防げます。名前に適さない機能は入れなければよいのです。名前を裏切ってはいけません。Get●●関数内部で変数を返す以外のことをするとか絶対だめです。

マジックナンバーやめよう

if(age > 18){}

if(key > 3){}

 あー! やめてくださいお客様! マジックナンバーはやめてくださいお客様!

 マジックナンバーとは、プログラム中に直書きされた何に使っているのかわからない=名前のついていない数字のことを指します。上の例だと、18、3が該当します。

 マジックナンバーがなぜいけないのか? 理由は二つあります。

 まず、読んでいて意味が分からないことです。まだ上記の例では年齢、鍵と比べていることだけはわかるのでましですが、年齢が18歳以上ってどういうこと? 鍵が3つ以上ってどういうこと? という無駄な思考が発生することは防げません。ただでさえコードを読むことは面倒なのに、こんなところで負担を増すわけにはいきません。

if(age > adultAge){}

if(key > needKeyNum){}

 こう書けば、成人用の処理が続くこと、必要な鍵を集めきっているときの処理が続くことが一目でわかります。名前は偉大です。

 また、マジックナンバーは修正がしにくいです。これはクラス設計でも同じ考え方をしますが、同じようなものを何回も書くのはよろしくないです。もし必要な鍵数が4つに増えたらどうしましょうか。key > 3のように、3と直書きされている回数分、すべてを4で書き直さねばなりません。優秀なエディタの置換機能を使えばよいとはいえ、無駄な作業であることは否めません。また、訂正忘れで泣きを見る可能性があります。

readonly int ADULT_AGE = 18;

readonly int NEED_KEY_NUM = 3;

とすれば、以降の訂正は一元化できます。これを定数といいますが、マジックナンバーはなるべく定数に置き換えた方がよいです。(ちなみに、C#の定数はconstとreadonlyで二種類あり、内部の処理が若干違います)

if文はよみやすく

 if文は魔物です。多重ネスト、読みづらい条件式、分岐に分岐を重ね長ったらしいSwitch文と同義になっているものまで。多種多様な禍々しさを見せつけてくれます。

  if文は頑張れば読みやすくなります。上記のまともな名前付け、定数を使えばそこそこましになりますが、他にも早期リターンと分割という手があります。

 早期リターンというのは、名前のとおり、if文チェーンで条件に合わないものから比較、returnでさっさと返してあげることです。最後まで残っていたら処理を実行します。分割は、全部一気にネストとかで処理せず、別のとこで判定した結果を使います。今回は分割して書きます。

 ということで、以下にプレイヤーがAボタンを押したとき、プレイヤーキャラが攻撃可能状態にあるか(攻撃してない、死んでいない、ジャンプしてない)を判定するif文があります。(だいぶ適当だけど許してください)

 入力判定、ジャンプ判定、生死判定、攻撃中判定で四重ループとなっています。これをいい感じに書き換えます。例示なのにUnityに依存していて(しかもレガシーコードで)申し訳ないですが、キー入力を行っているInput.KeyCodeの部分はUnity依存なのでノータッチです。

 ということで三重ネストになりました。まだ汚いですが、死んでいるか?ジャンプしているか?という判定自体に名前がついたので若干わかりやすくなりました。もうちょっと比較したい値が増えると、分割がより便利に感じると思います。(処理の直前に自分で比べるより、bool返してくれる関数呼び出した方がまだすっきりするので)

以上、力尽きたのでこの辺で終わります。名前と定数は特に大事だよって話でした。クラス設計とか書こうと思ったのですが、SOLID原則すべてクラス図書いてコードビフォーアフターすると間に合わないので来年書きます(たぶんきっと)

Twitterでフォローしよう

おすすめの記事