注意事項
- 動作環境:Unity5.2.2f1 VisualStudio2017
- Unity 向けに執筆していますが、その他の環境でも参考になるかと思います
- この記事は、読み物要素を重視しているので文量おおめです。お急ぎの方は3行以上の文の塊は飛ばしてかまいません
- この記事の内容は、筆者が独学でUnityを習得するうえで重要だと思ったことになります。つまり主観です。
はじめに Unityでデバッグを学ぶ
Unityを使って何か実現したい時、ググって使えそうなところをコピペするだけで大体なんとかなります。しかし、そのコピペ元がコードを間違えていたり、コピペミスをしたときにデバッグができないと、途方に暮れるしかありません。にもかかわらず、デバッグを学ぼうにも、「Unity デバッグ」などと検索しても出てくる記事はUnityとVisualStudioのつなぎ方ばかりで、肝心のその先のテクニックについてなかなか得られないのが現状かと思います。
そこで本稿では、デバッグ環境を整えたその先、どのようにデバッグすればいいのかについて書き記すこととします。
デバッガを使ってデバッグする
何らかの不具合が生じた場合、大抵はコードを直す羽目になります。あてずっぽうにいろいろとコードをいじっていればいずれは何とかなりますが、修正しているうちに訳が分からなくなったりしますし、何より非効率です。コンパイルが面倒です。Unityなどの昨今のGUIが優れているゲームエンジンならばInspectorビューでコンパイルを挟まずにパラメータを調節できたりしますが、問題の原因の特定という点では不十分です。
デバッガを使ったデバッグでは、**コンパイルを挟まずに様々なパラメータを見ることができ、かつ問題の原因の特定に特化しています。**以下、実際のデバッグ作業画面をもとに順を追って解説します。
今回使うプログラム
こんな感じで、2Dアクションゲームによくある、近くでボタンを押すと村人に話しかけるというプログラムを動かしてみます。
オーディンスフィアとかMomodoraとかでよくあるやつです。
スクリプトはこんな感じです。(記事用に改編してあります)
void Update ()
{
// スペースキーを押したら話しかける
if (Input.GetKeyDown("tab"))
{
Debug.Log("Inputed");
if(isTalkEnabled == true && isPlayerAlive == true)
{
// 話しかけられる状態かつプレイヤーが生きていたら話しかける
Talk(GetTalkableObject());
}
}
}
isTalkEnabledでプレイヤーが話しかけられる状態かをチェックしています。
話しかけ中は他の人に話しかけてしまうと面倒なので、falseにします。
(話しかけ中も他の人に話しかけられるようにするとクロノトリガーっぽくなりますね)
isPlayerAliveでプレイヤーが生きているかをチェックします。
「死んでるなら話しかけられなくて当たり前じゃーん」って割と思うんですが、意外と死んでるときに話しかけられるものなんです。不思議ですね。
話しかけられる状態ならば、GetTalkableObject()で話しかけるオブジェクトを取得して、
Talk()でその中の一つを選んでそのオブジェクトと会話を開始します。
また、Tabキーを押したときにConsoleに「Inputed」と表示させます。
0. バグ発生
さて、バグを発生させてみます。
村人の前で会話ボタンを押しても会話が始まらなくなってしまいました。このバグを調査します。
1. ブレークポイントを貼る
まず、ブレークポイントを貼ってコードが正常に動いているかを見ます。
ブレークポイントは、ブレークポイントを貼った行が実行されるときにプログラムを止めるという動作をします。
ブレークポイントの利点は大きく分けて2つです。
- コードが正常なタイミングで実行されているかがわかる
- デバッガのデバッグ機能を使える
ブレークポイントはその個所に到達したときにプログラムを止めるので、止まらなければその処理は適切に実行されていないことになります。その場合は、さらにそこから遡ってブレークポイントを貼り、「どこで止まるのか」を調べることで「どこまでが正常に動いているのか」を調べることができます。
また、プログラムを止めた後は、一行ずつプログラムを実行したり、その時のパラメータの状態を監視したりなど様々なデバッグ機能を使うことができます。
ブレークポイントは、任意の行の行番号の左をクリックするか、F9キーで貼ることができます。同じ操作を既に貼っているところで行うと解除ができます。
さて、会話が始まらないことから38行目は実行されていなさそうなので、その少し前の35行目にブレークを貼って実行してみます。
止まりました!
ここの条件分岐で失敗しているっぽいというのはわかりましたが、何がいけないのかがまだわかりません。
そんな時はローカル変数をチェックします。
isTalkEnabled が falseだったようです。
このように、ローカルウィンドウではその時のそのクラスで使われている変数の一覧を見ることができます。
さて、悪者は見つかりましたので、これを true にしたときに正常通り動くかをテストしてみます。
コードを修正してコンパイルしなおして同じことをすればテストできるのですが、すこし面倒ですね。
そんな時は、このローカルウィンドウで直接値を変えてしまいましょう。ウィンドウの値のところでダブルクリックして編集するだけです。
ここで 1 とか "正" とか記入してしまっても、デバッガは値の型を見てくれるので、**想定外の値は入りません。**便利!!
また、ここで行った変更は実行中でのみ有効なので、後戻りが自由なので安心です。
さて、この状態でステップ実行してみます。
通りました!
つまり今回のバグは、「話しかけ時に isTalkEnabled が false になっていた」ことが原因であることが分かりました。
あとは適切なタイミングでこれが true になるようにコードを修正するだけですね。
補足
今回はボタンを押せば実行できるコードを扱ったので解説できませんでしたが、ブレークポイント+ローカル変数の変更は再現の難しいイベント(ボスを倒してからじゃないと発生しないイベントなど)のチェックなどにも便利です。
また、アセットや他人の書いたコードなど、処理がよくわからない場合にもとりあえずブレークを貼ることでどういう風に動いているかの理解を深めることができます。
おわりに
以上のように、デバッグは問題が起こっている範囲を狭めていきながら特定し、解決策を見つける作業です。その作業を大いに助けるのが、ブレークポイントなどのデバッガによる機能です。
お察しのとおり、デバッグは地味な作業ですが、その地味さにこそデバッグの強みがあるかと思います。
何かのコードが悪さをしているとき、たいていの場合はその時の状況やエラーメッセージでググれば解決します。しかし、何が悪いのか全く分からないときや、ググっても何とかならないときこそ、デバッグを地道に行うことで何とかすることができます。
デバッグ中の変更は実際のコードそのものに影響は与えないので、まずはご自身の作業でどんどん使って自分のものにしていくといいかと思います。
この記事がどのくらいニーズがあるかわかりませんが、少なくともゲーム作りを始めたころの自分のような人の助けになれば幸いです。