【ゲーム開発】自作ゲームのバグを減らす方法まとめ

自作ゲームのバグを減らす方法まとめ ゲーム開発

今回はゲーム開発(特に個人ゲーム開発)に関する一般的な話題で、タイトルの通り

自作ゲームのバグをできる限り減らす方法

を丁寧にご紹介するという内容になっております。

ゲームを作っているとバグが発生することは日常茶飯事です。ですができることならバグが発生しないように予防したり、発生したとしても簡単に修正できるようにしたいですよね。ゲームをリリースするときにはなるべくバグのない状態でプレイヤーさんにゲームを遊んでもらいたいものです。

そこでここでは私が長年ゲーム開発を続ける中で実践してきた

  • バグが発生しないように予防する方法
  • バグが発生しても簡単に原因を特定・修正するための方法

について詳しく書いてみます。

前提:そもそも「バグ」とは?

はじめにバグ対策がどうの…という前にそもそもバグとは何なのか?という点について定義をハッキリさせておきましょう。

ゲーム開発におけるバグとは広義には「開発者が意図していないゲームの挙動」を指します。例えば

  • キャラクターが通れるはずのない壁をすり抜けてしまった(=いわゆる壁抜け)
  • 特定の操作をしたらキャラクターの挙動がおかしくなった
  • ゲームが突然強制終了してしまった

とかそういうやつです。

バグによる挙動は開発者が意図していないものなので、リリースしたゲームにバグが残っていると

  • プレイに支障が出る
  • ゲームの楽しみが激減して興ざめする

などしてプレイヤーの怒りを買い低評価レビューをつけられてしまいます。つまりバグが残っているゲームは品質が低いと見なされるのです。こうなると当然売り上げに影響するので、たいていの開発者はゲームになるべくバグが残らないように懸命に努力しているというわけです。

バグは大きく分けて2種類ある

ところで一口にバグと言ってもいくつかの種類があります。すごく大雑把に分けると次の2種類に分類することができます。

  1. プログラムの誤りによるもの
  2. プログラム的には問題ないが不具合が生じるもの

一つ目はプログラムの誤りによるものです。例えば、最もよくありがちなのは条件分岐の条件式で不等号の向きを間違えた等が挙げられます。文法上のエラーであればコンパイルが通らないのですぐに分かりますが、不等号の間違いなど文法に問題がないミスはかなり気づきにくいので実際にゲームを動かして確かめるまで分からないことがよくあります。

また二つ目として、プログラム的には何ら問題がないもののデータ設定やレベルデザインのミスによって不具合が生じるものもあります。例えば一部の壁に当たり判定を設定するのを忘れたら壁抜けが起きてしまうとか、一部のキャラのデータ設定を間違えて本来想定していたよりも強くなってしまっていたとかそういう感じですね。

いずれにしても、それぞれ性質が違うのでバグの種類に応じて適切に予防・対処する必要があります。

「バグをなくす」のは不可能だけど…

ただまあなんていうか、ここで身も蓋もない話をするとバグを完全になくすのはほぼ不可能だといえます。なぜならバグの原因は人間のミスだからです。人間はいともたやすくミスをする生き物ですから、たとえどんなに優れたゲーム開発者であってもちょっとした不注意でバグを生み出してしまう可能性があります。

じゃあどうすればいいのか?バグの根絶は不可能だとしても工夫次第でバグを減らすことはできます。下記ではバグを減らすための具体的な工夫を色々ご紹介していきますのでぜひ参考になさってください。

プログラムの誤りによるバグを減らす方法

ではまずはプログラムの誤りによるバグを減らす方法をご紹介します。主な対策は次のとおりです。

  • 読みやすく分かりやすいコードを書く
  • プログラミング言語やゲームエンジンの仕様をきちんと理解する
  • 条件分岐の条件式に注意する
  • 巨大なクラス(神クラス)を作らない
  • きちんとバージョン管理を行う

それぞれ詳しく見ていきましょう。

読みやすく分かりやすいコードを書く

まず一つ目の対策は読みやすく分かりやすいコードを書くことです。

先ほどからご説明しているようにバグは人間のミスによって発生します。なのでプログラミング的な予防策としてはミスしにくいコード、つまり読みやすく分かりやすいコードを書くことが第一です。汚いソースコードはミスを誘発しバグの温床となるためなるべく避けなければなりません。

