今回はUnityでスクリプトのコンパイルエラーが出た時にどうやって対処していけばいいのか?を多少実例を入れて解説していきたいと思います。
記事でエラーが発生した時に参考にしてみてください。
既にバリバリバリンカランカランシャリーン・・・コロコロ
とプログラミングしている方には不要となります。(-_-)
わたくしが行っているエラーが発生した時の対処なので、もっといいやり方はあるとは思いますが、どなたかの参考になればと思いここに書き残しておきます。
エラーが出ている個所を特定する
まずは適当に新しいC#スクリプトのファイルを作成し、名前をErrorScriptとします。
ErrorScriptをダブルクリックするとMonoDevelop(スクリプトエディターを変えてなければ)が開きます。
ErrorScriptの中身を書き換えてフィールドのintValueを宣言し、Startメソッドでその値を出力するようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript : MonoBehaviour { private int intValue; void Start() { Debug.Log (IntValue); } } |
↑のような感じです。
しかし、Ctrl+Sキーでスクリプトを保存するとConsoleにエラーが表示されます。
エラーが出たらエラーの内容を見てどうしてエラーになっているかを理解します。
Consoleの下の文章(エラー文章を選択すると、上の画像のさらに下の線で区切られている下に文章が現れます)はコピー出来るのでコピーしたらGoogleの翻訳を使えば内容がわかります。
また、Consoleのエラーの文章をダブルクリックするとエラーとなっていると思われる箇所がMonoDevelopでハイライト表示されるのでエラーの内容とその周辺を見て修正します。
Assetsフォルダに作ったフォルダ名を日本語名にしているとダブルクリックしても反応がないかもしれません。
今回は↑のようにStartメソッドでintValueの値を出力しようとしましたがタイプミスでIntValueとしてしまった為エラーが発生していました。
↑のように何らかの認識できないフィールドや変数、コンポーネント、スクリプト等は赤く表示されるのでそういった場合はタイプミスをしているか存在していない可能性があるので、そこを修正します。
他の名前空間で定義されているスクリプトを取得する場合は

