2Dゲームで画像を用意する時、パフォーマンスの面からゲームで使う画像を大きな画像に纏める時があります。アニメーションの実装時も同じく一つの画像にアニメーションで使う全画像(コマ)を一つの大きな画像にまとめ、一部分だけ描画します。
例を上げると、以下ような画像の1コマ分だけを描画します。画像はXNAのスターターキットから借りています。
上の画像の大きさは480 x 48なので、1コマ 48 x 48で横に10コマとして扱います。
実装にはまず、アニメーション用のクラスを宣言し、必要な変数とコンストラクタを書きます。
class Sprite { Texture2D m_texture; // アニメーションを含んだ画像 Vector2 m_position; // 位置 Point m_frameSize; // 1コマの大きさ Point m_currentFrame; // 現在どのフレームかを表す Point m_sheetSize; // アニメーションが縦横何コマあるか float m_timer; // アニメーション用タイマー float m_updateInterval; // 何秒ごとに次のコマに進むか public Sprite(Texture2D tex, Vector2 pos, Point frameSize, Point sheetSize, float updateInterval) { m_timer = 0.0f; m_texture = tex; m_position = pos; m_frameSize = frameSize; m_currentFrame = new Point(0, 0); m_sheetSize = sheetSize; m_updateInterval = updateInterval; }
次に更新処理を実装します。現在描画するコマと、次のコマに進むかどうかの判断はこのメソッド内で実装しています。行う事は一定の時間が過ぎたら次のコマに進むだけで、それ以降は一番右のコマか、一番下のコマを通り過ぎた時の処理を行っているだけです。
なお、上記で紹介したサンプル画像では全ての画像が横並びでしたが、アニメーション用の画像ではよく大きさが縦横四角になっている場合も多く(480 x 480など)、その場合は横にコマを進めるだけでなく、縦にも進める必要があるので、その処理も実装しています。
public virtual void Update(float delta) { // タイマーを更新 m_timer += delta; // もし次のコマに進む時間が過ぎたら if (m_timer > m_updateInterval) { // タイマーをリセット m_timer = 0.0f; // 右に1コマ進む m_currentFrame.X++; // もしX軸のコマ数が、画像にあるコマ数を超えていた場合 if (m_currentFrame.X >= m_sheetSize.X) { // 一番左のコマへ変更 m_currentFrame.X = 0; // 一段下のコマに変更 m_currentFrame.Y++; if (m_currentFrame.Y >= m_sheetSize.Y) { // 一番上の段に変更 m_currentFrame.Y = 0; } } } }
次に描画処理です。このクラスでの描画処理は、SpriteBath.Draw()のオーバーロードの一つを使用し、第3引数に描画する画像の中で描画する範囲をRectangle型で指定します。
public virtual void Draw(SpriteBatch sp) { // 描画するコマを計算する Rectangle texRect = new Rectangle(m_currentFrame.X * m_frameSize.X, m_currentFrame.Y * m_frameSize.Y, m_frameSize.X, m_frameSize.Y); // 現在のコマを描画する sp.Draw(m_texture, m_position, texRect, Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0.0f); }
後は画像を読み込み、Spriteクラスを使用して描画するだけです。今回のサンプルコードはGame1クラス内で書かれています。
Sprite player; protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); Texture2D playerTex = Content.Load<Texture2D>("player"); player = new Sprite(playerTex, new Vector2(200, 200), new Point(48, 48), new Point(10, 1), 0.06f); } protected override void Update(GameTime gameTime) { float delta = (float)gameTime.ElapsedGameTime.TotalSeconds; player.Update(delta); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); player.Draw(spriteBatch); spriteBatch.End(); base.Draw(gameTime); }
以上で実装は終わりです。実装後は以下のようになります。
もしアニメーションの速度が速すぎたり、遅すぎたら、Spriteクラスのコンストラクタの第5引数を変更してみてください。なお、現在の実装ではアニメーションのコマの数と、1コマの大きさは自分で計算してSpriteクラスに渡す必要があります。