Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)

Akatsuki Hackers Labは株式会社アカツキゲームスが運営しています。

Unity Build Layout Viewer — Addressablesのビルド結果を可視化するセルフホスト型Webツール

1. ツールの概要

Addressables を本格運用していると、こういった問題が日常的に出てきます

  • 「このビルド、なんかバンドルが大きくなってるけど原因は?」
  • 「本番に混入してはいけないアセットが入ってないか確認して」
  • 「前回と今回で何が変わったか教えて」

Addressables ではアセットをバンドルという単位にまとめて配布します。バンドル内のアセットが変更・追加・削除されたり、依存先の構成が変わったりするとバンドルのハッシュが変わり、ユーザーへの再配布(=ダウンロードサイズの増加)につながります

この「どのアセットが変わってバンドルに影響したのか」を追うのが、BuildLayout.json だけでは大変で…

これらの問題を解決するためにブラウザから手軽にアセットの検索や複数ビルド・バンドル間の差分比較、アセットの依存関係の調査ができるツールを作りました

Unity Build Layout Viewer は、Addressables がビルド時に出力する BuildLayout.json を取り込んで

  • アセット一覧の検索・フィルタ
  • スナップショット間の差分比較(Diff)
  • アセットの上流依存関係グラフの可視化

をブラウザから行える、セルフホスト型の Web アプリです

クラス名・拡張子・名前・GUID でアセットを絞り込める

2. 主な機能

2-1. アップロードとスナップショット管理

  • ブラウザ、または REST API 経由で BuildLayout.json をアップロードが可能
  • スナップショットにタグ・コメントを付けて管理

2-2. アセット検索

  • スナップショットを選択してアセット一覧を表示
  • クラス名・ファイル拡張子・名前・GUID でフィルタ
  • 右クリック → Dependency Viewer で依存グラフへ遷移

2-3. スナップショット Diff

2 つのスナップショットを選んで差分を表示します

A vs B ビュー : アセット単位で Added / Missing / Hash or Size Changed を一覧

hash / missing / add / size のタグで変化の種類ごとにフィルタリングできる



Bundle ビュー : バンドル単位で内包アセットの変化を詳細表示(Moved / Added RefTo / Added RefBy なども分類)

バンドルごとにアセットの追加・削除・移動・サイズ変化を確認できる

2-4. 依存関係ビューア(Dependency Viewer)

  • 選択したアセットから「上流(誰がこのアセットを参照しているか)」をグラフ表示
  • 探索深さ・ノード上限をインタラクティブに変更可能
  • 逆隣接リスト(revAdj)をメモリキャッシュして高速化

誰がこのアセットを参照しているかが追える

3. 仕組みの解説

3-1. BuildLayout.json のパース

BuildLayout.json は大きなファイルになりがちなので、全体をメモリに展開せずストリーミングパースを採用しています

stream-json の chain / pick / streamArray を組み合わせて
references.RefIds 配列だけを逐次処理

各エントリは rid(参照 ID)を持ち、依存関係は from_id → to_id のリンクとして別テーブルに正規化して保存しています

3-2. 依存グラフの構築

DB から全リンクを取得 → 逆隣接リストを構築 → DFS で上流チェーンを計算しています
逆隣接リストはスナップショット ID をキーにインメモリキャッシュしており、同一スナップショットへの 2 回目以降のアクセスを高速化しています

3-3. Diff アルゴリズム

  1. 両スナップショットのアセットを name をキーに Map 化
  2. A にあって B にない → isMissing、B にあって A にない → isAdd、両方あってハッシュ違い → hash_diff
  3. バンドル単位では DFS でバンドル配下のアセットを収集し、同名アセットの移動・追加・削除を分類

4. セットアップ方法

ローカル起動もできますが、Dockerも用意してあります

Docker

cp .example.env .env
docker compose -f compose.prod.yml build
docker compose -f compose.prod.yml run --rm app npm run prisma:deploy
docker compose -f compose.prod.yml up -d
# → http://localhost:3435

