【ゲーム開発のためのC#入門講座・応用強化編】デバッグで処理を追いかけよう【#2】

2022-02-206.0_C#応用強化編

プログラミングにバグはつきもの

機械は命令通りに動くだけですから、ある意味においては間違えることがありません。ただ、命令そのものが間違えていた場合は、一生間違い続けます(機械学習のようなものは別ですが)。そして機械を動かす側の人間は当然たくさん間違いを犯すものです。

ゲーム開発に限らず、プログラミングという行為にバグはつきものです。皆さんがプレイしているゲームもアプリも、最初から何もかもがうまくいっていた訳ではありません。数多のバグ修正、そしてテストを繰り返し、ようやくリリースされているんですね。

皆さんがこれから数多のバグと遭遇すればするほど、世の中でバグを起こしてしまった製品に対して少しは優しい気持ちになれる……かもしれません。

今回はそんなバグと戦うゲーム開発者・プログラマーを大きく助けてくれるデバッグ技術について学習します。

デバッグって?

うぎゃー、虫が歯車に飛び込みやがったせいで機械が誤動作した!

なんてことは昨今なかなかありませんが、プログラムの中に入ってしまった欠陥・問題をそんな虫(バグ)に例え、調査して取り除く=プログラムの動作を確認することをデバッグといいます。

例えば下記のようなプログラムがあったとしましょう。

int result = 0;
result = TestMethod1(result); // 加算されて1になる想定
result = TestMethod2(result); // 加算されて2になる想定
result = TestMethod3(result); // 加算されて3になる想定
Console.WriteLine(result); // 実行結果は1

想定では3出力されるはずが、結果は2だったとします。

三つ使っているメソッドのどれかが悪さをしているようです。

皆さんならどうやって調査しますか?

まずはそもそもどのメソッドが原因なのかを調査しなければならないですよね。

以前、基礎拡張編で学習したコメントアウトもよいアイディアです。

// コメントアウトしてひとつずつ検証するのもあり
int result = 0;
result = TestMethod1(result); 
result = TestMethod2(result); 
//result = TestMethod3(result);
Console.WriteLine(result);

確認したい対象が明確なら、途中経過を表示してみるというのも手です。

int result = 0;
result = TestMethod1(result); 
Console.WriteLine(result); // TestMethod1の結果確認
result = TestMethod2(result); 
Console.WriteLine(result); // TestMethod2の結果確認
result = TestMethod3(result);
Console.WriteLine(result);

小規模ならこのような対応も可能ですね。

ですが、より複雑で規模のでかい、それこそゲームのようなものでバグが起きたとしたら、どうでしょうか?

事象ははっきりしているものの、そもそもどこに原因があるか、目星すらつかないかもしれません。そうすると手あたり次第コメントアウトや途中経過の表示をするのは、かなり非効率的ですよね。

何とかして、もっと簡単にプログラムの動きを調査する方法はないものでしょうか?

実は皆さん、既にそんなハイパーミラクルエックスに便利なものを持っています。

そう、開発環境「VisualStudio」です。

(まだの人は環境構築編を見てね!)

VisualStudioでデバッグしよう

実はVisualStudioでF5キーまたは再生ボタン(正確にはデバッグ開始ボタン)を押下した場合、指定の箇所でプログラムの動きを一時停止させることができます

そしてその時点での変数の内容を確認・変更したり一時停止させたところから1行ずつコードを実行させたりすることができます。

「な、なんだってー!?」って感じですね。

つまりその機能を使えば、頑張って途中経過を出力させたりせずとも、簡単にその時点での状態等を調査することができるのです。

統合開発環境の名は伊達じゃない!

早速実例を見るためにも、まずは新しいプロジェクトを作成して下記のコードをProgram.csにコピペしてください。

Healer healer = new Healer();
int playerHP = 1;
​
while (healer.MP > 4)
{
    playerHP += healer.Heal();
}
​
Console.WriteLine(playerHP);
​
public class Healer
{
    private int HealPower;
    
    public int MP { get; private set; }
    
    public Healer()
    {
        Random random = new Random();
        MP = random.Next(20) + 1;
        HealPower = random.Next(10) + 1;
    }
    
    public int Heal()
    {
        MP -= 4;
        return HealPower * 2;
    }
}

それでは、このコードを例としてひとつずつ見ていきましょう。

デバッグ機能①プログラムを一時停止させてみよう

VisualStudioで開いているプログラムファイルの左側に、一見するとただのフレームにしか見えないような隙間があります。そこにカーソルを合わせると、小さな「●」が表示されます。

試しに画像と同じ箇所、コードの9行目をクリックしてみましょう。

するとこのように、「●」の色が変わり、対象の行自体にも色がつけられます。

これがプログラムを一時停止させる位置を示すブレークポイントと呼ばれるものです。

F5キーまたはデバッグ実行ボタンを押下してみてください。

実際にプログラムがブレークポイントで下記のように一時停止します。

該当行に矢印と黄色のハイライトがついていますね。これが一時停止した証です。

この状態で、例えば変数「playerHP」にカーソルを当ててみてください。

その変数に現在どんな値が格納されているのか、確認することができます。

現在の値は25!

便利ですね!

再度F5キーを押すか、「続行」という表記に変わっているボタンを押すと、一時停止させていたプログラムを続行させることができます。

続行させた上で、自分が確認した変数「playerHP」の値が実際に出力結果として表示されることを確認してみましょう。

確認が完了したらコンソール画面を閉じてあげてください。

デバッグ機能②変数の内容をより詳細に確認・変更してみよう

先程と同じ設定(9行目にブレークポイント設定)で、再度F5キーかデバッグ開始ボタンを押下してください。

プログラムが一時停止したら、今度は変数「healer」にカーソルをあててみましょう。

