Unityでスクリプトを記述しているときに、Sceneを切り替えても前のシーンでの変数を引き継ぎたいことがあります。
また、スクリプトを複数に分けて書くときに他のスクリプトの変数を取得したいことがあるかと思います。
このように変数を共有したい場合には、変数の前にアクセス修飾子と呼ばれるものを付ける必要があります。
読者の皆様も、Unity(C#)のサンプルコードを調べていると、intやfloatなどのC言語やPythonなど馴染みある変数の型の他に、[SerializeField]やpublic、privateなど見慣れないものに遭遇することでしょう。
これらをアクセス修飾子と呼びます。アクセス修飾子はUnity(C#)だけではなく、JavaやC++などにもpublicやprivateなどがあります。
これらの言語ではこれらは必須で、アクセス修飾子を使わずにUnityを使いこなすことはできないです。
本記事では、Unityでよく使う[SerializeField]やpublic、privateについて理解しつつ、Unityでゲーム開発をする際にいつ使うのか、使い分けを紹介します。
- Unityで変数を共有したい。
- アクセス修飾子について知りたい。
アクセス修飾子
アクセス修飾子は、主にオブジェクト指向のプログラミング言語(Java・C++・C#・Python・Rudy・Swift etc.)において、クラスのメンバー(変数やメソッド)へのアクセス範囲を制御するために使用されるキーワードです。
参考サイト:「アクセス修飾子 – C# プログラミング ガイド」
言語によって使えるアクセス修飾子は違う
Pythonは、オブジェクト指向の言語ですが、冒頭で前述したようにアクセス修飾子がありません。
このように、オブジェクト指向であっても言語によって使用できるアクセス修飾子は異なります。
以下に、各言語で使用できるアクセス修飾子をまとめました。
言語 | private | public | protected | default | internal | protected internal | fileprivate | open |
Java | ○ | ○ | ○ | ○ | × | × | × | × |
C++ | ○ | ○ | ○ | × | × | × | × | × |
C#(Unity) | ○ | ○ | ○ | × | ○ | ○ | × | × |
Python | × | × | × | × | × | × | × | × |
Rudy | × | × | × | × | × | × | × | × |
Swift | ○ | ○ | × | × | ○ | × | ○ | ○ |
表に示したように、PythonとRudyはアクセス修飾子を使用しません。
Unityは、C#をもとにして開発し、5つあります。
ゲームによって異なりますが、これから紹介する[SerializeField]やpublic、privateの3つの使い方さえ理解できれば、基本的に困ることはありません。
private
privateは、変数やメソッドが同じクラス内からのみアクセス可能である修飾子です。
クラスの内部でのみ利用されるので、他のスクリプトはprivateを使用しているデータや処理を干渉することができません。
- 外部からの不正なアクセスや意図しない変更を防止。
- データの整合性やセキュリティを保護が可能。
以下にコードの例を紹介します。
public class Sample1 : MonoBehaviour
{
private int pr_myInt; // プライベートな変数
private void Start()
{
pr_myInt = 10;
}
}
このスクリプトはプライベートな変数を宣言しています。
この変数は、同じクラス内のメソッドからのみアクセスできます。
次に、プライベートなメソッドの使用例を紹介します。
public class Sample2 : MonoBehaviour
{
void Start()
{
pr_Fn();
}
private void pr_Fn()
{
// プライベートな処理
}
}
「pr_Fn」というプライベートなメソッドを定義しています。Startメソッド内でpr_Fnメソッドを呼び出すことができますが、他のクラスから直接呼び出すことはできません。
※Unityでスクリプトを作成すると、void Startが作成されるかと思います。このStartメソッドもprivateです。
public
publicは、変数やメソッドがどのクラスや外部からでもアクセス可能である修飾子です。
クラスの再利用性や拡張性が向上し、柔軟性のある設計が可能。
共同作業やオープンソースプロジェクトの管理が容易。
他のコードとの連携を確認ができて、バグの発見と修正が容易。
以下にコードの例を紹介します。
public class Sample3 : MonoBehaviour
{
public int Pb_myInt;// パブリックな変数
void Start()
{
Pb_myInt = 10;
}
}
このスクリプトはパブリックな変数を宣言しています。
次に、パブリックなメソッドの使用例を紹介します。
public class MyClass
{
public void pu_Fn()
{
// パブリックな処理
}
}
このpu_Fn()は同じスクリプト内で処理することも可能で、別のスクリプトから扱うこともできます。
static publicで別のスクリプトの変数を使用する
従来のゲームはエリア1があり、エリア2にエリア移動するときはロードをしないといけません。
このように、ゲームはシーン切り替え(遷移)をすることが主流です。
※最近のゲームは革新的な進化をして、オープンワールドといわれるエリアの移動制限がないものが多くなっています。
実は、シーンAからシーンBへ画面を切り替えると変数は保持されず、初期化されてしまいます。
この現象は重大な問題で、ゲームでエリアを移動したら、プレイヤーのHPが元に戻る、敵へのダメージが蓄積されていないなど初期化されると非常に困ります。
そこで、シーンを変えても変数が保持されるstaticを紹介します。
以下のソースコードは、ボタンを押すと数字が加算されるソースコードです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // UIを適用
using UnityEngine.SceneManagement; // シーン切り替えに必要
public class Test : MonoBehaviour
{
public int a = 0;
public static int b = 0;
public Button plusBtn; // ボタンの参照
void Start()
{
Debug.Log(SceneManager.GetActiveScene().name+ "@a:::" + a);
Debug.Log(SceneManager.GetActiveScene().name+ "@b:::" + b);
}
public void Plus()//UIボタンを押すと加算される
{
a++;
b++;
Debug.Log(SceneManager.GetActiveScene().name + "@a:::" + a);
Debug.Log(SceneManager.GetActiveScene().name + "@b:::" + b);
}
public void Change()
{
if (SceneManager.GetActiveScene().name == "SceneA")
{
SceneManager.LoadScene("SceneB");
}
else
{
SceneManager.LoadScene("SceneA");
}
}
}
a ,bはint型変数で、bは静的な(static)を宣言し、クラスの全インスタンス間で共有されます。
plusBtnはUIボタンで押すと、Plus()によってa,bを1ずつ加算(インクリーメント)します。
UIを使用するので、UnityEngine.UIを入れる必要があります。
Change()によって、シーンが “SceneA” であれば “SceneB” に、それ以外の場合は “SceneA” にシーンを切り替えます。
SceneManager.LoadScene()でシーン切り替えをしようとしても、エラー文が表示されます。
それは、作成したシーンが表示設定していないためです。
設定は、FileからBuild Settingsの項目を選択します。
その後、表示されたウィンドウの「Add Open Scenes」を押すと、現在作成されたシーンが表示されます。
このスクリプトを実行すると、次のようになります。
シーンを切り替えると、aは加算した数字は初期化されますが、bはシーンを変えても保持されます。
staticは、シーン遷移のときに変数を引き継ぐだけではなく、他にも別のスクリプトで定義した変数を呼び出すことができます。
しかし、静的メンバーは依存関係を増やし、複雑なシングルトンパターンはテストや保守性の観点から問題が生じることがあります。
そこで、staticを使わず、他のスクリプトから関数を呼ぶ方法があります。
シーン遷移で、staticを使えば変数を保持することができますが、UIボタンやUIテキストなどゲームオブジェクト(GameObject)が残ってしまったり、消えてしまったりなど保持、初期化する時にはDontDestroyOnLoadを使用しなければいけません。
DontDestroyOnLoadの解説は以下の記事でしています。
思い通りのゲームが作れない
Unityでゲーム開発しているけど完成しない。
技術的な壁や知識不足が原因で、思い描いたゲームを実現するのは難しいです。
しかし、Udemyは動画で実践的なゲーム開発を解説していて、
購入した講座は再生・停止・スキップなどが可能なオンデマンド形式なので、
専門的な内容を自分のペースで学習できます。
Unityの機能を網羅したいや作りたいゲームがある人はUdemy学習を取り入れましょう。
数多くある講座の中から特におすすめな講座を3つ紹介します。
初夏のセール開催中!(5月23日まで)
対象のコースが1300円から。
Unityのはじめの一歩としておすすめ。開発例に物理挙動やアニメーションを使用しているので、今後の開発が円滑になる。
トランプを題材にした講座。カードゲームやボードゲーム開発に応用可能
UnityエンジンのインストールやC#の文法に加えて、App StoreとGoogle Playにゲームをリリース方法を解説。
「public」・「private」の違い
ここまで、アクセス修飾子「private」・「public」の使い方を紹介してきました。
ここからは、Unity上でどのように使うか紹介します。
using UnityEngine;
public class pu_pr : MonoBehaviour
{
// 変数定義
public int pu_Int; // パブリックな整数型変数
private int pr_Int; // プライベートな整数型変数
public float pu_Float; // パブリックな浮動小数点型変数
private float pr_Float; // プライベートな浮動小数点型変数
public string pu_String; // パブリックな文字列型変数
private string pr_String;// プライベートな文字列型変数
// メソッド
public void method1()
{
// 処理
}
private void method2()
{
// 処理
}
}
本スクリプトをアタッチすると、以下の画像のように public型変数はInspectorウィンドウに表示されます。
このように、「public」はアクセスの制限を一切せずに、どのプログラムからでも参照、変更が可能です。「private」は記述されたスクリプト以外からのアクセスをできないように制限します。
メソッドはInspectorウィンドウに表示されません。
これらのメソッドは、クラスのインスタンスから呼び出すことができますが、privateであるmethod2()は同じクラス内からのみ呼び出すことができます。
SerializeField
SerializeFieldは、publicと同じように、変数やプロパティをUnityのInspectorウィンドウに表示可能にするために使用されます。
具体的には、SerializeField属性を変数やプロパティの前に記述することで、その変数やプロパティがUnityのインスペクター上で編集可能になります。これにより、ゲームオブジェクトやコンポーネントのプロパティを直接設定することができます。
以下にSerializeFieldを使用したコードを示します。
using UnityEngine;
public class Serialize : MonoBehaviour
{
[SerializeField]
private int se_Int;
[SerializeField]
private float se_Float;
[SerializeField]
private GameObject se_GameObject;
// ...
}
プライベートな変数ですが、シリアライズフィールドとしてマークしているため、このスクリプトをアタッチすると、Inspectorウィンドウに変数とオブジェクトが表示されます。
[SerializeField]とpublicの相違点
ここまでの紹介で、[SerializeField]とpublicの違いがないと思うでしょう。
実際は、2つの間には大きな違いがあります。
前述したserializeFieldのソースコードにて、3つの変数すべてアクセス修飾子がprivateです。
しかし、serializeFieldで定義している変数はpublicのようにinspectorウィンドウ内で編集ができます。
要するに、SerializeField属性はシリアライズされた変数をインスペクター上で編集可能にします。
あえて例えるならば、「much」、「many」、「a lot of」の3つの関係のようにといえるでしょう。
※3つの語句は、肯定文では使わない、どんな文にでも使えるなどそれぞれの表現があるので、混乱する場合は無視してください。
語句 | 可算名詞 | 不可算名詞 |
much | × | ○ |
many | ○ | × |
a lot of | ○ | ○ |
したがって、serializeFiledとpublicの違いは、他のクラスからでも参照できる変数の宣言はpublic、インスペクター上に表示し他のクラスからは参照できない変数の宣言は[SerializeField]を使うと考えれば大丈夫です。
まとめ
[SerializeField]は、インスペクタ―(Inspector)からアクセスするとき変数を定義するときに使います。
publicとprivateはアクセス修飾子と呼称される一種で、アクセス修飾子とはクラスやメソッド、変数がどこから参照や変更しているのか決定するものです。
「public」はアクセスの制限がなく、どのプログラムやクラスからでも参照、変更が可能です。
「private」は記述されたスクリプト内のみでアクセスができます。
[SerializeField]とpublicはInspectorウィンドウに変数を表示することは同じです。
しかし、[SerializeField]は、他のクラスから参照、変更することはできません。
publicは、他のクラスから参照、変更することができます。