ローカル

npm install
cp .example.env .env   # DATABASE_URL を設定
npm run prisma:deploy
npm run prisma:generate
npm run dev

5. 使い方のフロー

  1. Unity でビルドを実行して BuildLayout.json を出力
  2. ブラウザで http://localhost:3435 へアクセス
  3. Upload タブから BuildLayout.json をアップロード
    1. タグ・コメントを付けると管理しやすくなります
  4. Search タブでアセット一覧を確認・検索
    1. 右クリック → Dependency Viewer でアセットの依存関係を調査
  5. Diff タブで 2 つのスナップショットを選択して差分を確認

6. 実際に役立ったシーン

混入してはいけないアセットの調査

本来含まれるはずのないアセットがビルドに混入していないか確認する作業が格段に楽になりました

  • Search タブでアセット名を直接検索して、含まれているかどうかをすぐに確認できます
  • Diff タブで「前回のビルドには存在しなかったのに今回から追加された」アセットを一目で発見できます

以前はビルドログや生の JSON を目視確認していましたが、今は数秒で判断できるようになりました

バンドルサイズの増加原因の特定

「なぜかバンドルが大きくなった」という状況に対して、従来は原因の特定に時間がかかっていました

このツールを使うと:

  • バンドル単位の Diff でどのアセットが追加・削除・移動したのかをすぐに確認できます
  • アセットが別のバンドルに移動したことでサイズ増減が発生したケースも検出できます
  • 新規追加・削除・ハッシュ変化・サイズ変化でそれぞれ分類されているので、原因を絞り込みやすくなっています

意図しない参照連鎖の発見

Dependency Viewer でアセットを選択すると、「誰がこのアセットを参照しているか」を上流方向にグラフで追えます

これにより、意図していない参照チェーン(例:共通アセットが思わぬ場所から引っ張られている)を視覚的に発見できるようになりました

チーム内の意思決定がしやすくなった

ゲームアプリにおいてダウンロードサイズが大きくなると、ユーザーがインストールや更新を敬遠して継続プレイから離れてしまうリスクがあります

  • 「次回は小規模アップデートなのでダウンロードサイズの増加は許容できない、修正が必要」
  • 「大型イベントを控えた大規模アップデートなので、一定のサイズ増加は問題ない」

数値の根拠があることで、感覚ではなくデータで話し合えるようになりました

まとめ

Addressables のビルド管理で同じような課題を抱えている方にぜひ使ってみていただきたいです
もし使ってみて良さそうと思っていただけたら、GitHub のスターを押してもらえると励みになります!

github.com

YJIT・pitchforkのreforkingを導入し、パフォーマンスが3割向上しました

はじめに

こんにちは。アカツキゲームスの松下です。

弊社ではRailsを用いた大規模なゲームサーバを運用しており、お客様に良い体験を届けるべく、パフォーマンス改善が常に重要事項であり続けています。そのため、RubyにおけるJITコンパイラ・高速化技術であるYJITについても、登場時から注目していました。弊社でもこれまでに何度か導入を試みてきたものの、YJITを有効化すると負荷試験中に segmentation fault(SEGV)がごく稀かつランダムな箇所で発生するという現象に悩まされており、原因の特定すら難航する、歯痒い状態が続いている状況でした。

そのため、長らく本番導入を見送らざるを得ない状況が続いていましたが、一念発起し試行錯誤を重ねた結果、YJITとpitchforkのreforkingを本番環境に導入することに成功し、大きなパフォーマンス改善を得ることができました。 本記事では、無事に導入に至るまでの経緯と、その過程で得られた知見について紹介します。

前提

今回YJITを導入したサービスはゲームサーバであり、マスタデータ*1をプロセス上に保持する構成をとっています。このような特性から、メモリ効率の最適化が重要です。

