blogプログラムやゲーム、旅、愛する家族について綴っていきます。バグ1.はじめに長年コンシューマゲーム開発に携わってきた中で毎回思うことがある。開発の終盤には必ずテストプレイが行われ、毎回大量のバグと闘っている。大量のバグが出ているのか、どうしたら大量のバグが出なくなるのかについて考え、まとめてみることにした。バグは「潰す」のではなく「出さない」ことに注力すべき!バグを「出さない」ようにするためには以下の2つを心掛ける必要がある。「自分」のところでいかにして「バグを出さない」ようにするか。実装確認をする自己完結させる常に自分を疑う「他の人」のところでいかにして「バグを出させない」ようにするか。エラーは早い段階でエラーとして処理するエラーを分かりやすく教えてあげるバグを「出している」というのは新人でもベテランでも一緒。学術的なコーディングスキルがあっても、新人が書いたコードでもバグは出る。実装範囲の大小も関係ない。「責任」を持って取り組んでいるかで、バグを出すか出さないかに違いが出てくる。これがイケてる(=バグを出さない)プログラマである。大量のバグを潰すのは相当の労力を必要とする精神衛生上もよろしくないコンシューマ開発において一つのプロジェクトでの不具合報告が数千件を超えるケースも珍しくない。それが仕様の増大化、複雑化によるものなのか、短期のプロジェクトによるものなのか原因は色々考えられる。最近ではゲームのボリュームやオンライン要素などに伴いテストプレイは最低でも2カ月、3か月かけて行われるようになっている。事例2ジャンル:アクションプラットフォーム:PSVita言語:日本オフラインのみ不具合報告件数:2000件テストプレイ期間:2ヶ月プロジェクトの規模、内容にもよるが、概ねこれくらいの期間のテストプレイが行われ、これぐらいの不具合報告の件数がある。最近ではリリース前のテスプレイだけではなく、開発途中の各種マイルストーン毎にもテストプレイが行われたり、プロジェクトによっては開発中も開発メンバーのみで不具合報告を行っているケースもあるので実際にはもっと多くの不具合報告が行わていることになる。バグが多ければ多いほどデバッグの工数が増える。大量のバグのチケットを捌くことで本来のテストプレイの質も下がり致命的な不具合が見過ごされる可能性がある。保守性XPコードレビューペアプログラミングテストツールユニットテストCppUnitGoogleTest静的解析CoverityデバッガCIJenkinsここに挙げたのは一例でプロジェクトに合わせて実践したり、導入したりしている。コーディング規約は保守性は高くなる。導入コストも低く、プロジェクトでやっているところも多い。プロジェクトで徹底させる必要がある。ユニットテストは社内ライブラリやプロジェクト共有のプログラムで継続的に使用されるようなケースでは適用できる可能性はある。ゲーム側の場合、短期の開発で導入が困難であったり、ゲーム側の場合テストする範囲としての切り出しが困難だったりする。静的解析はあると良い。運用しないと意味がない。大量の報告があると放置されたり、どう対処すべきかなどのスタディが必要になってくる。コードレビューも継続するのが大変。指摘するのが良いのか、レビュワーのスキルによってもばらつきが出やすい。コンシューマ開発でペアプログラミングを実践しているところはあるのだろうか?デバッガはバグを追うのには必須。ステップで見ていくだけでなく、使い方もちゃんと習得する必要がある。大量不具合が出ている場合は大量に潰すことには変わりない。CIは安定的なビルドを提供するために必要。ビルドは通っているけど朝の最新版で起動したら落ちたという場合もある。手法はたくさんあるが全てを導入したり、継続的に行うのは困難なものある。4.まずは「心掛け」を考えよう方法論を駆使すれば効率は良くなるかもしれない方法論はまた別の機会にでも議論するそれ以前にコーディングする人の「心掛け」一つでバグは減らせるバグを出さないように「心掛ける」ができていないと、方法論を駆使しても結局バグが出ることには変わりがない。5.そもそも大量に出ているバグは...それって本当に致命的な不具合?デバッグ期間を実装確認と勘違いしていないか?プログラムなんだから「バグ」が出て当然と思っていないか?5000件出ている不具合の全てがプログラムによるものではない。仕様的な不具合や文言の不具合、キャラクタモデルの形状のチェックやフィールドのコリジョンチェック、モーションのチェックなどリソースの不具合も相当な数含まれている。この様な不具合の報告もあったりする。不具合報告1特定のスキルを使用すると必ずハングアップします。手順:1.主人公のレベルを50にします。2.それで習得できるスキル「×××」を使用します。3.ハングアップします。他のスキルを使用してもハングアップしません。::不具合報告2ステータス画面で特定のキャラクタを開くと必ずハングアップします。手順:1.キャラクター「○○○」を選択します。2.ステータス画面を開きます。3.ハングアップします。::不具合報告3オプションメニューのカーソル移動時のSEが再生されていません。再現頻度:10回/10回中この場合、カーソル移動音が再生されるのが適切かと思われます。手順:::これは単に実装漏れというか、テスト前に誰も確認していないのか?実装したプログラマ、仕様を書いたプランナー、音をつけるサウンドエンジニア、もちろんディレクターも。誰もチェックしていないのにテストプレイで指摘って、実装者が確認すれば見つけられたバグもあるはず。誰でもとりあえず動いているものは大丈夫だと思う。当然のことながら不具合が出ない限りは何もしない。不具合が起きた時に特定しづらいケースを生むことがよくある。エラーチェックを強化した途端、動かなくなることもしばしばある。サンプルプログラムの流用SDKのサンプルプログラムや書籍のサンプルプログラムをそのまま流用しているケースも見かける。サンプルプログラムはあくまでもサンプル。動作保証はまったくない。バグあって当然だと思わなければならない。昔、DirectXのサンプルプログラムにIMEを使ったサンプルがあり、そのサンプルに不具合があった。それをそのまま流用しているケースが多々あり、マイクロソフトのセミナーでも「こういう対応をお願いします」という話をしていた。コピペの件バグを人のせいにするベテランでも初心者でもある。自分のところは大丈夫と誰もが思う。ベテランの場合はさらに厄介なのが自分の非をなかなか認めない。テスターさんからツッコミがあったら修正とりあえず実装してみてテスターさんからツッコミがあったら修正。最近はプログラマだけでなく、プランナー側でもよくあるケース。メッセージやテキストなどの文言であればまだしも(本当はこれも良くはないが)、ヘタをするとフローまで変更する可能性まで出てくる場合がある。テストの段階では仕様も含めてFixしている必要がある。仕様通りに実装すればよいというものではない。完璧な仕様書は存在しないのだから。「とりあえず動いている」は「動いていない」と思うようにする。サンプルプログラムを流用サンプルプログラムはコピーしない。参考にしても良いが自分でちゃんと咀嚼し、理解して実装すること。バグを人のせいにする自分の不具合は素直に認めよう。実装してみて問題があるところや改善点があるのであれば早くフィードバックしよう。7.「たち」の悪いバグメモリリークメモリ破壊による落ちバグエージングで出るバグリソースが不足して落ちるバグ他人のバグメモリ、リソース関連のバグメモリリークやメモリ破壊、エージングバグはマスター直前になって現れる「たち」の悪いバグ。再現性が低く、原因を特定するのがものすごく困難な厄介なバグ。関連している人全員が疑われるために出してはいけない。いった不具合こそ真の不具合である。組み合わせのワーストのチェックが抜けていた。マスター提出後に特定のキャラクタ、装備、ミッションでメモリ不足が起きた。各キャラクタ、装備でのメモリ使用量のリスト、各フィールドのメモリ使用量、各ミッション開始時のメモリ使用量のリストを出力してテスターに連絡はしていてもこういったチェックが抜けてしまうケースがある。60時間後に出た不具合MMOのプロジェクト。クライアントにもASSERTと同様の処理を仕込んでおき、エラーが出たらその時のログをファイルに出力してユーザーに報告してもらっていた。大概は「起動できません」といった内容の報告で低スペックによるドライバのエラーなどによるものが大半を占めていた。有益な情報のフィードバックもある。あるときの報告のエラーで普段とは違うエラーが来ていた。自前のwidgetの実装で、そのwidgetをゲーム側で用意したHANDLEで管理していた。そのHANDLEが枯渇してASSERTに引っかかっていた。調べてみるとアイテムウィンドウの詳細ウィンドウの中のタブを切り替えるとウィンドウのHANDLEがリークしていた。デバッグ機能でウィンドウのHANDLE数の表示を追加し、一定数を超えたらゲーム中にワーニングを表示するようにした。シーンの遷移(といってもMMOなのでタイトル画面に戻る時)にリークチェックを挟むようにした。【バグ】マスターまで残り4日!1バイトのメモリ破壊を追え!メモリ、リソースの使用量は常に把握しておく必要がある!他人のバグ他人のバグも厄介。自分のところにきたバグのチケットが調べてみたら他人のバグによるものだった。MMOの事例MMOのプロジェクトで運営が開始されてからクライアント側でファイル読み込み中に読み込みに失敗する不具合報告があった。クライアント側と疑って、読み込みのところのエラーチェックを強化したり、データの整合性チェックする機能を入れたりしたが原因が特定できずにいた。あるときランチャの機能更新があったためエラーチェックを強化したところファイルのダウンロード処理でエラー処理が正しく行われていなかったことがわかった。これによりデータの一部が不正な状態になっていたためにクライアント側で該当のファイルを読みに行った時にエラーになっていた。自分(当事者)が出したバグが他の人たちに影響を与えていることは確か!8.自分のところでバグを「出さない」メインプログラマだと...メインプログラマになると自分のバグより他人のバグに関わる方が多くなる不具合報告のチケットの割り振り良くわからないバグは担当じゃなくても調査しなければならない不具合のチケットの割り振りも意外に時間がかかる。プログラム以外の不具合であっても担当が不明な場合はメインプログラマにチケットが回ってくる。内容を精査し、原因、対応方法なども追記してチケットを担当に割り振る。再現性が低いものは考えられるケースや可能性も検討し、他の人の不具合であっても継続的に調査しなければならない。自分のバグに付き合っている時間が無い。いった「心掛け」が必要?自分のところではバグは出さない!自分のバグは自分のところで完結させる!バグを出してもすぐに特定できるようにする!エラーチェックを強固に!当たり前のことだがエラーをエラーとして処理することが重要!ASSERTをとにかく仕込むようにする。値をセットする場合は有効な範囲かどうかチェックする。配列にアクセスする場合は要素の範囲をチェックする。引数や返却値で受け取ったポインタのチェック。ロジック的に通らないような場所にもASSERTを仕込む。switch文でdefaultが不定であればASSERTにする。chara->Damage(10);}以下の様な実装だとRESULT_OK,RESULT_ERROR以外の処理が想定されていない。}}関数の仕様が変わって返却値が変更されたとしても最初の想定から外れた値が返ってきてもエラーを拾える。SDKやライブラリなども更新されると挙動が変更される可能性がある。続編や移植などの際はこういったところのエラー処理も強化しておく方が良い。}最近では続編や移植で追加、拡張が行われてもエラーチェックを仕込んでおかないと動作してしまう可能性がある。そのようなケースも含め早期にエラーを検出できるので、パフォーマンスに影響がない限りはとにかくASSERTを仕込むようにする。メンバ変数は全て初期化!リリース版のエージングで起きる不具合で、まれに挙動がおかしくなったり、場合によってはハングする不具合を引き起こす。最近では通信、オンラインなどのタイトルも増えているため、同期エラーの原因にもなる。普段から意識して初期化しておかないと、最終的にはCoverityなどの静的解析ツールのやっかいになる。テストケースを考える!自分の実装確認という意味でも最低限のテストケースは考えておく。エンバグを防ぐ意味でも考えておき、実装、修正などを行った後で実装のチェックをする。過去のプロジェクトでは実装者がテスト用のチェックリストを作成し、実装者自らチェックしてからテストに回すということもあった。保守性は重要!そうは言ってもやっぱり保守性は重要。プロジェクトでも他の人のソースも読まなければいけなくなし、続編や移植などで再利用される可能性もある。シンプルで読みやすいコーディングを心がける。ライブラリ、コンパイラすら疑え!ライブラリやコンパイラが引き起こす不具合もある。この場合はきちんと原因を特定するまで調査する必要がある。コンパイラの事例とあるコンパイラで64bitの比較演算子に不具合があった。移植案件でとりあえず動作はしていたが、なぜか特定の挙動だけ呼ばれないという報告が来た。調べてみると以下のようなコードでif文がtrueにならないことが分かった。defineによる定数と64bitの変数の比較演算子が正しくないようだ。}}これを64bitの変数同士での比較や、defineの定数をキャストなどして動作することを確認。コンパイラのメーカーに問い合わせたら「コンパイラのバグです」とのこと。こんなことが、と思うかもしれないが実際にあった話だ。報告しないでゲーム側でこの箇所だけ修正してしまったら、どこか他の場所で同じような不具合が出ても気が付かないままになっていたかもしれない。ライブラリの事例とある携帯機で通信プレイ中にスリープモードに移行した際にハングアップするとの報告が来た。頻度はかなり低く、1日のテストプレイで回しても1、2回程度。ライブラリに渡すパラメータを直前でチェックしたり、ライブラリが返す返却値も全部チェックし、適切に処理するようにしても改善されず稀に不具合が発生した。原因が分からないので問い合わせをしたら不具合が発生したらダンプファイルを送って欲しいとの事。マスター直前だったため土日かけてテストをし、ようやく再現したのでダンプファイルを送ったところ、後日「ライブラリの不具合です。次回のアップデートで修正しますので今回の不具合は問題ありません」とのことだった。9.他の人のところでバグを「出させない」ゲーム開発では多くの人が作成に関わっている。大規模なプロジェクトになると50人とか100人、もっと多いプロジェクトだってある。関わった人の分だけバグが出る。プランナー、レベルデザイン、アーティスト、サウンド、こういったメンバーも当然バグを出す。その分プログラマの作業も増える。最後に不具合の原因を突き止めて解消できるのはプログラマのみ。バグを出させないようにするかが重要。非プログラマによるバグパラメータ、スクリプトなどのリソース化に伴い非プログラマのバグも増えてきている。この様な場合、エラーを早い段階でエラーにする必要がある。パラメータをExcelで管理し、CSVでエクスポートしてデータテーブルとしてゲーム側で読み込ませている。プランナーがパラメータの上限が100にも関わらず110と入力してしまった。ゲーム中はカスタマイズなどでこのパラメータに係数がかかり、実際のパラメータは補正されてしまう。エラーチェックが無い場合テストプレイからの報告で「なんかダメージの値がおかしい気がします」。何度も繰り返し検証を行い、統計を取って不具合かもしれないという不具合報告。プログラマが不具合報告を受けてキャラクタに設定されているパラメータの値を確認する。範囲外の値が設定されているのでプランナーに指示をしてバグチケットを回す。プランナーは修正してチケットを戻す。テスターは修正されたビルドで繰り返しテストを行い、問題ないことを確認してチケットを終了にする。パラメータの設定なのに問題が起こるところまでが遠い。line:[253]不正なパラメータがセットされた段階でエラーにできる。データテーブル(CSV)を読み込んだタイミングでエラーチェックしていたら...起動時のパラメータテーブルの読み込みのタイミングで整合性チェックをしてエラーにできる。起動直後にエラーにできる。Excelからエクスポートするタイミングでエラーチェックをしていたら...プランナーが入力して、エクスポートしたタイミングでエラーにできる。データ入力時にエラーにできる。早い段階でエラーチェックができればゲームを起動しなくてもバグを防ぐことができることになる。不具合の責任の所在もはっきりする。テストプレイの繰り返しのチェックも、プログラマの確認も不要になる。開発当初から防げるバグも...開発当初はリソースが無かったり、パラメータが仮であっても開発の効率化という名目でとりあえず動作させてしまう。そのまま開発を続けていると結局終盤やテストプレイ前にそれらを潰していく必要が出てくる。リソースの不備やパラメータの設定ミスをワーニングとして分かりやすく画面に表示する。WARNING:Effect_Fireを設定しているノード名'Eff_1'が存在しません。effect.csvの設定を確認してください。画面に表示しても非プログラマの担当者は指摘をされないと修正はしない。本来であればエラーなのでASSERTで停止させるのが望ましい。再実装など修正の範囲が大きくなることがある。仕様バグを早めに問題化、表面化させるために1ヶ月単位などのイテレーション開発が効果がある。1ヶ月ごとに成果物を確認し、仕様面での不具合やブラシュアップを早めることで仕様バグを減らすことができる。毎日動作するものをリリースする。自動チェック、エージングの環境も整える。ビルドが通るだけでなく、起動チェック、例えば先ほどの起動時のパラメータのチェックが実装されていればこの時点でパラメータの不備が検出できる。人力のテストプレイにも限界はあるので自動化の環境は早めに準備する方が良い。エージング環境が整えば人が作業しない時間を有効に活用できる。アクションゲームでバトルシーンをキャラクタの組み合わせを変更しながらAIでプレイさせ、ミッションを繰り返しプレイするエージング環境を用意したとする。退社する時間から出社するまでの時間、例えば22時から9時までエージングをかければ11時間×機材分のテストプレイが行える。人の手でしか出せないバグもあるだろうがエージングで発見できるバグも存在するし、逆に不具合が出なければ正常に動作している実績にもなる。これらを問題視して環境の構築、改善ができるのはプログラマのみ。10.最後にバグを完全に出さなくすることは不可能...でも、バグを減らすことは可能!「バグは出て当たり前」という考えは捨てよう!バグは「潰す」のではなく「出さない」ことに力を注ぐべき!個人が少し意識するだけでも格段に「バグ」は減るハズ商品のクオリティを向上させるためにも、開発終盤のデバッグのストレスを軽減するためにも「心掛ける」ようにしよう。BotW』の講演は聞きたかった...けど記事やら話を聞い...こんにちはタケゾーです。...2017-07-04【UE4】エディタ実行とStandalone実行で挙動が異なるUE4.16の環境。リーダブルコードを読みました。はてなブログProプログラムやゲーム、旅、愛する家族について綴っていきます。C++はてなブログをはじめよう!takezoh_1127さんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。