WWDC Quick Look 💓 By SwiftGGTeam
StoreKit と Background Assets でゲーム内コンテンツを解放する

StoreKit と Background Assets でゲーム内コンテンツを解放する

元の動画を見る

ハイライト

Apple は Unity 向けの StoreKit と Background Assets プラグインを導入しました。ローカライズされたアセットパックの自動配信に対応し、新しい Steam アセット変換ツールでゲーム開発者が既存ビルドを移行しやすくなります。

主要内容

モバイルゲーム開発で大きな悩みの1つは、パッケージサイズです。プレイヤーはゲームを始めるだけで長く待ち、数百 MB のアセットをダウンロードします。その多くは実際には使われない場合があります。たとえば英語プレイヤーが、日本語、中国語、韓国語、フランス語、ドイツ語、スペイン語、ロシア語など12言語分の音声パックをダウンロードしても、使うのは英語だけです。

Apple は iOS 27 で Managed Background Assets に重要な機能を追加しました。ローカライズされたアセットパックです。システムはプレイヤーが設定で選んだ優先言語を認識し、対応する言語のアセットだけをダウンロードできます。対応する言語パックがない場合は、最も近い言語バリアントへ自動でフォールバックします。

この機能は、多言語ゲームのパッケージ肥大化を直接解決します。10言語対応のゲームでも、各プレイヤーが必要とする言語アセットはそのうち1言語分だけで済む可能性があります。

もう1つの現実的な問題は、多くのゲームがすでに Steam で配信され、アセットを depot 形式で管理していることです。Apple プラットフォームへ移行するためにアセットを整理し直すのは、大きな作業になります。Apple は、Steam depot を Asset Pack Manifest に直接変換できるコマンドラインツールを提供しました。

Unity 開発者向けには、StoreKit と Background Assets の C# プラグインも正式に公開されました。以前は Unity から Apple のネイティブフレームワークを使うには、自前のブリッジコードが必要でした。公式プラグインにより、C# から直接呼び出せます。

詳細

ローカライズされたアセットパック

(01:35)

アセットパック manifest に language フィールドを追加すると、そのアセットパックの言語版を示せます。

// アセットパック manifest
{
   "assetPackID": "voice-english",
   "downloadPolicy": { /* ... */ },
   "language": "en-US",
   "sourceRoot": ".",
   "fileSelectors": [ /* ... */ ],
   "platforms": [ /* ... */ ]
}

重要な点:

  • language フィールドは en-USzh-Hans などの BCP 47 言語タグ形式を使います。
  • システムは「設定 > 一般 > 言語と地域」でのユーザー選択に基づいてアセットパックを照合します。
  • 完全一致がない場合、en-GB から en-US へ戻るように、基礎言語へフォールバックします。

Steam アセット変換ツール

(03:13)

Steam depot を Apple のアセットパックへ変換するには、1つのコマンドで済みます。

# Steam depot をアセットパック manifest に変換する
xcrun ba-package convert --asset-pack-id voice-english -l en-US --on-demand voice-english.vdf -o voice-english.json

重要な点:

  • --asset-pack-id はアセットパックの一意識別子を指定します。
  • -l は言語タグを指定し、省略可能です。
  • --on-demand はダウンロードポリシーを設定します。prefetch などの選択肢もあります。
  • 入力は Steam の .vdf manifest ファイルです。
  • 出力は Apple の JSON manifest です。

manifest を生成したら、次にアセットパックアーカイブへパッケージします。

# アセットパック manifest をアセットパックアーカイブに変換する
xcrun ba-package voice-english.json -o voice-english.aar

このツールは macOS ではすでに利用可能です(Xcode 27 が必要)。Linux 版と Windows 版も近日公開予定です。

Unity StoreKit プラグイン

(05:41)

商品を取得して購入します。

using UnityEngine;
using Apple.StoreKit;

async void Start() {
    var products = await Product.FetchProducts(new[] {
        "com.thecoast.capecod"
    });
}

async void Purchase(Product product) {
    var result = await product.Purchase();
    if (result.Result == PurchaseResult.ResultEnum.Success
        && result.TransactionVerification.IsVerified)
    {
        // 購入済みコンテンツへのアクセスを解放する
        result.TransactionVerification.SafePayload.Finish();
    }
}

重要な点:

  • Product.FetchProducts は商品情報を非同期に取得します。
  • product.Purchase() はシステムの購入画面を表示します。
  • IsVerified は取引が改ざんされていないことを確認します。
  • Finish() は取引を完了するために必ず呼び出す必要があります。呼ばないと取引が繰り返し処理される可能性があります。

取引更新を監視します。

using UnityEngine;
using Apple.StoreKit;

public static class TransactionListener {
    public static void Initialize() => Transaction.Updates += OnUpdate;