将来的なYJIT導入を見据えて、昨年夏時点にUnicornからpitchforkへ移行していました。後述するSimpleMasterなど、reforkingによるCoW(Copy-on-Write)の恩恵を十分に得ることが狙いです。これはYJITに使うメモリ使用量削減にも寄与します。*2

マスタデータのロードと管理には、弊社の趙が開発したSimpleMasterを利用しています。SimpleMasterは、マスタデータをプロセス起動時に読み込み、不変データとして扱うことでCoWを活用し、ワーカプロセス間でメモリを効率よく共有できる点が特徴です。ゲームサーバのように大規模なマスタデータを扱う環境において、メモリ使用量の削減に大きく寄与しています。

また、JSONレンダリングには、同じく趙が開発したSimpleJson を利用しています。SimpleJsonは、Lambda記法により直感的にviewを記述可能でありながら、軽量かつ高速に動作する点が特徴です。

なお、負荷試験環境および本番環境はAWS上でGraviton 4のインスタンスを利用しています。

導入のきっかけ

これまでRubyの新しいバージョンがリリースされるたびにYJITの有効化の検討・負荷試験などを行ってきましたが、負荷試験中に高い確率でSEGVが発生してしまい、導入を断念せざるを得ませんでした。原因の特定も難しく、調査が進まない状況が続いていました。なお、このSEGVはUnicorn・pitchforkいずれの構成においても発生していました。

昨年12月頃、Ruby v3.4.8 へのアップグレードを検討するにあたりリリースノートを確認したところ、YJITに関する気になるチケット Bug #21266: YJIT GC safety crash with proc objects as block argument がありました。内容的に、これまで遭遇していたSEGVと関係がありそうだったため、再度本格的にYJIT導入に挑戦しました。ただし、後にRuby v3.4.8にアップグレードするだけではSEGVは解消しないことがわかります。

導入に向けた流れ

負荷試験環境でRuby v3.4.8, Rails v8.0を使って負荷試験を進めていました。yjit_exec_mem_size: 128MiB(default)ではSEGVは起きず、yjit_exec_mem_size: 256MiB(本番想定サイズ)ではSEGVが発生しました。このため、Ruby v3.4.8の導入のみだと、SEGVになる原因は取り除けていません。

SEGVのスタックトレースを見ていると、SimpleJson Gemが怪しいことに気がつきました。SimpleJsonはテンプレートファイルを読み込んでLambdaとして評価し、instance_exec(&lambda)で実行します。SimpleJsonには起動時にテンプレートキャッシュを保持する仕組みがあります。しかしながら、改めて設定を見返すと、正しくテンプレートキャッシュが機能していないことがわかりました。そのため、大量にLambdaを生成・実行しており、これがYJITと相性が良くない可能性があると推測しました。

そこで、SimpleJsonに変更を加えることにしました。PRはこちらです Replace lambda with method to avoid YJIT crash by AZQ1994 · Pull Request #12 · aktsk/simple_json。既存のテンプレートファイルの置き換えをしないために、テンプレートのLambdaを正規表現でメソッド定義文字列に変換し、class_evalでクラスに直接メソッドとして定義するようにしました。あわせて設定を見直しSimpleJsonのテンプレートキャッシュを有効にしました。

これらのSimpleJsonの変更を加え、Ruby v3.4.8 / yjit_exec_mem_size: 256MiBの構成で負荷試験を進めましたが、SimpleJsonのLambda実行とは関係ない箇所でSEGVが発生しました。該当コードを確認しても不自然に思える点はなく、対応に行き詰まっていました。 ちょうどこの検証を進めていた時期がクリスマス頃でRuby v4.0がリリースされており、リリースノートを確認するとGC周りの改善やYJITのバグ修正が含まれていることがわかりました。これらの変更が影響する可能性も考え、Ruby v4.0.0でも負荷試験を進めることにしました。

