[Unity] 友達に教えるために "自分はこう書いている" を伝えてみる

# 記事の概要
入門者の友人と一緒にコーディングをする機会が生まれたので、
「自分はこういうコーディングルール・コツを持っている」を記事にまとめてみる。


# 「自分の」コーディングルールの紹介
## 波括弧の開始位置
波括弧の始まり " { " は**行の末尾**に置く。新しい行は作らない。

コードが縦に伸びるとエディタで見づらくなるからだ。


```CSharp: 縦に短い
namespace Enemy {
  public class Enemy: MonoBehaviour {
    public void Awake() {
      Debug.Log("Hoge");
    }
  
    public void Update() {
      Debug.Log("huga");
    }
  }
}
```

```CSharp: 縦に長い
namespace Enemy 
{
  public class Enemy: MonoBehaviour 
  {
    public void Awake() 
    {
      Debug.Log("Hoge");
    }
  
    public void Update() 
    {
      Debug.Log("huga");
    }
  }
}
```


## インデントの作り方
インデントは**半角スペース2つ**で作る。スペース4つでも、Tabでもない。

コードが横に伸びるとエディタで見づらくなるからだ。


## 読めば分かるコメントを書かない
コードの内容をそのまま説明するコメントは基本的に必要ではない。

```C#: 不要なコメントの例
class Enemy: MonoBehaviour {
  // 敵のHP
  private int _hp;

  // HPを減らす処理
  public TakeDamage(int amount) {
    _hp -= amount;
  }
}
```


## bool型変数は true の条件が明確な名前を付ける
変数名の先頭に "is" または "can" を付ける。

そうすることで、フラグ用の変数と分かりやすく、かつ true の条件が明確になるからだ。

```C#: 良い例
public bool isShooting;

if (isShooting) {
  Debug.Log("hoge");
}
```

```C#: 悪い例
public bool shootingStatus;

// trueがどの状態か分からない
if (shootingStatus) {
  Debug.Log("hoge");
}


public bool shooting;

// あまり無いと思うが (is not) shooting でtrueになる可能性を捨てられない
if (shooting) {
  Debug.Log("hoge");
}
```


## 波括弧を省略しない
1行で終わる for や if などで波括弧を省略できる場合でも、波括弧は省略しない。

2行目を増やしたいときに手間であり、中途半端に使うと見づらくなるからだ。

```C#: 良い例
if (health < 0) {
  return;
}

for (int 1 = 0; i < 10; i++) {
  for (int j = 0; j < 5; j++) {
    Debug.Log("hoge")
  }
}
```

```C#: 悪い例
if (health < 0)
  return;  // この上に1行差し込めない

for (int 1 = 0; i < 10; i++) 
  for (int j = 0; j < 5; j++) {  // この上に1行差し込めない
    Debug.Log("hoge")
    Debug.Log("huga")
  }
```


## ローカル変数は var を使う
メソッド内でのみ使用する変数は、基本的に型定義を var で行う。

複雑な型情報を書くのは面倒であり、型情報はIDEの補足で足りるからだ。

```C#
public List<Dictionary<int, string>> GetItemStatusList() {
  return ...
} 

// 型情報をしっかり書くと↓
List<Dictionary<int, string>> itemStatusList = GetItemStatusList();

// var を使うと↓
var itemStatusList = GetItemStatusList();
```


## マジックナンバーを使わない
マジックナンバー、つまり「後から見返して意味の伝わらない数値や文字列など」を生み出さない。

変数として名前を付けましょうという話。

```C#:良い例
class Player: MonoBehaviour {
  private float _moveVectorX = 1f

  public void FixedUpdate() {
    transform.Translate(_moveVectorX, 0, 0);
  }
}
```

```C#:悪い例
class Player: MonoBehaviour {
  public void FixedUpdate() {
    transform.Translate(1, 0, 0);  // この1は何故1なのか分からない
  }
}
```


## [SerializeField] を public で代用しない
Inspectorに表示したい変数は[SerializeField]を付ける。これをpublicで代用しない。

[SerializeField]を書く手間を減らすよりも、アクセスレベルが変わることによるバグを生まない方がよほど重要だからだ。


## クラスのメンバ変数には _ を付ける
クラスのメンバ変数には、例外なく先頭に_を付ける。その変数がpublicでもprivateでも関係ない。

メンバ変数とローカル変数の区別ができるからだ。

```C#
class Enemy: MonoBehaviour {
  private int _hp;
  [SerializeField] private float _moveSpeed;

  public void TakeDamage(int damageAmount) {
    _hp -= damageAmount;
  }
}
```

## 他のクラスへの影響は、そのクラスにやってもらう
一つのアクションから自分以外のクラスに影響が出る場合、それらのクラスに関数を作り、それを呼ぶだけにする。

コードの見通しが良くなり、各クラスの責務・知っておく必要があることを明確にできるからだ。

```C#:良い例
class Enemy: MonoBehaviour {
  private int _hp = 100;

  public void TakeDamage(int damageAmount) {
    _hp -= damageAmount;

    if (_hp <= 0) {
      Destroy(this.gameObject)
    }
  }
}

class PlayerBullet: MonoBehaviour {
  private int _bulletDamage = 10;

  public void OnCollisionEnter(Collision collision){
    if (collision.gameObject.TryGetComponent<Enemy>(out hitEnemy)){
      // PlayerBulletクラスが知る必要があるのは、EnemyクラスのTakeDamageを呼べばいいという事だけ。
      hitEnemy.TakeDamage(_bulletDamage);
    }
  }
}
```

```C#:悪い例
class Enemy: MonoBehaviour {
  public int _hp = 100;
}

class PlayerBullet: MonoBehaviour {
  private int _bulletDamage = 10;

  public void OnCollisionEnter(Collision collision){
    if (collision.gameObject.TryGetComponent<Enemy>(out hitEnemy)){
      // PlayerBulletクラスが知る必要があるのは、Enemyクラスに_hp変数があり、_hpが0になったらDestroyする必要があるという事。
      hitEnemy._hp -= _bulletDamage;

      if (hitEnemy._hp <= 0) {
        Destroy(hitEnemy.gameObject);
      }
    }
  }
}
```


## 他クラスからメンバ変数を操作する際は、Getter, Setterを作る
前項に含まれる内容だが、他クラスからメンバ変数を操作する場合は必ずGetter, Setterを仲介する。

~~Getter, Setterはプロパティで書かず、普通の関数として定義する。~~
一周回ってプロパティでも良い気がしてきた。
※プロパティについて：https://qiita.com/No2DGameNoLife/items/2c42337d41db5d6b176d



# おわりに
Unityさん公式のスタイルガイドもあるので、興味があればこちらも見てほしい。
https://unity3d.jp/game/create-code-c-sharp-style-guide-e-book-unity-6/