ゲームループ テストを使用すると、ゲームエンジンにネイティブなテストを記述し、Test Lab を使用して特定のデバイスでテストを行うことができます。これにより、UI やテスト フレームワークの違いを気にせずにテストを実施できます。ゲームループ テストでは、実際のプレーヤーの動作をシミュレートします。Test Lab でテストを行うと、高速かつスケーラブルな方法でゲーム パフォーマンスを検証できます。
このページでは、ゲームループ テストを実施して、その結果を Firebase コンソールの [Test Lab] ページで確認し、管理する方法について説明します。カスタムテストの結果の書き込みやテストの早期終了などのオプション機能でテストをカスタマイズすることもできます。
ゲームループ テストとは
ループは、ゲームアプリのテストの全体または一部を通しで実行します。ゲームループ テストは、ローカルのシミュレータで行うことも、Test Lab の一連のデバイスで行うこともできます。ゲームループ テストは次の目的で使用できます。
エンドユーザーがプレイするのと同じ方法でゲームを実行する。ユーザーの入力はスクリプト化できます。また、ユーザーをアイドル状態にすることもできます。AI を実装している場合は、ユーザーを AI で置き換えることも可能です(たとえば、カーレース ゲームでユーザーの入力の代わりに AI のドライバを配置できます)。
最高品質の設定でゲームを実行し、デバイスの対応状況を確認する。
技術的なテストを行う。たとえば、複数のシェーダをコンパイルして実行し、予期した結果が出力されるかどうか確認します。
ステップ 1: Test Lab のカスタム URL スキームを登録する
まず、Firebase Test Lab のカスタム URL スキームをアプリに登録する必要があります。
Xcode でプロジェクト ターゲットを選択します。
[Info] タブをクリックし、新しい URL タイプを追加します。
[URL Schemes] フィールドに「
firebase-game-loop
」と入力します。また、<dict>
タグ内の任意の場所で、プロジェクトのInfo.plist
構成ファイルにカスタム URL スキームを追加することもできます。<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLName</key> <string></string> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLSchemes</key> <array> <string>firebase-game-loop</string> </array> </dict> </array>
これで、Test Lab を使用してテストを行うようにアプリが構成されました。
ステップ 2(省略可): 複数のループを実行するようにアプリを構成する
アプリに複数のカスタム URL スキームを登録し、テストで複数のループ(シナリオ)を実行する場合は、起動時にアプリで実行するループを指定する必要があります。
アプリのデリゲートで、application(_:open:options:)
メソッドをオーバーライドします。
Swift
func application(_app: UIApplication,
open url: URL
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
if components.scheme == "firebase-game-loop" {
// ...Enter Game Loop Test logic to override application(_:open:options:).
}
return true
}
Objective-C
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary <UIApplicationOpenURLOptionsKey, id> *)options {
if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
// ...Enter Game Loop Test logic to override application(_:open:options:).
}
}
テストで複数のループを実行すると、アプリの起動に使用される URL に現在のループがパラメータとして渡されます。カスタム URL スキームの取得に使用される URLComponents
オブジェクトを解析して、現在のループ番号を取得することもできます。
Swift
if components.scheme == "firebase-game-loop" {
// Iterate over all parameters and find the one with the key "scenario".
let scenarioNum = Int(components.queryItems!.first(where: { $0.name == "scenario" })!.value!)!
// ...Write logic specific to the current loop (scenarioNum).
}
Objective-C
if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
// Launch the app as part of a game loop.
NSURLComponents *components = [NSURLComponents componentsWithURL:url
resolvingAgainstBaseURL:YES];
for (NSURLQueryItem *item in [components queryItems]) {
if ([item.name isEqualToString:@"scenario"]) {
NSInteger scenarioNum = [item.value integerValue];
// ...Write logic specific to the current loop (scenarioNum).
}
}
}
ステップ 3: テストを作成して実行する
Test Lab のカスタム URL スキームを登録したら、Firebase コンソールまたは gcloud beta CLI でテストを行います。アプリの IPA ファイルをまだ生成していない場合は、生成します(このファイルは後で必要になります)。
Firebase コンソールでテストを行う
プロジェクトを作成していない場合は、Firebase コンソールを開いて作成します。
Firebase コンソールの Test Lab ページで、[最初のテストの実行] > [iOS ゲームループ テストの実行] の順にクリックします。
[アプリのアップロード] セクションで [参照] をクリックし、アプリの IPA ファイルを選択します(まだ作成していない場合は、アプリに IPA ファイルを生成します)。
省略可: 一度に複数のループ(シナリオ)を実行する場合や、特定のループを選択して実行する場合は、[シナリオ] フィールドにループ番号を入力します。
たとえば、「1-3、5」と入力すると、Test Lab はループ 1、2、3、5 を実行します。デフォルト([シナリオ] フィールドに何も入力しない場合)では、Test Lab はループ 1 のみを実行します。
[デバイス] セクションで、アプリをテストする物理デバイスを 1 つ以上選択し、[テストを開始] をクリックします。
gcloud beta CLI でテストを行う
まだ実施していない場合は、ローカルの gcloud SDK 環境を構成して、gcloud beta コンポーネントをインストールしてください。
gcloud beta firebase test ios run
コマンドを実行し、次のフラグを使用して実行環境を構成します。
ゲームループ テストのフラグ | |
---|---|
--type
|
必須: 実施する iOS テストのタイプを指定します。テストタイプとして |
--app
|
必須: アプリの IPA ファイルの絶対パス(Google Cloud Storage またはファイル システム)。このフラグは、ゲームループ テストの場合にのみ有効です。 |
--scenario-numbers
|
アプリで実行するループ(シナリオ)。1 つのループ、1 つまたは複数のループ、あるいはループの範囲を入力できます。デフォルトのループは 1 です。
たとえば、 |
--device-model
|
テストを行う実機(使用可能なデバイスで使用できるデバイスを確認してください)。 |
--timeout
|
テストの最大期間。期間を表す整数を秒単位で入力します。より長い時間を指定する場合は、整数と時間単位を入力します。 例:
|
次のコマンドでは、iPhone 8 Plus でループ 1、4、6、7、8 を実行するゲームループ テストを行います。
gcloud beta firebase test ios run --type game-loop --app path/to/my/App.ipa --scenario-numbers 1,4,6-8 --device-model=iphone8plus
gcloud CLI の詳細については、リファレンス ドキュメントをご覧ください。
ローカルでテストを行う
テストをローカルで行うには、ゲームアプリをシミュレータに読み込み、次のコマンドを実行します。
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://
シミュレータの UDID を確認するには、
instruments -s devices
コマンドを実行します。実行中のシミュレータが 1 つだけの場合は、SIMULATOR_UDID の代わりに特殊な文字列
"booted"
を入力します。
テストに複数のループが含まれている場合は、ループ番号を scenario
フラグに渡し、実行するループを指定できます。テストをローカルで行う場合、一度に実行できるループは 1 つだけです。たとえば、ループ 1、2、5 を実行する場合は、ループごとに別々のコマンドを実行する必要があります。
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=1
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=2
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=5
早期にテストを終了する
デフォルトでは、すべてのループが実行された場合でも、ゲームループ テストは 5 分間のタイムアウトに達するまで継続されます。タイムアウトに達すると、テストは終了し、保留中のループはすべてキャンセルされます。テストを高速化または早期に終了するには、アプリの AppDelegate で Test Lab のカスタム URL スキーム firebase-game-loop-complete
を呼び出します。例:
Swift
/// End the loop by calling our custom url scheme.
func finishLoop() {
let url = URL(string: "firebase-game-loop-complete://")!
UIApplication.shared.open(url)
}
Objective-C
- (void)finishLoop {
UIApplication *app = [UIApplication sharedApplication];
[app openURL:[NSURL URLWithString:@"firebase-game-loop-complete://"]
options:@{}
completionHandler:^(BOOL success) {}];
}
ゲームループ テストで現在のループを終了し、次のループを実行します。実行するループがなくなると、テストが終了します。
カスタムテストの結果を書き込む
デバイスのファイル システムにカスタムテストの結果を書き込むように、ゲームループ テストを構成できます。この場合、テストが開始すると、Test Lab はテストデバイスの GameLoopsResults
ディレクトリに結果ファイルを保存します(ディレクトリは自分で作成する必要があります)。テストが終了すると、Test Lab はすべてのファイルを GameLoopResults
ディレクトリからプロジェクトのバケットに移動します。テストを設定する際は、次の点に注意してください。
ファイル形式、サイズ、数量に関係なく、すべての結果ファイルがアップロードされます。
Test Lab は、テストのすべてのループの実行が終了するまでテスト結果を処理しません。出力を書き込むループが複数ある場合は、各ループの出力が 1 つの結果ファイルに追加されるようにするか、ループごとに結果ファイルが作成されるようにする必要があります。これにより、前のループの結果が上書きされるのを防止できます。
カスタムテストの結果を書き込むようにテストを設定するには:
アプリの
Documents
ディレクトリに、GameLoopResults
という名前のディレクトリを作成します。アプリのコード内の任意の場所(アプリのデリゲートなど)から、次のものを追加します。
Swift
/// Write to a results file. func writeResults() { let text = "Greetings from game loops!" let fileName = "results.txt" let fileManager = FileManager.default do { let docs = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let resultsDir = docs.appendingPathComponent("GameLoopResults") try fileManager.createDirectory( at: resultsDir, withIntermediateDirectories: true, attributes: nil) let fileURL = resultsDir.appendingPathComponent(fileName) try text.write(to: fileURL, atomically: false, encoding: .utf8) } catch { // ...Handle error writing to file. } }
Objective-C
/// Write to a results file. - (void)writeResults:(NSString *)message { // Locate and create the results directory (if it doesn't exist already). NSFileManager *manager = [NSFileManager defaultManager]; NSURL* url = [[manager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL* resultsDir = [url URLByAppendingPathComponent:@"GameLoopResults" isDirectory:YES]; [manager createDirectoryAtURL:resultsDir withIntermediateDirectories:NO attributes:nil error:nil]; // Write the result message to a text file. NSURL* resultFile = [resultsDir URLByAppendingPathComponent:@"result.txt"]; if ([manager fileExistsAtPath:[resultFile path]]) { // Append to the existing file NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:resultFile error:nil]; [handle seekToEndOfFile]; [handle writeData:[message dataUsingEncoding:NSUTF8StringEncoding]]; [handle closeFile]; } else { // Create and write to the file. [message writeToURL:resultFile atomically:NO encoding:NSUTF8StringEncoding error:nil]; } }