Ruby v4.0.0 / yjit_exec_mem_size: 256MiBで負荷試験をすると、これまで遭遇していたタイミングでSEGVが起きなくなりました。Ruby v3.4.8とv4.0.0の差分を調査しましたが、どの修正が直接効いたのかまでは特定できませんでした(推測ではありますが、GCやメモリ周りの改善によるものかと考えています)。念のため、十分な時間をかけて繰り返し負荷試験を回しましたが、SEGVは起きませんでした。負荷試験を十分な時間回してSEGVが起きないことを確認できたため、本番環境へ導入を進めました。一方で、明確な原因が不明だったこともあり、本番環境で、SEGVが起きないという保証・確証があるわけではありませんでした。そこで本番環境へ導入するにあたり、SEGVを検知できるようにアラートを仕込み、すぐに対応ができるようにしておきました。

本番導入による効果

導入前後での構成の違い

  • Ruby v3.4.7 → v4.0.0
  • YJIT無効 → 有効(yjit_exec_mem_size: 256MiB)
  • reforking無効 → 有効
  • SimpleJsonのテンプレートキャッシュ無効 → 有効

YJITの有効化およびreforkingの導入が大きく寄与していると考えていますが、個別の寄与割合については計測できていません。効果は以下になり、大きなパフォーマンス改善が確認できました。

  • レスポンスタイム
    • 平均: 約28%改善
    • P90: 約32%改善
  • メモリ使用量
    • 約30%削減

導入前後でのレスポンスタイム(水色が平均、緑色がP90)

SEGV再び

本番環境導入後、特定のサーバ環境のみにおいて稀ですが何度かSEGVが発生しました。スタックトレースを見ると、SimpleJsonとは関係ない箇所のLambdaの実行でSEGVが発生していました。SEGVが起きるエラー箇所はランダムで異なり、再現性が低く、再現手順の確立が難しい状況です。

SEGVが起きるのが稀であることから、SEGVが発生したときにpitchforkのhookを利用して自動的に復旧できる仕組みを実装しました。具体的には、SEGV発生を after_worker_exit で検知し、 after_mold_fork においてRubyVM::YJIT.code_gc()(YJITのコードを捨てる)とreforkingを行って、SEGVを引き起こす原因を取り除くというものです。コンテナの再起動などでの対応も可能ですが、再起動時に初期化処理でマスタデータのロードなどで幾分か時間がかかるワークロードであることと、CoWによって他のpitchforkのワーカでもSEGVが発生しやすい状況になっており、ダウンタイムを小さくしたいためこの手段を選択しています。これにより、ごく稀に発生したとしても、即座に自動復旧・正常化することが確認できています。

現状、Ruby v4.0.2へアップグレードしていますが、依然として稀にSEGVが発生しています。関連しそうな修正として YJIT: Fix remaining version_map aliasing UB from get_iseq_payload by rwstauner · Pull Request #16510 · ruby/ruby があり、これによって改善される可能性も考えていますが、現時点では影響は不明です。

まとめ

本記事では、長らく見送っていたYJITの導入に向けた取り組みと、最終的に得られたパフォーマンス改善、SEGVの対処の一例について紹介しました。

現時点でも稀にSEGVが発生する課題は残っているものの、運用でカバーしつつ大きな性能向上を享受できており、実用的な形でYJITを活用できる状態になっています。SEGVの再現手順については調査を引き続き進めていきます。

同様にYJITの導入で課題に直面している方の参考になれば幸いです。

また、私を含め弊社メンバーも数名RubyKaigi 2026に参加予定です。現地でお会いできるのを楽しみにしております。

*1:ゲームの設定や数値などの全ユーザー共通のデータ

*2:https://byroot.github.io/ruby/performance/2025/03/04/the-pitchfork-story.html

動画を集めて、描いて、話す。チーム向け動画レビューツール VideoReview

この記事は、Akatsuki Games Advent Calendar 2025 25日目の記事です

