【Unity】正しくエラーを制御して解決への手がかりを残す【C#】
プログラミングにはエラーがつきものです。これはゲームを作っている時も一緒です。
だからといって全くエラーの出力されないシステムがいいかというと、そうとも限らないんですよね。
システムに異常が発生しているなら、それを正しく人間に伝えてもらわなければなりません。エラーが発生しているにもかかわらず処理が進んで異常なデータが作成されるのも困るので、既存のデータを保護するために一旦止めるのも大切です。
ゲームの場合はゲーム自体が終了しないで一切の操作を受け付けなくなる、なんてこともあるので適切に対処する必要があります。
このページではエラーが出た時にうまくハンドリング(対処)することの大切さに触れます。
間違った対処の例
前にいた会社で先輩から聞いた話ですが、あるパッケージ導入の案件でエラーが出ていたそうです。
その先輩はその製品のサポートセンターに「エラーが出ているので解決してほしい」と問い合わせたところ、「この方法でエラーが出ないようになります」との返事が。
その中身を見てみると……なんと、ログをコメントアウトするという解決策(ワークアラウンド)でした。
違う、そうじゃない。
大元の問題が解決されていないので再度問い合わせて事なきを得ましたが、何か問題が起きているからこそエラーとして出力されている訳です。
ただ単にメッセージを出ないようにするのは問題自体を検知できなくなってしまうため逆に危険なシステムになったりします。
エラーを正しく処理しよう
エラーを正しく処理するのはエラーハンドリング、または例外処理と呼ばれていて、システム内の処理に失敗したらなんらかの方法でフォローします。
例えばstring型の値をint型に変換する処理は良くある処理で、int.Parse(“123”)のように引数の文字列をintに変換するメソッドがあります。こんな感じ。
このメソッドは変換に失敗すると例外(Exception)を出力します。int型に変換できる文字列でない場合などに出力されます。
俗に「例外を投げる」なんて言い方をしますが、この投げられた例外をキャッチしないといけません。上記のコードでは単純にコンソールにエラーが出力されるだけですが、try-catch文を使うことで何らかのエラー(例外)をキャッチして、エラーに対応した処理を行うことができます。
以下の例では引数がnullの場合、引数の書式が異なる場合、引数の数値がオーバーフローする場合の3つの処理を入れています。Parseの処理に失敗すると、後続のDebug.Log()で結果を表示する処理に進まず、対応する例外のcatch句に進みます。
catch句の引数は対応する例外を表していて、それぞれの例外に応じた処理を記述します。C#標準の例外のクラスはSystemの名前空間にあるのでusingで宣言してあります。
起こった例外に対して回復可能な場合は個別に対応することで、単純にエラーが出てゲームが止まってしまった、なんて状況を減らすことができます。この例ではデバッグ文を切り替えるようにしていますが、実際にゲームとしてビルドする場合はデバッグ文だと誰も内容を知ることができないので、画面にメッセージを表示するようにするのが良いと思います。
また、全ての処理でtry-catch文を使う必要はなくて、例えばC#のリファレンスを見たときに「例外」の項目が記載されていたら、どのような例外が発生するのかを確認してから使うとグッドです。
例えばint.Parseだったら以下のページに例外がいくつか載っているので、こうした場合はtry-catch文でハンドリングすると良いですね。
エラーへの対応
問題が起きた時にはそのことを知らせてもらって、人間が対応しなきゃいけないのか、システム内で自動的に処理できるのか、なんらかの対応をします。
例えば人間が対応しなければならないのなら、エラーを画面なりログなりに出力する必要があります。
スマホゲームだと、何か問題が起きたら対応するエラーコードを表示してタイトル画面に戻る、なんて対応が多いかもしれません。不正なデータが作成されてしまったり、正常に動作しない状態なのにシステムが頑張って動こうとしている場合が困るので、続きの処理を行わないようにタイトルに戻ったり、何ならゲームを再起動して一旦状況をリセットします。
エラーコードが画面に表示されるならそれを報告してもらって、可能なら状況を聞くことで開発者側でも何が起きたのかを知ることができます。
エラーが発生したことを検知できなければ人が対応することもできないですし、システム自体が何も言わずに落ちてしまったら、どうしていいか分からずお手上げ侍……なんてことにもなりかねません。
なので、エラーについては単純にメッセージが出ないようにするのではなく、本来の問題を検知できるように正しく出力することも意識してみてください。
エラーハンドリングについては多くのブログで紹介されているので調べてみるといいかもしれません。
よく聞く「例外を握り潰す」って?
ここからはちょっと余談です。ゲーム開発以外でもプログラミングをしている場合は「例外を握り潰す」なんて言葉を聞いたことがあるかもしれません。これは、catch句で例外をキャッチしておきながら何もしないでそのまま処理を終了させる行為を指します。例えばこんな感じ。
int.Parseに渡す引数は書式のエラーが出るはずですが、catch句で何もしていないことで例外が発生しているはずなのに人間側が検知できない状態になります。これが「例外を握り潰す」なんて呼ばれていたりします。
Unityでこれをやると、本来はコンソールにエラーメッセージが出力されるはずなのに、なまじtry-catch文を使って例外をキャッチしてしまっていることで、そのハンドリングは私たちが書いたコードに委ねられます。つまりコンソールに出力する処理をcatch句の中で書いていない場合はメッセージが表示されない状態になってしまうんです。
さらには、try句の中にある後続処理は行われないものの、try-catch文が終わった後の処理は続けられちゃったりします。もしこのtry-catch文で後続の処理に使うデータを処理していたのなら……エラーがまた発生してしまうことに。
エラーが出ているということは正常に処理を続けられないということですから、その状態をいかに見つけてあげるかが大事なポイントです。検知できない問題には対処するのは難しいですからね。場合によってはアプリケーションを止めることも安全のためには必要だったりします。
なので「エラーを見たくないから」と言ってcatch文で例外を握り潰すことのないようにしましょう。
まとめ
プログラムがエラーの情報を出してくれるということは、直すための手がかりがあるということです。なので、エラーを検知して、検知した結果システム的に回復可能ならエラーハンドリングのための処理を行う、無理ならエラーの状況を人間が検知できるようにする、といったことを考えておく必要があります。
特にユーザさんの元に届いた後のゲームでエラーが出ても、開発者が飛んでいって対処することは難しいですから、ゲーム単体でハンドリングしたり、それが難しければエラーコードやメッセージを画面に表示する処理を入れて検知できるようにするのも大切です。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【Unity】クラスは膨らむ前に分けよう。保守性と可読性の向上へ【C#】 2020.12.03
-
次の記事
ゲームのやらされ感をなくすならプレイヤーに裁量を【ゲーム開発】 2020.12.05
コメントを書く