26
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

パフォーマンス検証の自動化で得た知見の共有

26
Last updated at Posted at 2025-12-06

◆この記事を読むのにかかる時間:約5分程度
◆この記事の主な対象者:iPhone、Androidのパフォーマンス検証を行っている方

はじめに

はじめまして!ゲームのQAをしています。
今回でアドベントカレンダー3回目の参加になります。
日々テスト自動化に邁進しています。

背景

昨今、バトル中のスキル演出等がどんどんリッチになっていくため、端末にかかる負荷も増加してきています。
その中でもユーザーが快適にプレイできることを担保するためにパフォーマンス検証を行っています。
長時間プレイする必要があるため工数が嵩んでしまいますし、検証外の作業にも時間がかかっていました。
今回は、特に【検証外の作業】の自動化にフォーカスし、得た知見やアイディアを共有します。

パフォーマンス検証について

目的

3DのRPGタイトルのビルド更新によるパフォーマンスの変化を検知する

対象端末

  • iPhone
  • Android

検証対象

  • 主にストーリーで大事なボス戦
  • プレイ頻度の高いバトル 等

検証内容

アプリリリース前の検証時に

  • パフォーマンスが重くなりがちなキャラメインの編成、最新キャラメインの編成を組む
  • 検証対象のコンテンツをプレイする
  • カクつき具合を記録するために録画する
  • 編成内容をスプレッドシートに記録する

使用した言語

  • GAS
  • Bash

自動化した内容

  • 演出がリッチなキャラメインの編成、最新キャラメインの編成を組む
  • 検証対象のコンテンツをプレイする 
  • カクつき具合を記録するために録画する
  • 編成内容をスプレッドシートに記録する

具体的な自動化方法と得た知見

今回は【検証外の作業】にあたる
1️⃣ 演出がリッチなキャラメインの編成、最新キャラメインの編成を組む
2️⃣ カクつき具合を記録するために録画する
3️⃣ 編成内容をスプレッドシートに記録する
について説明します。

"検証対象のコンテンツをプレイする"では「airtest」を使用しているため今回は割愛します。
こちらの記事がおすすめです。

1️⃣ 演出がリッチなキャラメインの編成、最新キャラメインの編成を組む

GASという言語を使用しました。

GASとは
Google Apps Script(GAS)は、Googleが提供するクラウドベースのスクリプト言語です。
言語: JavaScript をベースにしています。
実行環境: Googleのサーバー上で実行されます。
主な用途: Google Workspaceのアプリケーション群(Gmail, Google Sheets, Google Docs, Google Calendar など)を連携・自動化・機能拡張するために使われます。

以下のような編成作業用のシートを用意しました。
スクリーンショット 2025-12-03 10.59.58.png

「キャラデータベースを最新に更新するボタン」で最新化ボタンを押下することでリリースキャラの最新情報を取得します(①)
スクリーンショット 2025-12-03 12.34.34.png

サンプルコード
// 事前シートリセット処理
function clearRowsFromSecondRow() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName('キャラデータシート'); // シート名を指定

  var lastRow = sheet.getLastRow();                 //最後の行を取得
  var lastCol = sheet.getLastColumn();              //最後の列を取得

  //setValuesが二次元配列じゃないといけないので入れ物の事前準備
  var blankBox = [];
  blankBox = sheet.getRange(2,1,lastRow,lastCol).getValues();

  Logger.log(lastRow);
  Logger.log(lastCol);

  //ブランクBOXの作成
  for(var i = 0; i < lastRow; i++){
    for(var y = 0;y < lastCol; y++){
      blankBox[i][y]="";
    }
  }

  Logger.log(blankBox);
  //ブランクで埋める
  sheet.getRange(2,1,lastRow,lastCol).setValues(blankBox);

}