※読みやすいコードを書くことは「言うは易し」の典型例

…と、なんか偉そうなことを書いてしまったのですが、正直言って私自身そんなコードを書けているかというと甚だ疑問です。

実はそれもそのはずで「読みやすく分かりやすいコード」を書くのは実際難しいし、なんとそのトピックだけで250ページを超える本になるくらいです(下記は読みやすいコードを書くためのコツが詰まった名著なのでぜひ読んでみてください)。

そんなわけでここではその辺についてのコツを長々説明するのはやめておきますが、初心者の方でも比較的簡単に実践できそうなことを挙げると次のようなものがあります。

  • 変数・関数・クラス等にわかりやすい名前をつける(=適当な名前をつけない)
  • わかりにくい箇所にはコメントを入れる
  • 条件分岐などのネストを浅くする
  • 長い処理は分割する

最低限上記のことを意識しながらコードを書くようにすることをお勧めします。

プログラミングにおいては「数ヶ月後の自分は他人」

なお個人ゲーム開発の場合は自分で書いたコードを他の人に見せる必要がないので、「まあ汚くてもいいか…」とついついソースコードを適当に書いてしまいがちです。

しかしたまに言われるようにプログラミングにおいては「数ヶ月後の自分は他人」(=数ヶ月もすれば自分で書いたコードの意味を忘れる)です。そのコードの仕様を忘れた未来の自分が見ても理解できるようにコードを書くことを普段から心がけるべきでしょう。

プログラミング言語やゲームエンジンの仕様をきちんと理解する

次に二つ目はプログラミング言語やゲームエンジンの仕様をきちんと理解することです。

ゲーム開発においてはたいていプログラミング言語とゲームエンジンを駆使することになると思うのですが、その2つの仕様を十分に理解しないまま使うとゲームが思わぬ挙動になり・しかも原因もよくわからないということで頭を抱える事態になってしまいがちです。

特にゲームエンジン側の機能は複雑な場合があるため、それにアクセスする際はその機能の仕様を公式リファレンスで事前に確認しておくことをお勧めします。というのは(このブログもそうですが)非公式の情報だと分かりやすさ優先で肝心の注意点が書かれていなかったり、そもそも情報が間違っていたりする可能性があるからです。

そこでまず第一に公式リファレンスを見ることが重要です。公式リファレンスは英語だったり分かりづらい書き方がしてあったりして嫌がる方もいらっしゃるかもしれません。しかし公式情報は最も信頼できる情報源ですから面倒でも必ず目を通しておきましょう。もしどうしてもわからなければそれからわかりやすい二次情報を探せばいいのです。

条件分岐の条件式に注意する

三つ目は条件分岐の条件式に注意することです。

↑のほうでも少し触れましたが、条件分岐の条件式を間違えることはよくある割に重大なバグにつながることもあるため条件式を書く際は注意しないといけません。

この条件式のミスにはとても厄介な点があります。それは例えば「条件式の不等号が逆だった」というようなミスで、何が厄介なのかというと最もやりがちなミスでありながらエラーにはならないことです。そのため実際にゲームをプレイするまで気づかないことが多く、特に他の条件も絡んでいる場合はチェックをすり抜けてしまいゲームをリリースした後に発覚するなんてこともしばしばです。

したがって条件式を書く場合は「本当にその式で合ってるのか?」という点をよく確認する癖をつけた方がいいでしょう。

巨大なクラス(神クラス)を作らない

四つ目は巨大なクラス(いわゆる神クラス)を作らないことです。

ゲームを作っていると、例えば下記のような巨大で万能なクラスを作ってしまいがちです。

  • プレイヤーキャラクターの機能を何でも詰め込んだ「Player」クラス
  • ゲームの裏方を何でもこなす「GameManager」クラス

このような肥大化したクラスを俗に「神クラス」と呼びます。

神クラスのデメリット

しかし神クラスには変更が極めて困難というやばいデメリットがあります。なぜ変更が困難なのかというと

  • 神クラスには様々な変数や関数が詰め込まれ複雑に絡みあっている
  • つまりある箇所を変更すると全く違う箇所に思わぬ影響が出る可能性がある