変数「healer」はクラスなので、具体的な値ではなく「Healer」というクラス名が表示されていますね。

横に小さな▶マークがついているのがわかりますか?

試しにクリックしてみてください。

なんと、インスタンスが持っている情報も確認することができます!

素晴らしいですね。これなら途中経過をわざわざ表示しなくても一目瞭然です。

と、この方法もお手軽ではあるのですが、実はもうひとつ、変数の値をもっとしっかり確認する方法があります。

そのままずばりウォッチという名前の機能です。

変数「healer」を右クリックすると、「ウォッチの追加」という選択肢があると思います。選択してみましょう。

すると画面下部の「ウォッチウインドウ」に対象の変数が表示されます。

先程と同じように「▶マーク」を押すと、同じく中身を参照することができますね。

なおかつ、ウォッチのさらにすごいところは、参照するだけではなく、更新までできてしまうところです。

「MP」という項目行の「値」という列をダブルクリックしてみてください。

エクセルなどと同じように、値を書き換えることができます。

試しに「30」に更新してみましょう。

無事、変数の値を更新することができました!

デバッグ機能③処理の実行位置をジャンプさせてみよう

せっかくMPを30に更新したので、本当にMPが30になっているか、確認したいですよね。メソッド「Heal」を実行してもらえれば確認できるのですが、9行目は処理の最後。もう「Heal」を使う機会がなさそうです。

が、デバッグ機能では処理の実行位置をジャンプさせて、そこから処理を継続実行させることもできます。

画面左側に出ている黄色い矢印をクリックしたまま、「●」のついている枠の内側を移動しつつ、カーソルを4行目に移してみてください。下記画像のように、矢印の位置が動いていればOKです。

これで実行位置を移動させることができました。

F5キーを押すか「続行」ボタンを押せば、移動させたところから処理を継続実行できるのですが、ちょっと待ってください。ここでさらに便利な機能を使ってみましょう。

デバッグ機能④1行ずつ処理を実行させてみよう

プログラムを一時停止させている状態で、F10キーを押してみてください。

プログラムが1行進んだのが、矢印の位置でわかると思います。

「VisualStudio」を使えば、このように処理を手動で1行ずつ進めることもできます。

この1行処理を進める方法をステップ実行といいます。

練習がてら、9行目にいくまで、ひたすらF10キーを押しながらプログラムを実行してみましょう。実行していく中でウォッチウインドウに表示されているMPがメソッド「Heal」を実行する度に減っていくのが見て取れるかと思います。

無事、再度9行目まできたら、今度は矢印を6行目に移動させてみましょう。本来ならMPが5以上ないと通らない処理ですが、デバッグなら無理やり移動させることができます。

この状態で、今度はF10キーではなく、F11キーを押してみてください。

どうなるかというと、メソッド「Heal」の中に移動します。

F10キーの時には見た目上の1行ずつ処理していたのに対して、F11キーの場合はコンピュータが実際に実行する1行ずつ処理します。だからメソッドの中へ移動したんですね。

このように、ステップ実行には実は三種類の方法があります。

最後のひとつも確認してみましょう。

Shiftキーを押しながらF11キーを押してください。

今度はメソッドの外に出ましたね。

改めて、ステップ実行三種類とそれぞれの実行内容を説明します。

ステップ実行コマンド内容
ステップオーバーF10表示上の1行ずつコードを進める(メソッド実行も1行とみなす)
ステップインF11実動上の1行ずつコードを進める(メソッド実行の場合はメソッド内に移動する)
ステップアウトShfit + F11現在実行中のメソッド処理を実行完了時点まで進める(メソッド外に移動する)

プログラムの大まかな流れを知りたいならステップオーバーが便利です。メソッド内の細かい動作まで見たい時はステップイン、反対にメソッド内で確認したいことがすべて確認できたらステップアウトでメソッド外へ抜けると、効率よくプログラムの動作を追いかけることができます。

非常に便利ですね!

最後にF5キーまたは続行ボタンを押下してみましょう。

ブレークポイントを設定してあるところまで処理を実行することができます。今回は9行目にブレークポイントを置いているので、9行目でまた止まるはずです。

そうしたらもう一度F5キーを押すと、それ以上ブレークポイントはないため、プログラムが最後まで実行されます。

無事、コンソール画面に結果が出力されればOKです。

普段見ることのないプログラム内部の旅、いかがだったでしょうか?

まとめ

改めて、「VisualStudio」が提供してくれるデバッグ機能のまとめです。

デバッグ機能名効果
ブレークポイント指定の行でプログラムを一時停止することができる
ウォッチ変数の確認・変更ができる(実はメソッド実行も可能)
コード位置のジャンプ(特に名称なし?)コード処理の現在地を指定の位置までジャンプさせ、そこから処理を継続実行させることができる
ステップ実行コードを任意の間隔で実行することができる。移動間隔はステップオーバー・イン・アウトでそれぞれ異なる

これらの機能を活用すれば、バグ調査の時間は劇的に短縮することでしょう。

「VisualStudio」はただC#のプログラムを作れるだけでなく、入力補完だったりデバッグ機能だったりを提供してくれる、まさに統合開発環境なんですね。

慣れないうちはステップ実行のキーが「どれがどれだっけ?」となりがちなので、この記事を復習するなどして使いこなせるように練習していきましょう。

今回は実演しながらの解説となったため、課題はありません。

また、Unityでの活躍ポイント紹介もありませんが、このデバッグ機能はUnityでも使うことができるとお伝えしておきます。最強機能なので、ぜひ活用していきましょう。

次回はUnityで使うコルーチンという機能でよく利用する「yield」について学習します。お楽しみに!

それでは、今回もお疲れ様でした!

また次の記事でお会いしましょう!

Posted by yuumekou