この記事では、Unityでより安定したプロジェクトを出荷するのに役立つ、さまざまなテストプラクティスを紹介します。
テスト、または品質保証(QA)は、ゲーム開発サイクル全体を通して実行されるべき重要なプロセスです。経験豊富な開発者なら知っているように、あなたが書いたコードはすべてテストされる必要がある。
あなたが独立した開発者であろうと、大規模なチームの一員であろうと、採用できるテストとQAの方法はさまざまだ(実績のあるスタジオには、専任のQAエンジニアがいることが多い)。
もしあなたがスポーツをするのが好きなら、あるいは見るのが好きなら、多くの試合で優勝を勝ち取るのはディフェンスだということを知っているだろう。QAプロセスは、可能な限り最高のパフォーマンスと安定性を備えたゲームをリリースするために、チャンスを逃さないための防衛戦略だと考えてください。
テストは、コードのバグ、アートワークのビジュアル的なアーチファクト、ゲームデザインやゲームプレイにおけるユーザーエクスペリエンスの問題などを発見するために非常に重要です。どんなに技術的に素晴らしいゲームを開発しても、10回中9回クラッシュするようでは、ユーザーはすぐに見切りをつけてしまうだろう。
ゲーム開発プロセスの最後にテストを残すことは避けましょう。テストを一連の流れの中の一つの段階として考えるのではなく、ゲーム開発の他の段階を支える継続的なプロセスとして捉えることが助けになる。アイデアやプロトタイプを、生産中や出荷前にテストしましょう。ゲームのアップデートをリリースするたびに、このプロセスを繰り返します。
プロジェクトのさまざまな段階に適した、さまざまなテスト技法があります。
あなたはQAチームを持たない小さなスタジオのメンバーですか?仲間を集めてテストを手伝ってもらうか、サードパーティのQAプロバイダーを選ぶ。社内にQAチームを持つスタジオでも、ローカライゼーション・テスト(LQA)などの追加テスト・サービスのために外部の会社を利用することが多い。
QAのサブセットとも言えるプレイヤーテストは、あなたのゲームがターゲットとするオーディエンスとマーケットに確実に響くようサポートします。これは、開発段階でゲームを改善するために選手から貴重なフィードバックを得られるプロセスだ。
選手テストに参加すべき人は、テストの具体的な目標によって異なる。しかし、一般的には、そのゲームのターゲットオーディエンスを代表する多様なプレーヤーを参加させることが重要である。プレイヤーテストにさまざまなプレイヤーを参加させることで、ゲーム開発者はさまざまな視点からのフィードバックを集め、ゲームが幅広いユーザーにアピールできるようにすることができます。
以下のセクションでは、一般的なテスト技法について説明します。これらの方法を組み合わせることで、コードベースが可能な限りスムーズに動作するようにすることができる。
また、ターゲットプラットフォーム内のさまざまなデバイスでゲームをテストすることも重要です。これは特にモバイルゲームに当てはまる。異なるオペレーティングシステム、画面サイズ、最低スペックのモバイルデバイスでテストしたい。そうすることで、最低限必要なデバイスのベンチマークになり、アプリケーションに機能を追加する際には、各カテゴリを常に見直す必要があります。
さまざまなプラットフォームでのテストは、ゲームを破壊する可能性のある問題を特定するだけでなく、モバイルゲームのバッテリーの消耗や熱によるオーバーヒートなど、長期的または間接的な影響を理解することも重要です。
ユニットテストは、ゲームの個々のユニットやコンポーネントを分離してテストする技術であり、開発プロセスの早い段階でバグを発見し、コードに変更を加えても既存の機能が壊れないようにするのに役立ちます。
ユニットテストは、コードの特定の動作を実行する小さなテストケースを書くことによって行われる。テストは、個々のスクリプト、GameObject、またはゲームの特定の機能に対して実行することができます。
ゲーム開発では、ユニットテストの手動と自動の両方の方法を使用する必要があります。
手動ユニットテスト
手動テストでは、人々がゲームをプレイして、その特徴や機能をテストします。UIのバグや、ゲームプレイやデザインのバランスの悪さ、不完全さなど、自動テストでは発見できない問題があるからだ。
Unityでの手動ユニットテストの例としては、関数のテスト条件を追加し、Debug.Logを使用してUnityのPlayモードを使用して合格または不合格の基準(テストシナリオの出力)を出力します。
ユニットテストの自動化
単体テストの自動化には、個々のユニットやコードの断片を分離して自動的にテストするコードを書く必要がある。Unityでは、スクリプト、コンポーネント、その他のゲームコードのユニットに対してテストを記述し、テストスイートの一部として実行することができます。
Unity Test Framework(UTF)パッケージは、Unity EditorのEditモードとPlayモードの両方で、開発者が独自の自動テストを書くためのフレームワークを提供します。UTFは、NUnitを参照するアセンブリ内のテストを探します。このようなアセンブリはTestAssemblies と呼ばれる。プレイモードとエディットモードのテストは別々のアセンブリにする必要がある。
UTF では、テストケースの実行と管理を支援するテストランナーウィンドウをエディターに用意しています。
UTFはテスト・アセンブリ定義を使用するため、プロジェクトをランタイム・アセンブリ定義に分割する必要があります。これは、開発プロセスの早い段階で計画すれば容易なことであり、よりモジュール化されたコードを書くことにもつながる。
UnityパッケージのPerformance Testing Extensionは、UTFで使用できる拡張機能です。テストケースから測定を行い、カスタムメトリクスを提供するための追加APIを提供します。
ゲーム開発のためのUnityテストフレームワークチュートリアルを読んで、Unityテストフレームワークを始める方法を学びます。
テスト駆動開発(TDD)は、機能の一部について、それを実装する実際のコードを書く前にテストを書くことを含むテクニックである。このプロセスでは通常、失敗するテストを書き、テストをパスさせるために必要な最小限のコードを書き、次のテストケースの設計に移る前に、保守性を高めるためにコードをリファクタリングする。
TTDはゲーム開発ではかなり珍しい(主流のソフトウェア開発では一般的)。これはおそらく、プロトタイプを作成し、楽しく魅力的なゲームプレイを作り上げるには、直感に反するプロセスだからだろう。
しかし、ゲームを壊すような変更は即座に失敗したテストケースを作成するため、ゲームの壊れた部分を特定するプロセスをスピードアップすることができます。
UnityでのTDDについては、ブログポスト "Test-Driven Development with the Unity Test Runner"を参照してください。
コードカバレッジ
TDDやユニットテストを実装したい場合は、UnityCode Coverageパッケージの使用を検討してください。コード・カバレッジ・パッケージは、UTFと併用することで、プロジェクト内のどのコード行がテストされているかを表示し、プロジェクトの脆弱な部分やテストされていない部分を特定するのに役立ちます。
コード・カバレッジは、プレイ・モードでゲームをテストしている間にレポートを生成することもできます。これは、定期的にテストを実行しているコードの一部を、たとえユニットテストの対象外であっても表示する。しかし、コード・カバレッジ・レポートでは、テストでカバーされたコード行はわかりますが、ロジックがコード中を流れるさまざまな経路はわかりません。
ブログ記事「コード・カバレッジ」を参照:テストのギャップを見抜く" 詳しくはこちら。
統合テストとは、システムのさまざまなコンポーネントを一緒にテストし、それらが正しく動作することを確認する手法である。これには、ゲーム内で異なるGameObject、スクリプト、またはシステムがどのように相互作用するかをテストすることも含まれます。統合テストはその範囲が広いため、データの流れやコンポーネント間の通信の問題など、単体テストでは見つからないバグを発見するのに役立つ。
例えば、個々のユニットテストを使う代わりに、プレイヤーが弾丸をインスタンス化する武器を発射し、弾丸が敵に命中して敵を殺し、プレイヤーが敵を殺したことで得点を獲得し、プレイヤーが一定の得点に達すると実績がアンロックされる、という一連の流れ全体に対して統合テストを書くことができる。
Unityで統合テストをビルドするには、UTFを使用できます。SetUpメソッドとTearDownメソッドを使用すると、一度に多くのシステムをテストする環境を作成できます。プレーヤーのロジックと入力は、特にコマンドパターンを使って実装している場合(新しいInput Systemを使っている場合は、そうなっている可能性が高い)、プレーヤーの入力からゲームロジックへの完全な流れをテストするために、シミュレートすることができる。
回帰テストは、ソフトウェアや機能が修正または更新された後に正しく動作することを検証する方法です。リグレッション・テストの目的は、コードベースに変更を加えても、ソフトウェアに新たなバグやリグレッションが発生しないことを確認することである。このテクニックは、ゲーム実況のような、頻繁に更新される大規模で複雑なアプリケーションに適用するのが最適である。
ゲームの世界やメカニクスが拡大するにつれて、回帰テストの必要性も高まり、コストがかさみます。したがって、可能な限り自動化することが重要だ。回帰テストは、特に複数のプラットフォームをターゲットにしている場合は、新機能の開発と並行して行うべきである。
リグレッションテストは、大規模でライブなゲームに最も適していますが、ソフトウェアの複雑さ、変更または更新の頻度、影響を受ける機能の重要性(ミッションクリティカルまたはセーフティクリティカルシステム)などの他の要因も、その必要性を決定します。
機能テストは、システムまたはソフトウェアアプリケーションの機能を、その機能要件に照らしてテストすることによって評価する。システムの特徴、ユーザー・インターフェース、データベースとのやりとり、その他動作や機能に影響を与える部分のテストが含まれる。機能テストの目的は、システムやアプリケーションが顧客やエンドユーザーから提供された要件や仕様を満たしているかどうかを検証することです。
ほとんどのユニットテストは、コードを通る特定のパスに焦点を当て、特定の入力に基づいて正しい出力を返すかどうかをテストする。ゲームが設計された通りに動くかどうかをテストすることはできない。
機能テストはそのためのアプローチである。各機能や特徴を元の設計と比較し、出力が期待に応えているかどうかを確認する。これには、ゲームのコントロール、ゲームプレイの仕組み、全体的なユーザー体験のテストが含まれます。
ゲームを開発する際に機能テストを念頭に置き、「こうなったら、こうする」というアプローチでタスクを作成することは有効である。例えば、プレイヤーがスペースバーを押すと、キャラクターはジャンプする:これは、ある機能をどのように実装すべきかという開発者への指示であると同時に、テスターがそれに対してテストするための受け入れ基準でもある。時には、ある機能がいつ使用可能で、いつ使用できないかといった、いくつかの関連する受け入れ基準を含むこともある。しかし、一般的には一つの機能に焦点を当てるべきである。
機能テストが最も威力を発揮するのは、要件が明確に定義され、スコープアウトされたときである。そのため、フリーランサーやスタジオと契約し、ゲームプレイやゲーム世界のアセットを提供するのに役立ちます。
自動と手動の両方の機能テストを実行できる。ブラックボックス」テスト(内部構造を知らずにシステムをテストする)と「ホワイトボックス」テスト(内部構造を知ってシステムをテストする)の両方を実行すべきである。
パフォーマンステストでは、ゲームがさまざまなハードウェアおよびソフトウェア構成でスムーズかつ効率的に動作することを確認するためのテストを行います。これは、プロファイリングおよび一般的なパフォーマンス最適化ワークフローと密接に関連しており、この実践は、ゲームのパフォーマンスに影響を与える可能性のあるパフォーマンスのボトルネックや問題を特定するのに役立ちます。
主な目的は、異なる作業負荷条件下でシステムやアプリケーションの性能を評価することである。ゲーム開発において、パフォーマンステストは、ゲームが許容できるレベルのパフォーマンス、フレームレート、応答性、安定性で動作し、メモリを最も効率的に使用できるかどうかを測定します。
さまざまな種類のパフォーマンステストがある:
- 負荷テスト:高負荷がかかったときのゲームのパフォーマンスを決定する。
- ストレステストプレイヤーの突然の活動増加など、予期せぬ事態にゲームがどのように対処するかを評価する。
- 持久力テスト:長期にわたる試合のパフォーマンスを評価する。
ユニティ・プロファイラー
UnityのProfilerツールは、開発中のゲームのパフォーマンスを分析するのに役立ちます。エディターで開発中に概要を確認することも、ケーブルまたはローカルネットワークでマシンに接続されたデバイスで確認することもできます。
Profilerは、取得したいデータの種類と、Profilerウィンドウでの視覚的な表示方法の両方をカスタマイズするために拡張することができます。
Unite2022のセッション「How to customize performance metrics in Unity Profiler」で、Profilerのカスタマイズについて詳しく説明します。
また、自動パフォーマンス・テストを作成するための独自のツールを作成することもできる。モニュメント・バレーの開発者であるustwo GamesがUnite 2022で行った講演は、これにどうアプローチするかについての興味深い議論だ:"アルバを作る:" パフォーマンスの高いオープンワールドゲームを作るには?
テストはバグの発見やパフォーマンスの監視だけにとどまらない。時には、あるゲーム機能の2つのバージョンを比較して、どちらがプレイヤーからのエンゲージメントが高く、パフォーマンスが高いかを判断したい場合があります。これはA/Bテストと呼ばれる。
A/Bテストは、ゲームのバランスに影響を与えるようなキャラクターや武器のステータスを変更する場合に便利ですが、ゲームメカニクスは変更しません。他の例としては、異なるチュートリアル、ゲームメカニクス、ユーザーインターフェイスの有効性を比較するために行われるテストがある。2つのグループから収集したデータを分析することで、どちらのバージョンのゲームや機能がより効果的かを特定し、どの変更を最終製品に取り入れるかについて、データに基づいた決定を下すことができます。
A/Bテストでは、プレーヤーをランダムに2つのグループに割り当てる:一方のグループはオリジナルバージョンのゲームや機能をプレイし(対照グループ)、もう一方のグループは修正バージョンのゲームや機能をプレイする(実験グループ)。
重要なのは、少人数のプレイヤーがさまざまな変更によってどのような影響を受けたかについてのデータを収集し、どのアプローチを広範なアップデートとしてリリースするかについて設計上の決定を下すのに役立てることです。
A/Bテストは、ユーザーエクスペリエンスを向上させ、プレーヤーのエンゲージメントとリテンションを高めるのに役立ちます。ユーザーのエンゲージメントやリテンションがわずかでも改善されれば、大きなインパクトを与えることができる。
A/Bテストは通常、バックエンドのサービスとして提供され、プレイヤーは異なるバリアントを使用していることに気づきません。Unity Gaming Services(UGS)には、ゲーム内でA/Bテストを実行するのに役立つツールがあります。また、UGSのバックエンドサービスのサンプル集であるUGSユースケースをチェックして、ゲーム内でA/Bテストをどのように設定するかのサンプルを調べることもできます。
Cloud Diagnostics Advancedは 、Backtraceを搭載したクラッシュレポートおよび分析ツールで、Unityと統合することにより、開発者にゲームのクラッシュや例外に関する詳細な情報を提供します。クラッシュが発生すると、Cloud Diagnostics Advanced は、環境、コールスタック、ヒープ、およびレジスタに関する情報を含む、クラッシュ時のゲームの状態のスナップショットをキャプチャします。このスナップショットはBacktraceサーバーに送られ、そこでクラッシュの根本原因を特定するために分析される。
Cloud Diagnostics Advancedは、詳細な分析およびレポートツールも提供し、ゲームのパフォーマンスと安定性の長期的な傾向やパターンを特定するのに役立ちます。重複排除アルゴリズムは、一般的なクラッシュをコード内の根本原因ごとに分類し、どのエラーを最初に修正すべきか優先順位をつけて、可能な限り多くのプレーヤーの安定性を向上させるのに役立てることができる。
どのようなテスト技法を使うにせよ、ゲームをどのようにテストするかの計画を立て、テストが開発プロセスの不可欠な一部であることを確認することが重要です。これらのテクニックを組み合わせることで、Unityゲームを最高品質で制作することができます。
プログラマー向けの新しい電子書籍シリーズがUnityから無料で提供されている。各ガイドは経験豊富なプログラマーによって執筆されており、開発チームにとって重要な特定のトピックに関するベストプラクティスを提供している。
C#スタイルガイドを作成する:スケールする、よりクリーンなコードを書く よりまとまりのあるコードベースを作成するためのアプローチを統一するために、スタイルガイドの開発をチームに指導する。
ゲームプログラミングパターンでコードをレベルアップSOLIDの原則と一般的なプログラミングパターンを使用して、Unityプロジェクトでスケーラブルなゲームコードアーキテクチャを作成するためのベストプラクティスを紹介します。
UnityでScriptableObjectsを使用してモジュラーゲームアーキテクチャを作成するは、ゲーム制作におけるScriptableObjectのデプロイのベストプラクティスを提供します。
このシリーズは、経験豊富なクリエイターに実用的なヒントとインスピレーションを提供するために作成しました。しかし、それらはルールブックではない!Unityプロジェクトを構成する方法はたくさんあります。各推奨、ヒント、パターンのメリットとデメリットを同僚と評価してから導入してください。
Unityのベストプラクティスハブで、より高度なガイドと記事をご覧ください。