アクセス修飾子

C#を初めとしてプログラミング言語には、アクセス出来る範囲を指定するアクセス修飾子 というものがあります。

publicとprivate

他にもありますが、publicprivateの違いについて考えてみます。

public class Player
{
	public int _level = 1;
	private int _hp = 10;
}
publicどこからでもアクセス出来る。アクセスが制限されない
privateclass,structの中からしかアクセス出来ない

Unityにおいて、MonoBehaviour、ScriptableObjectを継承したクラスだと以下の要素が追加されます

using UnityEngine;

public class Player : MonoBehaviour
{
	public int _level = 1;
	public int _hp = 10;
	private int _mp = 5;
}

publicインスペクタ上に表示され、値を編集できる
privateインスタンス上には表示されない

publicとした場合、シリアライズ対象になるためインスペクタ上に表示され、

編集した内容もシーンやプレハブに保存されます。

public変数で起きうる問題

個人で開発している場合は余り気にならないかもしれませんが、以下の様な問題が起きます。

どこからでも書き換えられる

public変数の場合クラスの外からでも書き換えられるため、

実際にどこから変更されたのか突き止めるのが困難になります。

自分でコードを書いたんだからそんなことしない、と思うかもしれませんが、

1ヶ月後の自分は他人です。

値を設定できると誤解される

複数人で開発していて、プログラマ以外がUnityEditor上で作業することがある場合、

インスペクタ上に公開されている値は初期値として使用できる ものとして誤解される可能性があります。

「ここに初期値を設定しておいたのに、ゲームを始めると全然違う値になるんだけど!?」

「初期値0の筈がいつの間にか100って入力されてて、ゲーム開始早々ステージクリアになる!?」

といったトラブルがあるかもしれません。

SerializeField属性

UnityではSerializeFieldという属性を使用できます。

using UnityEngine;

public class Player : MonoBehaviour
{
	public int _level = 1;
	[SerializeField] private int _hp = 10;
	private int _mp = 5;
}

publicどこからでもアクセス出来る。インスペクタ上に表示される。
[SerializeField] priavteclass,structの中からしかアクセス出来ない。インスペクタ上に表示される。
privateclass,structの中からしかアクセス出来ない。インスペクタ上に表示されない。

SerializeFieldを指定した場合、シリアライズ対象となりインスペクタ上に表示されるようになります。

ですが、private変数であるためクラス外から参照されることも変更されることもありません。

何かおかしな値が入っていても影響範囲はクラス内に絞られるので原因を調べやすいです。

public変数の使用をやめ、[SerializeField] privateな変数を使用するよう徹底すると

余計な変数をインスペクタ上に公開しなくて済み、

またインスペクタ上で設定できる値がゲーム中に使用される というルールで統一されるので、

おかしな値を設定されて問題が起きるということもなくなるでしょう。

private変数にアクセスしたい

値を取得したい場合

プロパティでgetterのみ設定して、クラス外から値を参照できるようにします。

// 変数
private int _mp = 5;

// プロパティ
public int Mp
{
	get
	{
		return _mp;
	}
}

この場合読み取りのみなので、外部から勝手に書き換えることは出来ません。

以下の様に書くことも出来ます。

public int Mp => _mp;

値を変更したい

プロパティでsetterを設定することでクラス外から書き換えられるようにする事も出来ます。

// 変数
private int _mp = 5;

// プロパティ
public int Mp
{
	get
	{
		return _mp;
	}
	set
	{
		_mp = value;
	}
}

もしくはpublicメソッドを用意するかです。

個人的にはこちらをおすすめします。

public void SetMp(int mp)
{
	_mp = mp;
}

public変数を直接書き換える場合と違い、代入される前に上限下限をチェックすることも出来ますね。

public void SetMp(int mp)
{
	_mp = Math.Clamp(mp, 0, 100);
}

public変数をやめよう

というわけでpublic変数を使うのはおすすめしません。

  • [SerializeField]
  • プロパティ
  • メソッド

を使うようにしましょう。

Unityの入門書やサンプルコードでよくpublic変数が使われている印象ですが、

ある程度Unityの開発になれて新しいプロジェクトを立ち上げたら、

public変数について考えてみてください。