初めまして、アカツキゲームスの有田です
アドベントカレンダーの最終日を枠を頂けましたので、
社内で開発してきた動画レビューツール「VideoReview」をOSSとして公開したので紹介をしたいと思います

動画レビューを行うとき、次のような困りごとを感じたことはないでしょうか?

  • 大量の動画がクラウドに置かれているだけで整理できない 
  • コメントと動画(時間・フレーム)が紐付かない
  • 修正のやり取りや履歴が追えなくなる

VideoReviewは、動画を集約しコメントやお絵描きでやり取りできる、チーム向けの動画レビューツールですSNS のような感覚で、「このカットどう?」「ここ直したい」といったレビューをテンポよく回せる環境を目指して作りました

「探す・把握する・確認する」をひとつの流れに

探したい動画に、すぐ辿り着ける高速検索

レビュー対象の動画が増えても、キーワード検索で瞬時に絞り込みができます
ボス名・チャプター名・用途など、あいまいな記憶でも目的の動画にすぐアクセスでき、「探す時間」をほぼゼロにできます

レビュー対象をツリー構造で一元管理

動画はチャプターや用途ごとにツリー構造で整理され、全体の構成を俯瞰しながらレビュー対象を選べます
どの動画がどの文脈に属しているのかが直感的に分かり、レビューの抜け漏れを防ぎます

新着コメントが一目で分かる、迷わないレビュー導線

新しいコメントが付いた動画には「New」アイコンが表示され、次に確認すべき動画がひと目で分かります
コメント → 動画確認 → 次の動画、という流れが自然につながり、レビュー作業を止めずに進められます

 

レビューの流れを止めない、外部ツールとのシームレスな連携

コメントをSlackへ同時共有し、レビューの拾い忘れ防止

動画へのコメントを、そのままSlackへ同時に共有できます
コメント内容に加えて、該当時刻・スクリーンショット・動画への共有URLを送信することで、Slack上でもレビューを始められます

  

コメントからタスクチケットを作成し、確実にトラッキング

コメントを起点にAtlassian社のJiraチケットを作成でき、指摘事項や修正タスクをそのままワークフローに乗せられます
チケットには動画へのリンクが含まれるため、「どのシーンの話か」を迷わず確認でき、レビューとタスク管理が自然につながります

動画に直接描き込み、伝えたいポイントを明確に

動画のフレーム上に直接描き込むことで、言葉だけでは伝えにくい位置・形・動きのニュアンスを、そのまま共有できます
コメントに視覚的な情報を加えることで、レビューの意図がより正確に、素早く相手に伝わります

自動化フローに組み込みやすい動画アップロードAPI

VideoReviewでは、動画アップロードをREST APIとして公開しています
CI・バッチ処理・ツール連携などから直接動画を登録でき、レビュー工程を既存の制作フローに容易に組み込むことが可能になっています

オンプレを前提に、必要に応じてクラウドを選べる構成

VideoReviewはオンプレミス環境での運用を前提に設計しています
社内ネットワーク内で動画を完結させることで、機密性の高い映像素材を外部に出さずにレビューを行えます

一方で、運用やチーム構成に応じて、AWS S3 をストレージとして選択することも可能です
オンプレ・クラウドを用途に応じて使い分けることで、セキュリティ・導入コスト・運用負荷のバランスを柔軟に取れます

試して見たい!という方

ローカルで簡単に起動できるDocker環境も用意しています!
手順:

  1. リポジトリをクローン:git clone https://github.com/KirisameMarisa/video-review.git
  2. ディレクトリへ移動:cd video-review
  3. 依存パッケージのインストール:npm install
  4. Dockerコンテナの起動:docker compose -d --build
  5. ブラウザでアクセス:http://localhost:3438

今後の展望

描画機能の強化:

現状の手書き注釈に加え、矩形・矢印・文字追加などの図形ツールを実装して、指示を分かりやすくします

動画検索強化:

