ゲームのオンデマンド インストール
この技術記事では、Windows インストーラーを使用した、オンデマンド インストールとバックグラウンド インストールの 2 つの手法について説明します。 ゲームでは、インストール時間を短縮し、より快適なゲーム体験をプレーヤーに提供するために、このようなインストール方法を活用できます。
概要
インストールは、長い間、コンピューター ベースのアプリケーションの要素でした。 現在のほとんどのアプリケーションは、使用する前にユーザーのローカル ハード ドライブにインストールする必要があります。 コンピューター ゲームも例外ではありません。コンシューマーが Microsoft Windows ゲームを購入して実行しようとすると、まず、ゲーム ディスクから必要なファイルをハード ドライブにコピーするインストール プロセスを実行する必要があります。 このインストール プロセスは通常は時間がかかり、完了までに 1 時間もかかる場合があります。 コンソール ゲームはゲーム ディスクを挿入したらすぐにプレイできるため、一部のプレーヤーにとっては、インストールの所要時間によってコンピューター ベースのゲームよりもコンソール ゲームの方が好まれる要因となっています。この記事で説明するテクノロジは、インストール時間を大幅に短縮することでこの問題を改善するためのものです。
従来、ゲームを起動する前に、すべてかほとんどのファイルをインストールする必要があります。 オンデマンド インストールを実現ために、ゲーム リソースをモジュール化する必要があります。つまり、開発者はアプリケーションのリソース (グラフィックス、オーディオなど) をコンポーネントに分割する必要があります。 各コンポーネントは、ユニットとしてインストールまたは削除できるリソースのセットです。 これが完了すると、ゲーム開発者は 1 つ以上の機能 (通常はレベルまたはゾーンごとに 1 つ以上) を定義します。 アプリケーションの各機能では、その特定の機能を実行するために必要なコンポーネントのセットが指定されます。 アプリケーションをインストールすると、その機能を "インストール" (インストール時にローカル ハード ドライブにコピーされたコンポーネント) または "アドバタイズ済み" (アプリケーションがその機能を使用する場合の最初のインストール後にローカル ハード ドライブにコピーされたコンポーネント) としてマークできます。 ゲーム開発者は、最小限の機能をインストールしたらゲームを起動して実行できるように設計することで、インストール時間を短縮できます。 残りの機能はアドバタイズ済みとしてマークされ、それらの機能で提供される機能をアプリケーションで実際に使用する必要がある場合にオンデマンドでインストールできます。
ゲームは Windows インストーラーを呼び出して、インストールされていない可能性のある特定の機能をインストールできます。 インストールをバックグラウンドで表示するには、ワーカー スレッドを使用してインストーラーを呼び出し、その間にプライマリ スレッドがゲーム ロジックの処理と画面のレンダリングを続けます。 これにより、インストールによって生じるゲームプレイの中断が最小限に抑えられます。 ゲームはいつでもインストールを開始できます。 ただし、インストールではプロセッサ サイクルが消費されるため、一般的に、ユーザーがアクションの途中である場合など、プライマリ スレッドに処理能力が不可欠であるときにインストールを実行することはお勧めしません。 たとえば、ユーザーがゲーム メニューにいる場合、ゲームが一時停止または最小化されている場合、またはユーザーが紹介ムービーやカットシーンを見ている場合などが、インストールを実行するのに適したタイミングです。
修正プログラムの適用のサポート
現在のゲームの多くは、出荷後もバグの修正や新機能の追加のために更新が必要です。 更新には、修正プログラムの適用が必要な場合が多く、従来はゲームのための簡単な手順です。 必要なすべてのファイルがユーザーのハード ドライブにインストールされるため、ゲームに修正プログラムを適用するには、変更したファイルをハード ドライブにコピーし、既存のファイルを上書きする必要があります。 オンデマンド インストールを採用した場合、修正プログラムの適用時にすべてのファイルがインストールおよびコピーされるわけではありません。 したがって、修正プログラムで更新されたファイルをゲーム フォルダーに書き込むことはできません。
Windows インストーラー には、オンデマンド インストールを使用するアプリケーションに修正プログラムを適用する機能があります。 修正プログラムを適用すると、インストーラーは修正プログラムをシステムにキャッシュします。 この機能は、差分の小さい修正プログラムに適しています。 元々リリースされていたファイルは、修正プログラムの適用時にディスク上に存在する必要がなくなったため、それらのファイルをアドバタイズできます。 後でアプリケーションが実行され、ファイルにアクセスする必要がある場合、インストーラーは、メディア (CD など) で元々リリースされていたバージョンをコピーし、保存されたパッチ データを読み取った後に修正プログラムを適用することで、これらのファイルの最新バージョンをインストールします。
InstallOnDemand SDK のサンプル
ゲームのオンデマンド インストールのサンプルは、この記事で説明するオンデマンド インストール手法を示しています。 他のサンプルとは異なり、ゲームのオンデマンド インストールはサンプル ブラウザーから直接実行することはできません。 このサンプルでは Windows インストーラーを使用してインストールを管理するため、インストーラーのインストール済みアプリケーションのデータベースに含める必要があります。
サンプルを起動するには
- サンプル ブラウザーの [プロジェクトのインストール] リンクを使用して、サンプルのファイルをフォルダーにコピーします。
- InstallOnDemand.msi をダブルクリックしてサンプルをインストールします。
- [標準インストール] を選択します。
- インストールされたフォルダー (通常は [プログラム ファイル\InstallOnDemand]) で InstallOnDemand.exe を起動するか、[スタート メニュー\プログラム] から起動して、サンプルを開始します。
InstallOnDemand.msi は、インストーラーによって認識されるデータベースです。 ここでは、ディレクトリ構造、コピーするものとしないもの、一緒にコピーするリソース、書き込むレジストリ値、作成するショートカットなど、インストール プロセス全体を定義します。
起動すると、サンプルではイントロが再生されます。 プレーヤーは、ESC キーを押してそれを終了し、メイン メニューに移動できます。 イントロに従って、プレイヤーはキャラクター名を入力して統計情報をスクロールすることで、新しいゲームを開始できます。 サンプルがイントロの再生を開始する前に、サンプルはインストーラー関数を呼び出して、レベル 1 の機能がインストールされているかどうかをチェックします。 レベル 1 の機能がインストールされていない場合、サンプルではバックグラウンド スレッドを使用してインストーラーにゲームのインストールを依頼し、プライマリ スレッドは別の操作 (イントロの再生、メニューのレンダリング、キャラクター作成時のプレーヤーとの対話など) を行います。 このエクスペリエンスは、インストールの進行中もユーザーがゲームに没頭している (イントロの視聴や新しいキャラクターの作成) という点で、従来のゲームのインストールとは異なります。 プレーヤーがキャラクターの作成を完了すると、サンプルはレベル 1 のリソースを読み込みます。
サンプル画面の右側には、"レベル 1 の再生"から "レベル 5 の再生" まで、マークされた 5 つのボタンがあります。これらのボタンで、プレーヤーが現在のレベルを完了し、次のレベルに進むことをシミュレートします。 これらのボタンのいずれかをクリックすると、完了したレベルに関する情報を示す統計画面が表示されます。 このサンプルでは、次のレベルがまだインストールされていない場合、インストーラーに確認とインストールを依頼するために、この時間がかかります。 プレーヤーが統計情報画面を読んでいる間にインストールが行われるため、ユーザーが [OK] をクリックして次のレベルに入ると、レベルのリソースがすべてインストールされ、読み込みの準備が整います。
サンプルの機能とコンポーネント
従来、ゲームを起動する前に、すべてかほとんどのファイルをインストールする必要があります。 オンデマンド インストールを実現ために、ゲーム リソースをモジュール化する必要があります。つまり、開発者はアプリケーションのリソース (グラフィックス、オーディオなど) をコンポーネントに分割する必要があります。 各コンポーネントは、ユニットとしてインストールまたは削除できるリソースのセットです。 これが完了すると、ゲーム開発者は 1 つ以上の機能 (通常はレベルまたはゾーンごとに 1 つ以上) を定義します。 アプリケーションの各機能では、その特定の機能を実行するために必要なコンポーネントのセットが指定されます。 アプリケーションをインストールすると、その機能を "インストール" (インストール時にローカル ハード ドライブにコピーされたコンポーネント) または "アドバタイズ済み" (後でアプリケーションがその機能を使用する際にローカル ハード ドライブにコピーされたコンポーネント) としてマークできます。 ゲーム開発者は、最小限の機能をインストールしたらゲームを起動して実行を開始できるように設計することで、インストール時間を短縮できます。 残りの機能はアドバタイズ済みとしてマークされ、それらの機能で提供される機能をアプリケーションで実際に使用する必要がある場合にオンデマンドでインストールできます。
次の表に、サンプルで定義されている 6 つの最上位の機能を示します。
機能名 | 機能 | コンポーネント | ファイル |
---|---|---|---|
コア | レベルに関係なく、常に必要なリソースが含まれます。 これらのリソースは、サンプル実行可能ファイル、イントロと読み込み画面で必要なメディア、サンプル内のすべてのレンダリングを処理する .fx ファイルです。 | コア | InstallOnDemand.exe, InstallOnDemand.fx, Loading.bmp, Level.x |
コア | (同上) | CoreUI | Media\UI\dxutcontrols.dds, Media\UI\DXUTShared.fx, Media\UI\arrow.x |
コア | (同上) | CoreMisc | Media\Misc\seafloor.x, Media\Misc\seafloor.bmp |
コア | (同上) | CoreSpeeder | Media\PRT Demo\LandShark.x, Media\PRT Demo\speeder_diff.jpg |
コア | (同上) | CoreReg | N/A (レジストリ値) |
Level1 | レベル 1 で使用されるリソースを指定します。 | Level1 | Level1.jpg |
Level1 | (同上) | L1Skybox | Media\Light Probes\galileo_cross.dds |
Level2 | レベル 2 で使用されるリソースを指定します。 | Level2 | Level2.jpg |
Level2 | (同上) | L2Skybox | Media\Light Probes\grace_cross.dds |
Level3 | レベル 3 で使用されるリソースを指定します。 | Level3 | Level3.jpg |
Level3 | (同上) | L3Skybox | Media\Light Probes\rnl_cross.dds |
Level4 | レベル 4 で使用されるリソースを指定します。 | Level4 | Level4.jpg |
Level4 | (同上) | L4Skybox | Media\Light Probes\stpeters_cross.dds |
Level5 | レベル 5 で使用されるリソースを指定します。 | Level5 | Level5.jpg |
Level5 | (同上) | L5Skybox | Media\Light Probes\uffizi_cross.dds |
レベル 1 からレベル 5 の機能には、サンプルで直接使用されないファイルを含む追加のサブ機能があります。 これらのサブ機能ファイルは、インストールにかかる時間を長くするために追加されました。 これは、サンプルの実行中にバックグラウンドで実行されている継続的なインストール操作を説明するために行われます。
次の表は、サブ機能の一覧です。
機能 | サブ機能 | ファイル |
---|---|---|
Level1 | L1PH1、L1PH2、L1PH3、L1PH4、L1PH5 | Level1 Placeholder Data\L1PH1.dat Level1 Placeholder Data\L1PH2.dat Level1 Placeholder Data\L1PH3.dat Level1 Placeholder Data\L1PH4.dat Level1 Placeholder Data\L1PH5.dat |
Level2 | L2PH1、L2PH2、L2PH3、L2PH4、L2PH5 | Level2 Placeholder Data\L2PH1.dat Level2 Placeholder Data\L2PH2.dat Level2 Placeholder Data\L2PH3.dat Level2 Placeholder Data\L2PH4.dat Level2 Placeholder Data\L2PH5.dat |
Level3 | L3PH1、L3PH2、L3PH3、L3PH4、L3PH5 | Level3 Placeholder Data\L3PH1.dat Level3 Placeholder Data\L3PH2.dat Level3 Placeholder Data\L3PH3.dat Level3 Placeholder Data\L3PH4.dat Level3 Placeholder Data\L3PH5.dat |
Level4 | L4PH1、L4PH2、L4PH3、L4PH4、L4PH5 | Level4 Placeholder Data\L4PH1.dat Level4 Placeholder Data\L4PH2.dat Level4 Placeholder Data\L4PH3.dat Level4 Placeholder Data\L4PH4.dat Level4 Placeholder Data\L4PH5.dat |
Level5 | L5PH1、L5PH2、L5PH3、L5PH4、L5PH5 | Level5 Placeholder Data\L5PH1.dat Level5 Placeholder Data\L5PH2.dat Level5 Placeholder Data\L5PH3.dat Level5 Placeholder Data\L5PH4.dat Level5 Placeholder Data\L5PH5.dat |
インストール中、コア機能は [インストール] とマークされ、その他の機能は [アドバタイズ済み] とマークされる必要があります。6 つの機能をインストールする代わりに 1 つだけインストールすることで、ゲーム起動までの待ち時間が大幅に短縮されます。
インストール
Windows インストーラーは、アプリケーションがアドバタイズされた機能のインストールを要求するためのメカニズムを提供します。 ただし、このメカニズムは同期アプリケーション プログラミング インターフェイス (API) 呼び出しであるため、アプリケーションは、インストールが完了するまで呼び出し内で待機する必要があります。 バックグラウンド インストールを実現するには、プレーヤーに視覚的なフィードバックを与え続けるための画面上のレンダリングなど、メイン アプリケーション スレッドが他の重要なタスクを自由に実行できるようにするワーカー スレッドが必要です。
このサンプルでは、サンプルの実行中に発生するインストールの状態には、アクティブ インストール、パッシブ インストール、およびインストールなしという 3 つの状態があります。
- アクティブ インストールは、1 つ以上の機能によって提供されるリソースにアクセスまたは読み込む必要がある場合に、サンプルによって開始される要求です。 このサンプルは、リソースがインストールされるまで続行できない場合に実行されます。
- パッシブ インストールは、プレイヤーがメニューにいる場合やカット シーンを見ている場合など、サンプルが重要なタスクを実行していない場合に開始されます。 この場合、ワーカー スレッドはサンプルの機能がまだアドバタイズされているかどうかをチェックします。 検出されると、インストーラーを呼び出してその機能をインストールします。 このプロセスは、サンプルのすべての機能がインストールされるまで繰り返されます。 基本的に、パッシブ インストールでは、メイン サンプルに最も邪魔にならない場合に、余分なプロセッサ サイクルを使用してバックグラウンドでインストールを実行します。
- プレーヤーが積極的にゲームに参加している場合、インストールは行われません。これにより、ユーザー エクスペリエンスを中断するフレームレートの低下を防ぐことができます。
このサンプルでは、すべてのインストール関連タスクを処理するために CMsiUtil クラスが定義されています。 基本的に、CMsiUtil は、インストーラーを呼び出すワーカー スレッドを使用して、サンプルの機能をループでインストールします。 このクラスには、インストール要求を格納する 2 つのキューがあります。1 つはアクティブ インストール用の優先度の高いキュー、もう 1 つはパッシブ インストール用の優先度の低いキューです。 初期化中、クラスは製品のすべての機能を列挙し、パッシブ インストール キューに追加します。 この方法で製品全体がキューに登録されるため、サンプルに十分な空きプロセッサ サイクルがある場合は、最終的に製品全体がインストールされます。
サンプルでアクティブ インストールを要求する必要がある場合、サンプルは CMsiUtil::UseFeatureSet() を呼び出し、最上位の機能の名前を渡すことができます。 UseFeatureSet() は、要求された機能とそのすべてのサブ機能をワーカー スレッドがインストールできるように、アクティブ インストール キューに登録します。
インストール要求の実行中、ワーカー スレッドはアクティブ インストール キューとパッシブ インストール キューをチェックして、いずれかのキューに追加の要求があるかどうかを確認します。 スレッドが要求を検出するたびに、インストーラー API を呼び出して実際のインストールを実行します。 両方のキューが空になると、ワーカー スレッドは WaitForSingleObjectを呼び出してスリープ状態にします。 初期化中に製品全体がパッシブ インストール キューに配置されるため、キューが空の場合は製品全体がインストールされたことを意味します。
このサンプルでは、CMsiUtil::EnablePassiveInstall() を呼び出して、パッシブ インストールを有効または無効にします。 EnablePassiveInstall(true) はパッシブ インストールの有効カウントを増やし、EnablePassiveInstall(false) によって減らします。 有効カウントが 0 より大きい場合、クラスはパッシブ インストール キューを処理します。 このサンプルでは、次のいずれかに該当する場合にパッシブ インストールが許可されます。
- ユーザーが最初のイントロを表示している。
- ユーザーがサンプル メニューに移動している。
- ユーザーがレベルの最後に統計情報を表示している。
- サンプル アプリケーションがフォーカスを失い、バックグラウンド状態になる。
CMsiUtil のメソッドを次に示します。
メソッド | 説明 |
---|---|
AbortAllRequests | 現在のインストールを中止し、アクティブ インストール要求キューを空にします。 |
AbortCurrentRequest | 進行中のインストールを中止します。 その後、ワーカー スレッドは、キューに次の要求が存在する場合に処理します。 |
EnablePassiveInstall | パッシブ インストールの有効カウントを増減します。 このサンプルでは、パッシブ インストールを実行できるタイミングと実行できないタイミングを制御するためにこの呼び出しを使用します。 |
GetCurrentFeatureName | アクティブにインストールされている機能の名前を返します。 |
GetFeatureProgress | インストールされている機能の現在の目盛り位置を返します。 |
GetFeatureProgressMax | インストールされている機能の進行状況ティックの最大数を返します。 |
最新エラーの取得 | このメソッドを使用して、前回のインストール要求からリターン コードを取得します。 |
GetPassiveProgress | パッシブ インストールの進行状況バーの目盛り位置を返します。 |
GetPassiveProgressMax | 現在の目盛り位置とパッシブ インストールの目盛りの最大数を返します。 このサンプルでは、これらを組み合わせて使用して、パッシブ インストールの全体的な進行状況を示すことができます。 |
GetProgress | アクティブな機能セットのインストールの進行状況バーの目盛り位置を返します。 これは、サンプルがインストールの進行状況バーをレンダリングする場合に使用されます。 Windows インストーラーでは、インストールされている 1 つの機能の進行状況情報のみ提供されるため、このメソッドは、ユーザーがインストール全体を 1 つのタスクとして確認できるように、要求された機能の間で進行状況バーを分割します。 |
GetProgressMax | アクティブな機能セットのインストールの進行状況バーの目盛り位置を返します。 これは、サンプルがインストールの進行状況バーをレンダリングする場合に使用されます。 |
Initialize | 製品グローバル一意識別子 (GUID) を使用してクラスを初期化します。 また、このメソッドは、アドバタイズされたもののまだインストールされていないアプリケーションのすべての機能を列挙し、パッシブ インストールを設定するためにパッシブ インストール キューに配置します。 |
IsInstallInProgress | アクティブ インストールが処理中であるかどうかを調べるには、この方法を使用します。 |
UseFeature | UseFeatureSet によって呼び出されるプライベート メソッド。 機能がインストールされているかどうかを確認します。 要求された機能がインストールされている場合、メソッドは返します。 この機能がまだインストール (アドバタイズ) されていない場合、メソッドはワーカー スレッドの新しいアクティブ インストール要求をキューに登録し、その後返します。 要求されたインストールの完了時に通知されるオプションのイベント ハンドル。 |
UseFeatureSet | 特定の機能またはそのサブ機能で提供される機能にアクセスする必要がある場合に、サンプルによって呼び出されます。 このメソッドは、サンプルのすべての機能を列挙し、指定されたルート機能のサブ機能に対して UseFeature() を呼び出します。 このサンプルは、機能セット全体がインストールされた場合に通知されるイベント ハンドルを渡すことができます。 機能はすべてセットとしてインストールされるため、ハンドルは、すべての機能ではなく UseFeature() によってキューに登録された最後の機能に対して指定されるため、要求されたすべての機能がインストールされた後にサンプルに 1 回通知されます。 |
UseProduct | ワーカー スレッドがインストーラーを呼び出して製品の完全インストールを実行するためのアクティブ インストール要求をキューに登録します。 要求されたインストールの完了時に通知されるオプションのイベント ハンドル。 |
制限事項
インストーラーの現在のバージョンは、複数のスレッドによる同時アクセス用には設計されていません。 したがって、ワーカー スレッドがインストーラーを呼び出している場合、メイン スレッドでインストーラーを呼び出さないようにしてください。 この制限の例は、メイン スレッドが機能を要求し、ワーカー スレッドがインストールを完了する前に同じ機能をもう一度要求する場合に、サンプルで発生します。 2 番目の要求は MsiQueryFeatureState() を呼び出して、要求された機能が既にインストールされているかどうかを調べます。これは、ワーカー スレッドがまだファイルをコピーしているときに、インストーラーが機能が完全にインストールされていることを示す場合があるためです。
幸いなことに、これには簡単な回避策があります。 CMsiUtil は、MsiQueryFeatureState() や MsiUseFeature() などの関数を呼び出して問題の機能のインストール状態を依頼する前に、機能がワーカー スレッドによってインストールされているかどうかをチェックします。 この制限は、他の場所でも問題になる可能性があることに注意してください。
修正プログラムの適用は、エンド ユーザーのコンピューターでのオンデマンド インストールの動作に影響を与える可能性があります。 以前のバージョンから変更されたデータのみを含む修正プログラムを適用するには、差分を適用するために、更新されたファイルの以前のバージョンをインストールする必要がある場合があります。 この状況では、修正プログラムをゲームに適用する前に、影響を受けるアドバタイズされた機能をインストーラーがインストールするように要求する必要があります。
このサンプルは、Windows インストーラーがコンピューターに存在することを前提としているため、InstallOnDemand.msi を起動してインストールされます。 インストーラーが存在しない場合、.msi ファイルは認識されず、起動しても機能しません。 この問題を解決するには、アプリケーションでインストール プログラムを使用してタスクを実行する必要があります。 このプログラムは、インストーラーが存在するかどうかを最初にチェックし、存在する場合は、そのバージョンをチェックする必要があります。 バージョンがアプリケーションの要件を満たしていない場合は、インストール プログラムで Windows インストーラーをインストールし、.msi ファイルを起動する必要があります。 このプロセスはブートストラップと呼ばれます。 アプリケーションは通常、ブートストラップ インストール プログラムに Setup.exe という名前を付けます。 サンプルはブートストラップを処理しません。 ただし、ブートストラップの完全な詳細は、Windows インストーラーで見つけることができます。
開発者は、ゲーム内の各機能のサイズにも注意を払う必要があります。 進行中のインストールを取り消すと、インストーラーはコンピューターをインストール前の状態に復元します。 これは、機能のインストールが完全に元に戻され、機能の部分的なインストールは行われないことを意味します。 大規模な機能はインストールに時間がかかるため、インストールが中断されて取り消されるか、インストールがメイン アプリケーションに干渉する可能性が高くなります。 たとえば、ユーザーがゲーム プレイの途中でゲーム メニューを起動したときにパッシブ インストールが有効になります。 機能をインストールして、ユーザーがプレイに戻った場合、ゲームはパッシブ インストールを完了させるか、パッシブ インストールを取り消すかの 2 つの動作のいずれかを実行できます。 大規模な機能は、どちらのモデルにも合わないでしょう。 ゲームが大規模なインストールを完了させると、インストールによってゲームのレンダリング パフォーマンスが長時間妨げられる可能性があります。 逆に、ゲームがインストールを取り消した場合、ユーザーはゲームに戻る前にメニューに長時間留まる必要があります。 開発者は、個々のゲームに最適なバランスの取れた機能サイズを見つける必要があります。