[Blog翻訳] C++ で Animation Manager を使って投射物の動作をシミュレーションする

みなさん、こんにちは。Windows 開発統括部の古内です。
アニメーション関連のブログ翻訳第二弾をお届けします。今回のは、2010 年 11 月 26 日に Developing for Windows Blog に投稿された 「Simulating projectile motion with the Animation Manager using C++」 の翻訳です。


C++ で Animation Manager を使って投射物の動作をシミュレーションする

前回の記事では、Windows アプリケーションにアニメーションを組み込むメリットと、その基盤となるサポートの概要について説明しました。今回の記事では、ネイティブ Windows アプリケーションと Animation Manager を使って簡単なアニメーション効果を実現する方法をさらに詳しく説明していきます。

まず、適当なサンプルが必要です。面白そうで、アニメーションの諸概念や実装方法を説明するためにある程度複雑なものが理想的です。私が思い当たったのは、Atari 2600 や私のお気に入りゲームの 1 つだった Human Cannonball です。これらは本格的なゲームには程遠いものですが、投射物の動きをシミュレートするために必要な要素はカバーしています。

ではここから、Animation Manager インターフェイスを使用したアニメーション効果の実現方法の概要を説明していきましょう。

  • アニメーション変数を作成する。 アニメーション変数は IUIAnimationVariable のインスタンスで、放物線に沿った投射物の座標を用意するために使用します。サンプルでは、水平位置と垂直位置の変数が 1 つずつあります。
  • 遷移オブジェクトを作成する。 遷移があらかじめいくつか定義されています。遷移は IUIAnimationTransitionLibrary を使用して作成できます。サンプルでは、水平座標にこのライブラリの線形遷移を使用します。垂直座標には、初速度に減速を適用するカスタム インターポレーターに基づいて遷移を作成します。これらの遷移は、前のステップのアニメーション変数に関連付けられています。
  • ストーリーボードを作成する。 ストーリーボードは、前のステップで作成した遷移で構成されます。これらの遷移は、単に Storyboard オブジェクトに追加されます。このオブジェクトは、Animation Manager によって生成されます。
  • ストーリーボードをスケジューリングする。 時系列上でアニメーション変数の更新プロセスを開始します。ストーリーボードが実行中になると、アニメーション変数を使用して、投射物を描画すべき x 座標と y 座標を取得します。

サンプル設計の概要

サンプル (英語) は こちら からダウンロードできます。

このサンプルには、2 つの主なクラスが実装されています。CMainWindow と CProjectileAnimation です。これらが処理の大部分を実行します。

この 2 つのオブジェクトの詳細を説明する前に、サンプル設計の概要について説明します。私は通常、ファクトリ生成パターンを使用しています。これは、私の定義するオブジェクトがファクトリ インスタンスへの静的メソッドを持つことを意味します。さらに、明確に定義されたコントラクト内に、クラスのパブリック メソッドをカプセル化します。そのために IUnknown 派生インターフェイスを使用しています。この方法の長所は、コンポーネント間にどのようなコントラクト要件があるかを事前に考慮することが必要な点です。また、この方法では参照カウント セマンティクスを使用するため、オブジェクトの寿命管理が容易になるというメリットもあります (パターンをメモリにコミットした場合)。短所としては、コードがやや必要以上に複雑に見えることがあります。これは特にパターンになじみがないときに問題になります。また、QI/AddRef/Release の定型的なスタイルをいくらか実装する必要もあります。

CMainWindow

CMainWindow は、Win32 HWND 上のシンプルなラッパーです。CMainWindow は、デバイス非依存とデバイス依存のアニメーション オブジェクトの作成を担当します。デバイス非依存オブジェクトは、初期化中に作成され、アプリケーションの寿命の間は維持されます。次のようなオブジェクトが含まれます。

  • Animation Manager: ストーリーボードやアニメーション変数のようなアニメーション オブジェクトの多くを作成します。
  • Animation Timer: ストーリーボードのスケジューリングと Animation Manager の更新に使用します。
  • Transition Factory: カスタムの遷移を作成します。
  • Transition Library: 標準的な遷移を作成します。
  • D2D Factory: さまざまな D2D オブジェクト (今回の場合は D2D サーフェイス) を作成します。

デバイス非依存オブジェクトに加えて、CMainWindow によって管理されるデバイス依存オブジェクトがいくつかあります。これらは、レンダリング パス (WM_PAINT) で要求に応じて作成され、Animation Manager に要求された際に破棄されます。次のようなオブジェクトが含まれます。

  • HWND レンダリング ターゲット (投射物が描画される D2D サーフェイス)
  • 背景ブラシ (クライアント領域を塗りつぶすために使用するシンプルなグラデーション ブラシ)
  • 投射物のビットマップ (放物線状の経路に沿ってアニメーション化したビットマップ)

投射物の発射は、Space キーを押して実行します。キーを押すたびに、CMainWindow が投射物を一定の曲線に沿って発射します。CMainWindow には一連のアクティブな投射物が保持され、それらはすべてレンダリング パス中に更新されます。1 つのレンダリング パスが完了すると、CMainWindow は Animation Manager に基づいてさらにレンダリング パスが必要であるかどうかを判断し、Animation Manager によって残りのすべての投射物に対するストーリーボードの処理が完了するまで無効にします。

CMainWindow は、IUIAnimationManagerEventHandler としても登録されます。これにより、Animation Manager の状態変化を待ち受け、無効化/レンダリング サイクルを起動することができます。

CProjectileAnimation

CProjectileAnimation は、投射物の効果の実現に必要なストーリーボードを作成するためのロジックをカプセル化した、とてもシンプルなクラスです。CProjectileAnimation の初期化には、ストーリーボードの作成に必要なさまざまなアニメーション サービスを検査する IServiceProvider (英語) が必要です。このサービスは、CMainWindow (デバイス非依存オブジェクトとして上記で説明) によってキャッシュされます。CMainWindow は、それらの要求を処理する IServiceProvider を実装しています。

CProjectileAnimation は、CGravityInterpolator によって提供される IUIAnimationInterpolator の実装に基づいてストーリーボードの水平コンポーネントを作成します。このようなシンプルな実装では、一定の初速度と加速度を使用します。インターポレーターは、アニメーション システムによって使用され、ストーリーボードの始点からの時間オフセットに基づいて速度と位置の増分値を供給します。

まとめ

とてもシンプルなアニメーションを作るにもさまざまな作業が必要なように見えますが、ぜひこのサンプルをダウンロードして試してみてください。私の作成方法をご覧いただくとわかるとおり、実はそれほど複雑な作業は必要なく、Animation Manager に備わっている多くの機能と柔軟性を活用すればよいのです。