アニメーションは、ロジックとレンダリングの間で完全に分割できます。アニメーションの抽象的なデータ状態は、グラフィックAPIがアニメーションをレンダリングするために必要な情報です。
たとえば2Dゲームでは、描画する必要があるスプライトシートの現在の部分を表示する領域を示す四角形の領域にすることができます(たとえば、キャラクターのさまざまなステップを含む30 x 80x80の図面で構成されるシートがある場合)ジャンプ、座って、移動など)。これは、レンダリングに必要のないあらゆる種類のデータにすることもできますが、現在のアニメーションステップが期限切れになるまでの残り時間やアニメーションの名前( "walking"、 "standing"など)など、アニメーションの状態自体を管理するために使用できます。等)それはすべてあなたが望むように表現することができます。それがロジックの部分です。
レンダリング部分では、通常どおりにそれを行い、モデルからその長方形を取得し、レンダラーを使用して実際にグラフィックスAPIの呼び出しを行います。
コード内(ここではC ++構文を使用):
class Sprite //Model
{
private:
Rectangle subrect;
Vector2f position;
//etc.
public:
Rectangle GetSubrect()
{
return subrect;
}
//etc.
};
class AnimatedSprite : public Sprite, public Updatable //arbitrary interface for classes that need to change their state on a regular basis
{
AnimationController animation_controller;
//etc.
public:
void Update()
{
animation_controller.Update(); //Good OOP design ;) It will take control of changing animations in time etc. for you
this.SetSubrect(animation_controller.GetCurrentAnimation().GetRect());
}
//etc.
};
それがデータです。レンダラーはそのデータを取得して描画します。通常のスプライトとアニメーションのスプライトは同じ方法で描画されるため、ここで多態性を使用できます!
class Renderer
{
//etc.
public:
void Draw(const Sprite &spr)
{
graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
}
};
TMV:
別の例を考えました。あなたがRPGを持っているとしましょう。たとえば、世界地図を表すモデルでは、世界でのキャラクターの位置をマップ上のタイル座標として保存する必要があります。ただし、キャラクターを移動すると、一度に数ピクセルずつ歩いて次の正方形に移動します。この「タイル間の」位置をアニメーションオブジェクトに保存しますか?キャラクターがマップ上の次のタイル座標に最終的に「到着」したときに、モデルをどのように更新しますか?
世界地図は、プレーヤーの位置を直接認識していません(Vector2fなど、プレーヤーの位置を直接格納するようなものはありません。代わりに、AnimatedSpriteから派生するプレーヤーオブジェクト自体への直接参照があります)そのため、簡単にレンダラーに渡して、必要なデータをすべて取得できます。
ただし、一般に、タイルマップはすべてを実行できるべきではありません。すべてのタイルを管理するクラス「TileMap」があり、それに渡したオブジェクトとマップ上のタイル。次に、私は別の「RPGMap」クラスを持っているか、またはそれを呼び出したいと思います。これには、タイルマップへの参照とプレーヤーへの参照の両方があり、実際のUpdate()呼び出しをプレーヤーとタイルマップ。
プレーヤーが動いたときにモデルを更新する方法は、何をしたいかによって異なります。
プレイヤーはタイル間を独立して移動できますか(ゼルダスタイル)?入力を処理し、それに応じてプレーヤーをフレームごとに移動するだけです。または、プレーヤーに「右」を押して、キャラクターが自動的に1タイル右に移動するようにしますか?RPGMapクラスがプレイヤーが目的地に到着するまでプレイヤーの位置を補間し、その間すべての移動キー入力処理をロックします。
どちらの方法でも、自分で簡単にしたい場合、すべてのモデルは、(変数の値を変更するだけでなく)実際に更新するロジックが必要な場合にUpdate()メソッドを持ちます-コントローラーを譲らないMVCパターンでは、コードを「1つ上のステップ」(コントローラー)からモデルに移動するだけで、コントローラーが行うのは、モデルのこのUpdate()メソッドを呼び出すことだけです(この場合のコントローラーはRPGMap)。ロジックコードは簡単に入れ替えることができます。クラスのコードを直接変更するか、まったく異なる動作が必要な場合は、モデルクラスから派生させ、Update()メソッドのみをオーバーライドできます。
このアプローチは、メソッド呼び出しなどを大幅に削減します。これは、純粋なMVCパターンの主な欠点の1つでした(GetThis()GetThat()を非常に頻繁に呼び出すことになります)。これにより、コードが長くなり、少し読みづらく、速度も遅い-それは、そのような多くのものを最適化するコンパイラによって処理されるかもしれませんが。