Upgrade to Pro — share decks privately, control downloads, hide ads and more …

『ヘブンバーンズレッド』のリグレッションテストにおけるストーリーの自動プレイ

 『ヘブンバーンズレッド』のリグレッションテストにおけるストーリーの自動プレイ

GREE Tech Conference 2026で発表された資料です。
https://techcon.gree.jp/2026/session/TrackB-1

Avatar for gree_tech

gree_tech PRO

October 16, 2026

More Decks by gree_tech

Other Decks in Technology

Transcript

  1. ヘブンバーンズレッド ジャンル: RPG リリース: 2022/2/10 配信端末: iOS, Android, Steam フィールドを駆け回り、

    かけがえのない日常を体験しながら 命を賭した戦いに挑むドラマチックRPG 4
  2. 環境 • Unity 6.0 (6000.0.65f1) • Lua 5.4.8 • Tencent/xLua

    2.1.16 ◦ Unity で Lua を使用するためのライブラリ 13
  3. xLua の特徴 • Lua → C# の呼び出し ◦ ◦ •

    C# 側の static メソッドを Lua 側に公開可能 公開した static メソッドを文字列で呼び出す C# → Lua の呼び出し ◦ LuaEnv(Lua Virtual Machine) を通して Lua を実行 static メソッドを実行 Lua C# Lua 文字列を実行 Lua ↔ C# 間で任意の処理を実行可能 LuaEnv 15
  4. 自動プレイ単一機能の実装 • ボタンのパスを受け取り、ボタンを押下する関数を C# 側で定義 ◦ static メソッドで定義し、Lua 側に公開 public

    static void ClickButton(string path) { var button = FindObject(path); button.OnClick(); } • Lua 側から関数を呼び出す CS.LuaMethod.ClickButton("UIAdv/SkipButton") C# static メソッドを実行 Lua 17
  5. 自動プレイの仕組み 自動プレイ用の LuaEnv(Lua Virtual Machine) を作成 • 自動プレイ用の Lua のスクリプトを本番から呼び出せない

    ヘブバンアプリ用 LuaEnv 自動プレイ用 LuaEnv ヘブバンアプリ用の関数登録 自動プレイ用の関数登録 ADV の実装 ・ ゲーム全般の実装 1s 毎のループ処理 → ループ内で自動化処理 18
  6. 自動プレイの仕組み • 『1秒ごとのループ処理』 と 『State ドリブンな UI 操作』 ループ if

    title 1秒ごとのループ処理(Coroutine) if story State ごとに処理を分岐 20
  7. 自動プレイの仕組み • 『1秒ごとのループ処理』 と 『State ドリブンな UI 操作』 ループ Story

    Loop if title 汎用処理 Story 中のループ処理 if story if storyId ==“XX” 汎用処理に加え、 ストーリーごとに個別で条件分岐 if storyId ==“YY” 21
  8. 自動プレイの仕組み • 『1秒ごとのループ処理』 と 『State ドリブンな UI 操作』 ループ Story

    Loop if title if story 汎用処理 ダイアログ OKクリック if storyId ==“XX” if storyId ==“YY” 全ストーリーに共通する処理は、 汎用処理として実行 22
  9. 自動プレイの仕組み • 『1秒ごとのループ処理』 と 『State ドリブンな UI 操作』 ループ Story

    Loop if title 汎用処理 Story XX Loop 汎用処理 if story if storyId ==“XX” if day == “01” if storyId ==“YY” if time == “0930” 23
  10. 自動プレイの仕組み • 『1秒ごとのループ処理』 と 『State ドリブンな UI 操作』 ループ Story

    Loop if title 汎用処理 Story XX Loop 汎用処理 if storyId ==“XX” if story ストーリー別のループ処理 if day == “01” ストーリー内の if storyId ==“YY” 日付や時間で個別実装 if time == “0930” 24
  11. 自動プレイの仕組み • 1秒ごとのループ処理 と State ドリブンな UI 操作 ループ Story

    Loop if title 汎用処理 Story XX Loop 汎用処理 if story if storyId ==“XX” 限られた状況下で、 if storyId ==“YY” 自動プレイ単一機能の実行可能 if day == “01” スキップボタン クリック if time == “0930” 25
  12. 自動プレイの仕組み • 1秒ごとのループ処理 と State ドリブンな UI 操作 ループ Story

    Loop if title 汎用処理 Story XX Loop 汎用処理 if story if storyId ==“XX” if day == “01” スキップボタン クリック if storyId ==“YY” if time == “0930” 26
  13. 実装 Loop = function() Auto.Loop 汎用的な処理 – その他Stateの処理 if Auto.state("title")

    then if state == “story” – タイトルので自動プレイ処理 end ストーリーでの汎用処理 if Auto.state("story") then Auto.storyLoop() end Coroutinue で if story == “storyId” 1秒ごとに呼ばれる ストーリーでの汎用処理 自動化プレイ用のループ関数 if story == “storyId” – その他Stateの処理 end 27
  14. 実装 Loop = function() Auto.Loop 汎用的な処理 – その他Stateの処理 if Auto.state("title")

    then if state == “story” – タイトルので自動プレイ処理 end ストーリーでの汎用処理 if Auto.state("story") then Auto.storyLoop() end Coroutinue で if story == “storyId” 1秒ごとに呼ばれる ストーリーでの汎用処理 自動化プレイ用のループ関数 if story == “storyId” – その他Stateの処理 end 28
  15. 実装 Loop Auto.storyLoop = function() – 汎用処理 汎用的な処理 for _,

    pair in ipairs(Auto.StoryDef) do if state == “story” if storyId == pair[1] then ストーリーでの汎用処理 if Auto.Story.isLoadedLua(pair[2]) then Auto[pair[1]].loop() else if story == “storyId” Auto.Story.loadLua(pair[2]) end Story用のループ関数 ストーリーでの汎用処理 StoryId に対応した、 個別ストーリーのループを実行 if story == “storyId” end end – 汎用処理 end 29
  16. 実装 Loop Event01.loop = function() if day == "01" then

    汎用的な処理 if time == "0530" then CS.LuaMethod.ClickButton("UIAdv/SkipButton") if state == “story” end ストーリーでの汎用処理 if time == "0830" then -- 実装 end end if story == “storyId” ストーリーでの汎用処理 if story == “storyId” ©WFS Developed by WRIGHT FLYER STUDIOS ©VISUAL ARTS/Key if day == "02" then -- 実装 end end 1秒に呼ばれる イベントストーリー毎の関数 30
  17. 実装 Loop Event01.loop = function() if day == "01" then

    汎用的な処理 if time == "0530" then CS.LuaMethod.ClickButton("UIAdv/SkipButton") if state == “story” end ストーリーでの汎用処理 if time == "0830" then -- 実装 end end if story == “storyId” ストーリーでの汎用処理 if story == “storyId”©WFS Developed by WRIGHT FLYER STUDIOS ©VISUAL ARTS/Key if day == "02" then -- 実装 end end Day 毎に実装を分岐させる 31
  18. 実装 Loop Event01.loop = function() if day == "01" then

    汎用的な処理 if time == "0530" then CS.LuaMethod.ClickButton("UIAdv/SkipButton") if state == “story” end ストーリーでの汎用処理 if time == "0830" then -- 実装 end end if story == “storyId” ストーリーでの汎用処理 if story == “storyId” ©WFS Developed by WRIGHT FLYER STUDIOS ©VISUAL ARTS/Key if day == "02" then -- 実装 end end 時間帯ごとに処理を分岐 32
  19. 実装 Loop Event01.loop = function() if day == "01" then

    汎用的な処理 if time == "0530" then CS.LuaMethod.ClickButton("UIAdv/SkipButton") if state == “story” end ストーリーでの汎用処理 if time == "0830" then -- 実装 end end if day == "02" then -- 実装 end end if story == “storyId” ストーリーでの汎用処理 if story == “storyId”©WFS Developed by WRIGHT FLYER STUDIOS ©VISUAL ARTS/Key 自動化の実装 スキップボタンの押下処理 33
  20. Lua テストスクリプト開発 • 自動プレイに必要なUI 操作の関数呼び出しはツールで対応 ◦ ◦ 「パスを含む関数呼び出し」をツールに表示しコピー&ペースト出来るツールを作成 パスの取り方はパスの深さ通りだけある。 ▪

    UIのPrefab のルートからのパスに固定 NGな例 CS.LuaMethod.ClickButton("UIRoot/UIAdv/SkipButton") CS.LuaMethod.ClickButton("SkipButton") OKな例(UIAdv prefabからのパスになっている) CS.LuaMethod.ClickButton("UIAdv/SkipButton") 34
  21. Lua テストスクリプト開発 • Lua はスクリプト言語 ◦ ◦ Unity を中断 →

    コンパイル → 再開 の手順を踏まずに、特定の Lua ファイルのみを再読 み込みし、処理を変更可能 エディタから入力した Lua 文字列の実行も可能にし、Lua の実行テスト自体も高速に行 えるようにした。 Lua 実装 Lua 再読込 動作確認 エディタを起動したまま実装が可能 35
  22. Lua テストスクリプト開発 • ストーリー毎のファイル分割 ループ if story Story Loop if

    storyId ==“XX” Story XX Loop if day == “01” if time == “0930” if storyId ==“YY” 36
  23. Lua テストスクリプト開発 • ストーリー毎のファイル分割 autoplay_XX.lua ループ if story Story Loop

    if storyId ==“XX” Story XX Loop if day == “01” if time == “0930” if storyId ==“YY” 37
  24. Lua テストスクリプト開発 autoplay_XX.lua • ストーリー毎のファイル分割 autoplay_YY.lua ループ if story Story

    Loop if storyId ==“XX” Story YY Loop if day == “03” if time == “1230” if storyId ==“YY” 38
  25. Lua テストスクリプト開発 autoplay_XX.lua • ストーリー毎のファイル分割 autoplay_YY.lua ループ if story Story

    Loop if storyId ==“XX” Story YY Loop if day == “03” if time == “1230” if storyId ==“YY” StoryIdとファイル名により、自動でロード 39
  26. Lua テストスクリプト開発 • ストーリー毎の実装を QA が担当 autoplay_XX.lua autoplay_YY.lua autoplay_ZZ.lua Story

    XX Loop Story YY Loop Story ZZ Loop if day == “00” if day == “00” if day == “00” if time == “00” if time == “00” if time == “00” 40
  27. 本番環境への影響考慮 • C# の実装は本番ビルドに含めない ◦ • プリプロセッサディレクティブ(#IF ~ #ENDIF)で囲み、開発用ビルドのみで動作する ように

    Lua は本番ビルドのアセットバンドルビルドからファイルを除外 開発用ビルド 本番ビルド 本番用の実装 自動プレイ用の実装 参照 & 操作 41
  28. QA側のテストスクリプト開発概要 • 大まかな進行は自動プレイシステムが担当 ◦ ◦ ◦ ◦ • ゲーム内Autoボタン押下によるオート移動・オートバトル ゲーム内Skipボタン押下による会話のスキップ

    会話中に現れる単純な選択 リザルト画面のスキップ システムがカバーしていない部分をスクリプト実装により自動化 ◦ ◦ ◦ ◦ シーケンシャルな処理が必要なメニュー 正解を選ぶ必要のある選択 ゲーム内Autoボタンが無効なシーンでの移動 ゲーム内Autoボタンが無効なバトル 自動プレイシステムとスクリプト実装による完全自動化 43
  29. QA側のテストスクリプト開発 • 大きく分けて3つのパートをスクリプト実装により自動化 ◦ ◦ ◦ アドベンチャー ▪ チュートリアル ▪

    正解を選ぶ必要のある選択 フィールド ▪ 他のシーンへの遷移操作 ▪ イベントトリガーへのタッチ(キャラクター、アイテム等) ▪ ゲーム内Auto機能が無効な場合の移動 バトル ▪ とにかくバトルで勝利する 44
  30. 自動プレイシステムを使用した結果 • サードパーティー製ツールと比較して ◦ ◦ • メリット ▪ タイムボックスの概念を利用してホワイトボックス的なテスト実装が可能 •

    テスト条件が事前に絞られているためコードに余計な判断や分岐が不要 • デバッグ機能の時間遡行(任意のシーンへジャンプ)を併用することで高速な実装を実現 ◦ ひと月でほぼ全てのイベントの自動化(45個中44個) デメリット ▪ コンポーネント操作用のAPIが存在しない場合のブロッカー • 代替できる処理がない場合手動での操作が必要 ◦ サードパーティー製ツールと併用により改善可能 自動プレイにより手動での検証工数を約40%削減 ◦ 進行不能な不具合を自動プレイシステムで検知可能にしたことで ▪ ユーザーのみなさまにいつでも安心してプレイできる環境を用意 ▪ ヘブバンの信頼性と品質向上に貢献 50
  31. まとめ • • 自動プレイの実装により、検証工数の40%削減 機能要件も達成した状態での実装 本番への影響をなくす アセバン削除や #IF による対応 実機で動作可能にする

    QA用ビルドには実装を含めた テストコードの 保守を容易にする State ドリブンな設計で達成 エンジニアが基盤実装し、 QAがスクリプトの実装を可能にする QA が Luaスクリプトを実装 51