    async void OnUpdate(VerificationResult<Transaction> result) {
        if (!result.IsVerified) return;
        var verifiedTransaction = result.SafePayload;

        // 消耗型商品は CurrentEntitlements に含まれないため、その場で処理する
        if (verifiedTransaction.ProductType == ProductType.ProductTypeEnum.Consumable) {
            if (verifiedTransaction.RevocationDate != null) {
                // verifiedTransaction.ProductId で識別される消耗型商品を取り消す
            } else {
                // 消耗型商品へのアクセスを付与する
            }
        } else {
            // 非消耗型商品とサブスクリプションでは、CurrentEntitlements を信頼できる唯一の情報源として読み直す
            await foreach (var verificationResult in Transaction.GetCurrentEntitlements()) {
                if (!verificationResult.IsVerified) continue;
                // 商品へのアクセスを付与する
            }
        }
        verifiedTransaction.Finish();
    }
}

重要な点:

  • Transaction.Updates は取引の変化があるたびに発火するシーケンスです。
  • 消耗型商品(Consumable)は個別に処理する必要があります。
  • 非消耗型商品とサブスクリプションでは、CurrentEntitlements を唯一の信頼できる情報源として使います。
  • システムは返金済み、取り消し済み、期限切れの権利を自動的に除外します。

Unity Background Assets プラグイン

(07:13)

アセットパックをダウンロードし、進捗を監視します。

using Apple.BackgroundAssets;
using UnityEngine;

async void LoadTutorial(string language) {
    try {
        string assetPackId = $"tutorial-{language}";
        AssetPackManifest manifest = await AssetPackManager.GetManifestAsync();
        AssetPack assetPack = manifest.GetAssetPack(assetPackId);
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        _ = Task.Run(async () => {
            await foreach (AssetPackManager.DownloadStatusUpdate statusUpdate in AssetPackManager.DownloadStatusUpdatesAsync(assetPackId)) {
                // UI のダウンロード進捗を更新する
            }
        }, tokenSource.Token);
        await AssetPackManager.EnsureLocalAvailabilityOfAssetPackAsync(assetPack);
        tokenSource.Cancel();
        // ローカルで利用可能になったアセットでチュートリアルを開始する
    } catch (Exception exception) {
        // 例外を処理する
    }
}

重要な点:

  • GetManifestAsync() はアセットパックのマニフェストを取得します。
  • DownloadStatusUpdatesAsync は UI の進捗更新に使える非同期シーケンスを返します。
  • EnsureLocalAvailabilityOfAssetPackAsync はアセットがローカルで利用可能であることを保証し、必要なら自動的にダウンロードします。

テストツール

(08:37)

Xcode 27 でこれらの機能をテストします。

  1. StoreKit Configuration ファイルを作成し、テスト商品を追加します。
  2. Scheme の Run オプションでその構成ファイルを選択します。
  3. 同じ画面で、Mock Server が使うアセットパックフォルダのパスを設定します。
  4. プロジェクトを実行します。Background Assets Mock Server が自動的に起動し、デバッグセッションに接続されます。

重要な示唆

  1. 多言語ゲームを軽量化する:ゲームが多言語の音声や動画をサポートしているなら、ローカライズされたコンテンツを独立した Asset Pack にすぐ分割しましょう。500 MB のゲームでも、分割後は各プレイヤーが 150 MB だけダウンロードすればよくなるかもしれません。

  2. Steam ゲーム移行の近道:Steam 版を iOS に移植したい場合、新しい変換ツールを使えば既存の depot 構造を直接再利用でき、アセットを整理し直す手間を省けます。

  3. Unity アプリ内課金のハードルを下げる:Unity で Apple のアプリ内課金を扱うのが面倒だと感じていたなら、公式プラグインにより Fetch、Purchase、Finish の3手順で済みます。Objective-C や Swift のブリッジを自作する必要はありません。

  4. ダウンロード進捗を可視化する:Background Assets プラグインの DownloadStatusUpdatesAsync を使って、洗練された進捗バーを作ります。プレイヤーは黒い画面を見つめるのではなく、コンテンツが読み込まれていることを理解できます。

  5. 横画面の購入体験:iOS 27 のシステム購入画面は横画面モードをサポートします。ゲーム内購入フローを設計するとき、プレイヤーが横画面のまま支払いを完了できるようにすると、体験がより一貫します。

関連セッション

  • 210 In-App Purchase — App Store のアプリ内課金に関するベストプラクティスと最新機能
  • 223 Live Activities — Live Activities でゲームのダウンロードやコンテンツ更新の進捗を表示する
  • 258 Xcode 27 — StoreKit Testing の強化を含む Xcode の新機能
  • 277 WidgetKit — Widget でゲームコンテンツや更新情報を表示する
  • 319 PCC Foundation Model — Private Cloud Compute に関連するセキュリティ実践

コメント

GitHub Issues · utterances