コメント投稿の範囲から、投稿者から、タグからなど様々な角度から動画の検索性をあげ更新情報を見逃さないように改善します

最後に

VideoReviewは、 実際の制作チーム内で日常的に使われながら改善を続けているツールです

たとえば、
自動テストで撮影したスキットシーンの動画をアップロードし、レビューや過去動画のバックアップ用途として利用されていますまた、多言語チェックにおける動画の集約先としても使われています

現場で使う中で出てきた課題や要望を取り込みながら、
レビュー作業を少しずつ楽にすることを目指して育てています

まだ生まれたばかりではあるものの、沢山のフィードバックをいただいて成長させていきたいと思っているので、Issue や PR などのフィードバックはいつでも歓迎です

強化学習ライブラリGymnasiumを使ってみた

こんにちは! アカツキゲームス クライアントエンジニアのSuです。
この記事は Akatsuki Advent Calendar 2025 24日目の記事です。メリークリスマス!

はじめに

学生時代に強化学習の研究を少したので、久しぶりに強化学習をやりたいな〜の気持ちで本記事を書きました。今回はGymnasiumというPythonライブラリを使用した経験を紹介したいと思います!

強化学習とは?

強化学習(Reinforcement Learning)は簡単にいうと、エージェントが環境の中でアクションと実行し、その結果から学習し、エージェントがよりいい結果を出力できるようにアクションを選択する方法を最適化することです。

エージェントがアクションを行い、環境の変化を見て学習する
(Gymnasium-Basic Usageより)

例えば、レーシングゲームで自動運転のエージェントを作るとしたら:

  • Action: 加速、減速、方向を変わる
  • Observation: ゴールまでの距離、壁の方向と距離、経過時間...
  • Reward: 走行した距離、壁にぶつかる回数...

エージェントがアクションを決めるアルゴリズムは強化学習分野の重要トピックです。Q-Learning、PPO、SAC、最近でたDAPOなど、アルゴリズムの進化速度はとても早いです。この記事は深掘りしませんが、興味がありましたらぜひ調べてみてください!

Gymnasiumとは?

2021年にOpenAI Gym library の開発チームが Gymnasium に移転しました。
強化学習環境開発・訓練に特化した Python ライブラリです。