// Cardスプシから情報をコピーする処理
function copyCardSheet() {

  //コピー先のスプレッドシート取得
  var targetSpreadsheetId = "XXXXXXXXXX";               //コピー先スプシIDセット
  var targetSheetName = "XXXXXXXXXX";                                                //コピー先シート名セット
  var ss = SpreadsheetApp.openById(targetSpreadsheetId).getSheetByName(targetSheetName);

  //コピー元のシートデータを取得
  var sauceSpreadsheetId = "XXXXXXXXXX";               //コピー元スプシIDセット
  var sauceSheetName = "XXXXXXXXXX";                                                        //コピー元シート名セット
  var ss_sauce = SpreadsheetApp.openById(sauceSpreadsheetId).getSheetByName(sauceSheetName);
  var lastRow = ss_sauce.getLastRow();                        //最後の行を取得
  var lastCol = ss_sauce.getLastColumn();                     //最後の列を取得
  var ss_sauceRange = ss_sauce.getRange(1,1,lastRow,lastCol); //コピー範囲を設定(開始行,開始列,最終行,最終列)
  var ss_sauceValue = ss_sauceRange.getValues();              //コピー元データを取得
  //コピー
  ss.getRange(2,1,lastRow,lastCol).setValues(ss_sauceValue);  //コピー先に書き込み

}

// 更新ボタン押したときの処理
function KoshinButton(){

clearRowsFromSecondRow(); // 事前シートリセット処理
// clearSheetContents(); // 指定シートの値をすべてクリア


copyCardSheet();          // Cardスプシから情報をコピーする処理
Logger.log("コピー済み");
const prompt = "実行完了しました。";
Browser.msgBox(prompt);  //プロンプト(メッセージ)とOKボタンを表示
return;

}

次に、リリースバージョン毎に編成するキャラをプルダウンから選択します(②)
スキルはキャラとセットなので自動で表示されるようになっています(④)
「上記編成でデータを追加ボタン」を押すと入力ダイアログが表示されリリースバージョン毎に編成を一覧で記録できるようになります(③)

サンプルコード

// Ver情報を追加
function AddParty(rev_inputVer) {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName('Ver毎の編成データ置き場シート'); // データ置き場のシート名を指定

  //シートの最後の行を取得
  var maxRow = sheet.getMaxRows();

  //Ver毎の編成データ置き場シートのC列で文字が入っている一番最後の行を取得
  var cntrow = sheet.getRange("C"+ maxRow).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
  //文字が入っている行の次の行から入力なので、値を補正
  cntrow++;
  
  Logger.log("rev_inputVer:" + rev_inputVer);
  Logger.log("cntrow:" + cntrow);

  // B4の上から空白の行を検索(一番下の行以外に空いている行があればそこを使う)
  for (let row=4; row<=cntrow; row++){
    var Blankcell = sheet.getRange("B"+row);
    if(Blankcell.isBlank()){
      Logger.log(row);
      var defaultRow = row;   // 空白行がある数値を代入し、ブレイク
      break;
    }
  }

  var ver_array = new Array();

  //verを6セル分埋める準備
  for(var i = 0; i < 6; i++){
    ver_array[i] = [rev_inputVer];
  }

  Logger.log(ver_array);

  //ver情報を6セル分埋める
  sheet.getRange("B"+ defaultRow + ":B" + (defaultRow+5)).setValues(ver_array); //Ver情報

  //各編成データ設定
  var AddPT1 = sheet.getRange('AG4:AI9').getValues();
  sheet.getRange("C"+defaultRow + ":E"+ (defaultRow + 5)).setValues(AddPT1);    //編成1
  var AddPT2 = sheet.getRange('AG11:AI16').getValues();
  sheet.getRange("I"+defaultRow + ":K"+ (defaultRow + 5)).setValues(AddPT2);    //編成2
  var AddPT3 = sheet.getRange('AG18:AI23').getValues();
  sheet.getRange("O"+defaultRow + ":Q"+ (defaultRow + 5)).setValues(AddPT3);    //編成3

  //選択してた編成をリセット
  Paty_reset();
  
  //追加完了メッセージ作成&表示
  var ui = SpreadsheetApp.getUi();
  var messageUI = rev_inputVer + "のVerを追加が完了しました";
  var response = ui.alert(messageUI, ui.ButtonSet.OK);

}