↑の記事でusingディレクティブの追加方法を書いているのでそちらを参考に追加してみてください。
名前空間については記事の最後でも取り上げています。
今回の場合はIntValueをintValueとすればエラーが消えます(警告は出ますが・・・)。
ハイライト表示された部分に必ずエラーがあるとは限らず、その前の時点でエラーが発生していることもあるのでハイライト表示された部分から逆にプログラムを辿って原因を究明する必要があります。
エラーが出ている個所がメソッド内であればそのメソッドを呼び出している部分を見て、さらにそこから前の処理を見ていくという感じです。
Debug.Logを使う
ハイライトされたエラー箇所の中でどの部分がエラーになっているかわからない場合はDebug.Logを使ってエラーが出ている行の前でフィールドや変数等を出力します。
そうすることで、その行の前の時点で値が入っているか入っていないのかを把握する事が出来ます。
全然実用的なスクリプトではないですが、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript : MonoBehaviour { void Start() { Test test = new Test (); Debug.Log (test.myName + ":" + test.age + test.hobby[0]); Debug.Log (""); } class Test { public string myName; public int age; public string[] hobby; } } |
↑のようなスクリプトを作成します。
ErrorScript内にTestクラスを定義し、Startメソッド内でTestクラスのインスタンスを作成しフィールドの値を出力しています。
ErrorScriptを何らかのゲームオブジェクトに取り付け実行してみると、
↑のようにオブジェクトのインスタンスに参照が設定されていないというエラーが出ます。
エラーの文章をダブルクリックしてエラー箇所を表示すると
↑のようにtestのどのフィールドがNull参照になっているのかがわかりません。
そんな時は個々の値をその前にDebug.Logを使って表示し、どのフィールドでエラーが出ているのかを調べます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript : MonoBehaviour { void Start() { Test test = new Test (); Debug.Log (test.myName); Debug.Log (test.age); Debug.Log (test.hobby); Debug.Log (test.hobby [0]); Debug.Log (test.myName + ":" + test.age + test.hobby[0]); Debug.Log (""); } class Test { public string myName; public int age; public string[] hobby; } } |
例えば↑のように個々のフィールド値をDebug.Logで出力します。
すると、
↑のようにConsoleに表示されます。
実際にエラーになっているのはtest.hobby[0]であるという事がわかります。
test.hobby自体がNull参照になっていてさらにそこから配列の0番目を取得しようとしている為にエラーになっています。
なので、test.hobby[0]を表示している所を修正すればエラーを解消できることがわかります。
実用的なサンプルではないのでエラーの解消方法は割愛します・・・・(-_-)
エラーの対処の記事を書こうと思ってエラーを発生させようと思うと難しいですね・・・・(^_^;)
エラーが出ている行をコメントにする
エラーが発生した行を一旦コメントにして、Unityを実行しエラーが出ないか確認する方法もあります。
それで他のエラーが出なければその行でエラーが発生しており、何らかの対処をしなければいけません。
コメントにしてもエラーが発生する場合はそこの行以前の処理でおかしくなっているのかもしれません。
よく発生するエラー
よく発生するエラーについて見ていきます。
Null参照
よく発生するエラーとしてはNull参照が多いですね。
参照値とはGameObject型やstring型等の実際の値が入っている場所のアドレスを保持している値です。
ある箱があってそこに値が入っているのが値型で、ある箱があってそこに値が入っているがその箱の場所を指し示しているのが参照型です。
現実の世界で言うと家の中にあるテレビが値型で、家の中にあるテレビの場所を示しているのが参照型でしょうか・・・・ますます分かりづらくさせているかも・・・。
その為、テレビの場所を示している場所が設定されていなければNullとなり、テレビの情報を得ようと思ったらテレビの場所がわからないので情報は得られませんというエラーになっているという感じです。
単純なタイプミス
たぶん一番多いのがタイピングのミスでクラス名やメソッド名を間違えて打っている場合です。
例えばMonoBehaviourクラスで定義されているStartメソッドはゲームオブジェクトがシーンに登場すると実行されます。
1 2 3 4 5 6 7 8 9 10 11 12 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript2 : MonoBehaviour { void Start () { Debug.Log ("Start"); } } |
しかしStartメソッドを
1 2 3 4 5 6 7 8 9 10 11 12 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript2 : MonoBehaviour { void start () { Debug.Log ("Start"); } } |
↑のように最初のSを小文字のsにしてしまうと実行されなくなります。
これはMonoBehaviourクラスで定義されているStartメソッドではなく自分で作成した新しいメソッドのstartとして認識されてしまいます。
クラス名やメソッド名等は大文字と小文字の違い等を間違えないように記述しなければいけません。
またC#スクリプトのファイル名とpublicなクラスのクラス名は同じにする必要がありますので、ここも注意する必要があります。
例えばC#スクリプトのファイル名をErrorScript3としているのに
1 2 3 4 5 6 7 8 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class Errorscript3 : MonoBehaviour { } |
↑のようにSをsと小文字にするとエラーになります。
MonoBehaviourクラスで定義されているメソッドを使用することは多いですがタイプミスをすると自前のメソッドを定義しただけになるので、外部から呼び出されるまでのそのメソッドは実行されません。((+_+))
プログラミングをする時に混乱する事の一つとして、元々あるクラスやメソッド、プロパティなのか自分で作ったクラス、メソッド、プロパティなのかがわからなくなります。
デフォルトでMonoBehaviourクラスを継承しているのでMonoBehaviourクラスのnameプロパティはオブジェクトの名前を返します(MonoBehaviourの親クラスのObjectクラスで定義されている)。
ですがクラス内でnameというstring型のフィールドを自分で作成すると、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript4 : MonoBehaviour { private string name; // Use this for initialization void Start () { Debug.Log (name); } // Update is called once per frame void Update () { } } |
Startメソッドでnameフィールドを出力しようとし、実際に自分で作成したフィールドのnameがコンソールに表示されますが、コンパイルで警告が表示されます。
MonoBehaviourクラスにnameプロパティがあるので
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript4 : MonoBehaviour { // Use this for initialization void Start () { Debug.Log (name); } // Update is called once per frame void Update () { } } |
↑のようにnameを宣言していなくても親のMonoBehaviourクラスのnameが呼ばれ、ErrorScript4を取り付けたゲームオブジェクトの名前が表示されます。
このように親クラスのメソッドやプロパティはその名前だけで呼び出せるため、自分で作ったフィールドや変数なのか親クラスのプロパティなのか?といった混乱が発生するかもしれません。
そこら辺は気を付けてプログラミングする必要があります。
他のスクリプトを取得する場合も注意が必要です。
例えばMyerrorScriptクラスを作成しているとして、他のスクリプトからこのMyerrorScriptを取得したいとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ErrorScript3 : MonoBehaviour { private MyErrorScript myErrorScript; // Use this for initialization void Start () { myErrorScript = GetComponent<MyErrorScript> (); Debug.Log (myErrorScript); } // Update is called once per frame void Update () { } } |
すると
↑のようにアセンブリ参照のエラーが発生します。
他のスクリプトはMyerrorScriptとして定義しているところをMyErrorScriptとして取得しようとしている為です。
名前空間関連のエラーについて
エラーが発生する原因のひとつに名前空間があります。
名前空間とはそのスクリプトがどこに所属しているかという所属先の名前みたいなものです。
その名前空間に定義されているクラスを使用する時にクラス以下の名前で処理を呼び出せるのでスクリプトの最初にusingディレクティブを使って名前空間を指定します。
以前コメントへの返信で名前空間について書いたので、その内容を編集して載せておきます。
usingディレクティブとは何かと言うと、新しいスクリプトを作成すると上の方にすでに書かれているやつです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using UnityEngine; using System.Collections; public class Test : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } } |
例えば上の例ではMonoBehaviourクラスを継承してTestクラスを作成していますが、このMonoBehaviourクラスはUnityEngineという名前空間に定義されています。
そこでusingディレクティブでUnityEngineを指定する事でMonoBehaviourだけの記述で済みます。
例えばUnityEngineのusingディレクティブを無くすと
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using System.Collections; public class Test : UnityEngine.MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } } |
上のようにUnityEngineから毎回記述する事になります。
usingディレクティブを記述しておくとそれを記述しなくても良くなります。
またFollowTargetスクリプトはスタンダードアセットにもありますがUnityを使うユーザーがFollowTargetという同じ名前のスクリプトを作りたい、または作ってしまう事もあります。
(他の人が作ったスクリプトを使おうと思った時に同じ名前のスクリプトがある可能性もあります)。
その為、同じスクリプトの名前でも別物として扱えないと、どのFollowTargetスクリプトなのかがわかりません。
そこで使用するのがnamespace(名前空間)です。
例えば、スタンダードアセットのFollowTargetはUnityStandardAssets.Utilityという名前空間に定義されています。
その為スタンダードアセットのFollowTargetを使用する場合は
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using UnityEngine; using System.Collections; using UnityStandardAssets.Utility; public class UseFollowTarget : MonoBehaviour { // Use this for initialization void Start () { GetComponent <FollowTarget>().target = transform; } // Update is called once per frame void Update () { } } |
↑のようにusingディレクティブを記述しておきます。
この名前空間は自分で作る事も出来るので、Kamekumechan名前空間にNamespaceTestクラスを定義する場合は、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using UnityEngine; using System.Collections; namespace Kamekumechan { public class NamespaceTest : MonoBehaviour { private int testValue = 5; public int GetTestValue() { return testValue; } } } |
↑のようにnamespaceの中括弧で囲います。
このクラスを使用する時は
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using UnityEngine; using System.Collections; using Kamekumechan; public class UseNamespaceTest : MonoBehaviour { // Use this for initialization void Start () { NamespaceTest namespaceTest = GameObject.Find ("Main Camera").GetComponent<NamespaceTest> (); Debug.Log (namespaceTest.GetTestValue ()); } } |
↑のようにusingディレクティブでKamekumechanという名前空間を指定します。
スタンダードアセットのFollowTargetを見てみると、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System; using UnityEngine; namespace UnityStandardAssets.Utility { public class FollowTarget : MonoBehaviour { public Transform target; public Vector3 offset = new Vector3(0f, 7.5f, 0f); private void LateUpdate() { transform.position = target.position + offset; } } } |
↑のようにUnityStandardAssets.Utilityという名前空間に設定されている事がわかります。
名前空間自体も名前が被ってしまう可能性もありますが、C#ではどうやって分けているかわかりませんが、わたくしがJavaの本で見たパッケージの分け方と同じように自分の運営しているドメイン、例えばわたくしの場合は
gametukurikata.com
なのでそれを逆にして、
1 2 3 4 5 6 7 8 9 10 | using System.Collections; namespace com.gametukurikata.MyNameSpace { public class KamekumeTest : UnityEngine.MonoBehaviour { } } |
のようにするとドメイン以降で名前を分け、名前空間の被りがなくなるかもしれません。
usingディレクティブで名前空間を指定せずにクラスのメソッドを呼び出すとどの名前空間のクラスのメソッドなのかわからずにエラーが出る場合があります。
終わりに
エラーが発生する原因は色々ある為、細かく検証してエラーの発生場所を特定する必要があります。
本来のエラーとは関係ないエラー内容が表示される事もありますが、まずはハイライト表示された箇所から遡ってスクリプトの流れを地道に読んでいくといいと思います。
その時のフィールドや変数の値をDebug.Logを使って中身を確認していけば大体のエラーは解消出来ると思います。
どんな熟練のプログラマーでもエラーは発生させるはずなので、エラー箇所を特定し修正する地道な努力がプログラミング上達の鍵かもしれませんね・・・・。
一向に上達しないわたくしは向いていないのかもしれませんが・・・・・((+_+))