宇宙船を着陸させるタスクを学習させる環境は簡単に作れる
(https://gymnasium.farama.org/ より)

今回は公式ドキュメントにある例のマップを改造して、そのマップ上にプレイヤー、ゴール、トラップがあります。プレイヤーを動かして、トラップをできるだけ踏まないように、終点まで移動するタスクをAIに学習させ、その学習結果を評価したい思います。

環境の作成

まず、Gymnasium環境クラス gymnasium.Env 3パートで構成されます:初期化、更新、描画です。それと下記の情報はコンストラクタで定義します:

初期化

処理は reset() に入れます。環境を初期状態にする処理です。最初の Observation を返します。処理は 1 episode(タスク開始〜終了)ごとに実行されます。今回はプレイヤー、ゴール、トラップの位置をランダムに生成する処理を書きました。Observation はプレイヤー、ゴール、トラップの位置にします。

更新

実行するActionを step() 関数に渡して、環境はどう変化するかとこの行動はいいかどうかを返す。今回の Action は移動方向で、関数内にはプレイヤーの位置を更新、ゴールについたら点数を与える、トラップを踏んだら減点にしました。

描画

render() 関数に定義されます。今回は青い丸がプレイヤー、赤い丸がゴール、黒い丸がトラップ、それとマスを PyGame ライブラリで描画しました。

パッケージ

環境できたら、利用しやすくためパッケージ化します。パッケージ化すると、便利な Wrapper が使えます。Observation を他の形式に変更する(例えば、プレイヤーとゴールの位置ではなく、その相対位置に変更)にはよく使いします。

エージェント

Observation を見て、どういう Action を取るかを決めるルールです。Q-Learning、PPO、SAC というアルゴリズムの部分です。自分で実装するのもいいし、公式おすすめのライブラリ Stable-Baselines3 を使うと便利です。パッケージされた Gym 環境があれば下記のように簡単にモデル作れます。複数環境で並行学習もできます。

学習

環境から返した点数で今回選んだActionを評価して、次同じObservationが来た時に同じActionを選ぶかどうか、アルゴリズムを修正します。Stable-Baselines3のドキュメントを参考すれば各パラメターの意味が書かれていますので、ここは割愛します。

ログ

Stable-Baselines3 は TensorBoard 出力できます。TensorBoard を使用してログを出力できます。学習の過程を図で表示できます。学習足りているかどうかの確認やパラメター調整する際にかなり参考になりました。

tensorBoard

訓練結果を評価

Stable-Baselines3で学習したモデルを保存できます。その保存したモデルをロードすれば、与えた Observation に対して Action を出力します。

<gist>

よし!最強のAIを作ったので、早速モデルを試すぞ!
あれ、なんか動かないだけど......

移動しない

最初の問題は、壁にぶつけたら移動しなくなった問題です。これを解決するために、「前回と同じ位置なら減点」するRewardを追加してみましたが...

ずっと隣のマスに行ったりきったり

「同じ場所じゃなければいい」とエージェントもその抜け道を見つけて、ずっと同じマスに行ったりきったりしていました...

AIちゃんをちゃんと動かせるため、「すでに経過した場所に移動すると減点する!」ように Reward 修正しました。

色々調整した結果、それっぽい動きになった!
時々トラップに踏むですが、避けるように頑張ってるを感じてます。

より広いマップ

15x15のマップで改めて学習させました!10x10より学習時間2倍かかったが、成果は悪くないと思います。

Github Repo

今回使用したコードをGitHubにアップロードしたので、興味がある方はぜひ触ってみてください!

github.com

最後に

調整してAIをどんどん成長させるのも楽しいですが、適切なRewardを設定するのが難しいと実感しました。それと、個人的な感想ですが、Actionが連続スペースなタスクの方が得意なイメージがあります。となるとActionの設計も重要になってきますね。もっと複雑なタスクをやらせて欲しくなった!

AIだけではなく、強化学習の概念とアルゴリズムはゲーム開発中にも活用できると思います。勉強になりました!

明日は25日!クリスマス当日に軍曹が素敵な記事を公開する予定です。みなさんぜひ読んでみてください〜!

「一番メモリを消費するキャラクターは誰?」に即答したい。Unity アセットの静的解析と自動通知で実現したアセット肥大化を未然に防ぐ仕組み

この記事は Akatsuki Games Advent Calendar 2025 22日目の記事です。

はじめに

クライアントエンジニアの渡邊です。ゲームの新規機能開発やプロジェクト内部向けツールの製作を行なっています。

概要

運用型ゲームではキャラクターが増え続けますが、キャラクターのアセットが端末のメモリをどれくらい使用するかは常に気になる問題です。

「今、最もメモリを消費しているキャラクターは誰なのか?」

「複数キャラクターを同時に読み込んだ時、ゲームアプリがクラッシュしてしまうことはないだろうか?」

こうした問いに答えるために、私は、キャラクターのモデル、アイコンやサムネイルといった画像、VFX、サウンドデータなど複数の要素から構成されるキャラクターのアセットのメモリ上のサイズを推定して集計するツールと、メモリサイズ推定値が大きいキャラクターをランキング化して Slack に通知する仕組みを開発しました。

完成したツールの Editor Window
Slack へのランキング通知

本記事では、Unity エディタ上での集計ロジックの簡単な解説から、自動通知の仕組みまでを解説します。このツールは単なる計測に留まらず、アセット肥大化を早期に検知することができたり、アーティストさんへアセット最適化を促進したりと、当初の単なる「キャラクターのメモリ使用量測定」の枠を超えた変化を生み出すことができました。

続きを読む