得た知見

GASは便利ということ
1度実装してしまえば半永久的に使用できるため、パフォーマンス検証をすればするほど時間を削減できます。

2️⃣ カクつき具合を記録するために録画する

Bashという言語を使用し、録画開始→自動テスト→動画保存を自動化しました

Bathとは
「Bourne Again SHell」の略で、オリジナルのUnixシェルである**sh(Bourne Shell)**の拡張版として開発されたもの
Pythonと違うところ
外部ライブラリのインポートが不要
外部コマンド呼び出し不要

具体的なコードは下記です
Geminiに10秒間録画するコードを生成してもらい、10秒間の部分を自動テストに置き換えました

サンプルコード(Android)
#!/bin/bash

# --- 設定 ---
OUTPUT_FILE="screencast_$(date +%Y%m%d_%H%M%S).mp4"
DEVICE_PATH="/sdcard/${OUTPUT_FILE}"
LOCAL_PATH="${HOME}/Desktop/${OUTPUT_FILE}"

# 1. 録画を開始し、バックグラウンドで実行
echo "録画を開始します"
# バックグラウンド実行 (&)
adb shell screenrecord ${DEVICE_PATH} &

# 2. ADBプロセスのPIDを取得
# $! は直前にバックグラウンドで実行されたプロセスのPIDを格納する特別な変数
RECORD_PID=$!
echo "   -> ADBプロセスID: ${RECORD_PID}"

# 3. パフォーマンステストを実施
echo "自動テストを開始します"
/Applications/AirtestIDE.app/Contents/MacOS/AirtestIDE runner "XXX.air" --device Android://127.0.0.1:5037/$device

# 4. PIDに終了シグナル (SIGINT/Ctrl+Cと同じ効果) を送って録画を停止
echo "録画を停止します..."
# -2 は SIGINT (Ctrl+Cと同じシグナル) を送る
kill -2 ${RECORD_PID}

# 録画終了を待つ(念のため)
wait ${RECORD_PID} 2>/dev/null

# 5. ファイルの転送
echo "ファイルをPCに転送中..."
adb pull ${DEVICE_PATH} ${LOCAL_PATH}

if [ $? -eq 0 ]; then
    echo "転送完了: ${LOCAL_PATH}"
    # デバイス内のファイルを削除
    adb shell rm ${DEVICE_PATH}
    echo "デバイス内のファイルを削除しました。"
else
    echo "ファイル転送に失敗しました。デバイス内のファイルを確認してください: ${DEVICE_PATH}"
fi

echo "完了!!"

得た知見

Bashは便利ということ
ADBツールを使ったり、airtestスクリプトを実行したりしたかったのでBashを使用してみたのですが、Pythonよりも簡単に実装できました

使用するPCが変わってもシェルスクリプトを実行するだけで実行できるためとても便利です。
Bashやadb等に詳しくない人でもPCとスマホを接続してシェルスクリプトを実行すればいいだけな点も大きなメリットだと思います。

3️⃣ 編成内容をスプレッドシートに記録する

if関数を使用し、以下のようにリリースバージョン毎に記録する用のシートを作成しました。
記録シートでリリースバージョン(Ver)とコンテンツ(測定シーン)を選択したら編成が自動で表示されるようになっています(⑤)
スクリーンショット 2025-12-03 12.11.56.png
コンテンツによって1編成で戦う場合や3部隊で戦う場合があるのでコピペ作業が大変だったのが解消されました。

まとめ

今回はパフォーマンス検証の”検証外”の作業を自動化する上で得た知見を共有しました。
今や「やりたいこと」が具体的になっていればAIに頼めばコーディングをしてくれるので、アイディア次第で普段の作業の無駄を減らすことが容易になったと思います。

26
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?