からです。

私は初心者の頃によく神クラスを作ってしまっていたのですが、使ってみると神クラスはメンテナンス性が最悪で初心者ながら「こりゃ何とかしないとダメだなぁ」と思っていたものです。メンテナンス性が悪いということはバグの温床になりやすいということでもあるのでクラスが肥大化しないように気をつけたほうがいいと思います。

機能をモジュール化しよう

クラスが肥大化し神クラス化するのを防ぐには機能をモジュール化することが有効です。モジュール化とは簡単に言えば

機能を分離・独立化すること

です。

例えば3Dアクションゲーム用のプレイヤーキャラクターを作る場合を考えてみましょう。ゲームにもよりますが、3Dアクションゲーム用のプレイヤーキャラクターにはたいてい次のような機能が必要になります。

  • プレイヤーのHPなどのステータスを管理する機能
  • 歩く・走る・ジャンプするなど移動処理を行うための機能
  • 敵を攻撃するための機能

ただ考えてみると、これらはそれぞれ独立して存在できる別の機能となります。重要なのはこういった別々の機能を同じPlayerクラスに詰め込むのではなく、次のように別のクラスに分割して実装することです。

  • PlayerStatusクラス:プレイヤーのステータスを管理
  • PlayerMovementクラス:プレイヤーの移動処理を実装
  • PlayerAttackクラス:プレイヤーの攻撃処理を実装

このようにすればクラスのメンテナンス性が向上しバグの発生を予防することができます。

きちんとバージョン管理を行う

五つ目の対策はきちんとバージョン管理を行うことです。

バージョン管理を行うことで得られるメリットはたくさんあります。中でも一番の恩恵は過去の変更を簡単に追跡したり打ち消したりできることでしょう。ゲーム開発では「既存の機能を変更したらバグった」ということはよくあるので、変更を追跡できればバグの原因を突き止めやすくなり結果としてバグを減らすことに繋がります。

…まあゲーム開発を数年続けている方ならまさかバージョン管理をしていないなんて人はいないだろうなとは思いますが、一方で初心者の方であれば「何それ?」という方もいらっしゃるかもしれません。もしそうであれば是非この機会にバージョン管理について調べて導入してみてください。

レベルデザインやデータ設定等によるバグを減らす方法

さてお次はレベルデザインやデータ設定によるバグを減らす方法についてです。こちらに関してはプログラム的なバグ対策と比べると有効な対策はあまりないのですが、主な対策としては次のようなものが挙げられます。

  • しっかりテストプレイを行う
  • レベルデザインを効率化するアセットを導入する
  • 面倒くさがらずにデバッグ用の機能を用意する
  • データの入力ミスを防止・チェックする仕組みを作る

それぞれ詳しく見ていきましょう。

しっかりテストプレイを行う

まず一つ目の対策は、言わずもがなという感じですがしっかりテストプレイを行うことです。

レベルデザインやデータ設定によるバグは、プログラム的なバグとは関係ないことが多くエラーが出ないことがほとんどで気づきにくい点がとても厄介です。したがってその辺のバグは念入りにテストプレイを行って洗い出すのが一番でしょう。

テストプレイでは「ズルした場合」や「想定外の場合」も試す

テストプレイの具体的なコツは次のとおりです。

  1. まずは普通にプレイしてきちんとクリアできることを確かめる
  2. 次にズルをするなど「推奨されないプレイ」をしてどうなるかを試す
  3. 最後に思いつく限りの「想定外のプレイ」をしてどうなるかを試す

テストプレイを行う際は、まずは普通にプレイしてきちんとクリアできることを確かめます。

ここでいう「普通のプレイ」とは言い換えると「優等生的なプレイ」です。例えばクエストで○○しろ、と指示されたらちゃんとそれに従うような遊び方のことですね。最低限このプレイで上手くいかないとゲームが進行しなくなったりプレイヤーが不満を抱いたりするので最初にこの遊び方でクリアできることを確かめてください。

そうしたら次はズルをするなど「開発者が推奨していないプレイ」をしたらどうなるかをチェックしましょう。ズルとは例えばレースゲームでショートカットしようとするなどです。ここでの対応(ズルを許すか許さないか)はゲームによりけりだと思いますが、いずれにしてもバグが発生しては困るのでこのような場合も漏れなくチェックする必要があります。

そして最後は思いつく限りの「想定外のプレイ」を試します。想定外のプレイとは例えば

  • めちゃくちゃな入力をする(コントローラーをガチャガチャする、ボタンを連打する等)
  • 一見すると登れなさそうな崖を登ろうとする
  • わざとゲームオーバーにしようとする

といった感じです。プレイヤーは意図的かどうかにかかわらずゲーム開発者の想定外のプレイをすることがよくあるのですが、想定外のプレイをしてバグったとしてもバグはバグであり低評価をつけられてしまいます。

したがって開発者側としては想定外も想定内にするくらいのつもりできちんと穴をふさいでおく必要があります。なかなか難しいことですが想像力を働かせて色々なパターンをチェックするようにしましょう。

レベルデザインを効率化するアセットを導入する

次に二つ目の対策はレベルデザインを効率化するアセットを導入することです。

Unityなどのゲームエンジンは汎用性を重視しているせいか、デフォルトの状態だとレベルデザインにおいて便利な機能が不足している場合が多いです。そしてそのままの状態で複雑なステージを作ろうとするとオブジェクトの配置ミスなどを誘発することがあるため、もし可能であればレベルデザインを効率化するアセットを導入することをお勧めします。

面倒くさがらずにデバッグ用の機能を用意する

三つ目は面倒くさがらずにデバッグ用の機能を用意することです。

個人開発の場合はゲーム本体を作るのに忙しくてデバッグ用の機能まで手が回らないことがよくありますよね。しかし念入りにテストプレイすることを考えるとデバッグ用の機能は必須ですから、面倒でも予めそのような機能を用意しておくことをお勧めします。

なおデバッグ機能というと作るのが難しく思えるかもしれませんが

  • ゲーム実行時に指定した場所にワープする
  • 特定のフラグが立っているときだけ無敵化する

といった単純なものでも十分です。ご自身の作れる範囲でかまいませんから快適にテストプレイするための機能を揃えておきましょう。

…あ、ちなみにまさかそんなうっかりさんはいないとは思いますが、デバッグ機能はリリースするゲームに絶対に含まれないように注意してくださいね。デバッグ機能をプレイヤーに使われるとゲームにならなくなっちゃうので…。

リリースするゲームにデバッグ機能が含まれないようにするためには、Unityなら例えば「Debug.isDebugBuild」という変数で「今テストプレイかどうか」を取得できる(=エディタ上や開発ビルドならtrue)のでそれで場合分けすることができますし、中級者以上の方であればアセンブリ定義を使ってデバッグ機能用のC#スクリプトがエディタ上だけで使えるようにすることもできます。そういった機能を上手く使うとよいでしょう。

データの入力ミスを防止・チェックする仕組みを作る

四つ目はデータの入力ミスを防止したりチェックしたりする仕組みを作ることです。

ゲーム開発では必ずと言っていいほどデータを手入力する場面があると思います。しかし人力で入力するということはそこにミスが紛れ込む可能性があるということでもあります。そこでできればデータの入力ミスを防止・チェックする仕組みを作っておくとバグを予防することができます。

例えば「必ず0~99の範囲に収めなければならないデータ」があるとします。このような場合は次のような仕組みを作っておくのがいいと思います。

  • 入力欄に0~99の値しか入力できないようにする
  • そのデータを利用するプログラム側で0~99の値しか受け付けないようにして、それ以外の値が入ってきたらエラーを出すようにする

まずはそもそも決められた値以外を入力できないようにします。これでデータを入力する際のミスは防ぐことが可能です。ただしそれだけでは不十分なので、念のためそのデータを利用するプログラム側で決められた値のみを受け付け・それ以外の値が入ってきたらエラーが出るようにするのがいいでしょう。

おわりに

以上、かなり長くなってしまいましたが自作ゲームのバグを減らす方法を具体的にご紹介しました。

既に書いたとおりバグの発生を完璧に予防したり、発生するバグを根絶したりするのは不可能に近いです。しかし工夫次第でバグを予防したり減らしたりすることは十分可能なので、ぜひ上記のコツを参考にしていただきゲーム開発にお役立ていただければと思います。

この記事がゲーム開発のお役に立てば幸いです。