Top > ComputerGraphics > XNA > Sprite > TextureAnimation2
Last-modified: Sat, 20 Oct 2012 06:30:19 JST
Counter:1996 Today:2 Yesterday:0 Online:3
このエントリーをはてなブックマークに追加

スプライト・テクスチャのアニメーション2

About

characterwalk.gif characteranimation.png

 前の項目「スプライト・テクスチャアニメーション1」の内容を拡張したものなので、未読の場合は参照されたい。今回はテクスチャの中から特定のフレームのみを選択してアニメーションさせます。画像の様なキャラクタの歩行アニメーションであったり、状況によって異なるアニメーションを表示するメニューを実装したい場合などに利用することができます。例えば歩行アニメーションの画像は、0~2フレームを、「0,1,2,1」の順に繰り返すようになっています。

画像出典:Made in GAPAN 歩~Ayumu~ 改変・利用が許可されています。

実装「AnimatedTexture2DSelectFrames」

 実際の所、実装に関しては殆ど説明することがありません。前回取り扱ったクラス「AnimatedTexture2D」を継承する形で実装を行っていきます。※AnimatedTexture2Dに関しても、必要な実装を追加するなどしているので今回のサンプルの方を利用されたい。

 肝心の実装方法はと言うと、下の図のように配列を利用して使いたいフレーム番号とその順番を表現します。配列を最後まで参照すると、また配列の先頭に戻ってくるようにします。

0.png

Constructorと必要な値

 クラス「AnimatedTexture2DSelectFrames」の「int[] FrameNumberArray」がフレーム番号と順を指定する変数です。まずはコンストラクタでその値を指定します(メソッドでも指定できる)。配列の長さが0である場合には、最初から最後まで全てのフレームを再生することにします。その場合は前回の「AnimatedTexture」と同じ挙動をとることになります。

 メソッド「OptimizeFrameNumbers」や「OptimizeFrameNumber」についてはサンプルのソースコードを参照されたい。指定したフレーム番号が範囲外であるとき表示が乱れたりエラーを起こす原因になるので、必ず0~最後のフレーム番号までの値に収まるように設定するためのメソッドです。Resetメソッドについては次の項目を参照されたい。

public AnimatedTexture2DSelectFrames(Texture2D texture, int frameRow,
 int frameColumn, int framePerSecond, int[] frameNumbers)
            : base(texture, frameRow, frameColumn, framePerSecond)
        {

            //指定されていれば、再生するコマとして設定
            if (frameNumbers.Length != 0)
            {
                this.FrameNumberArray = frameNumbers;
                OptimizeFrameNumbers(ref FrameNumberArray);
            }
            //指定されていなければ、全てを再生するフレームとして設定
            else
            {
                this.FrameNumberArray = new int[frameRow * frameColumn];
                for (int i = 0; i < FrameNumberArray.Length; i++)
                    FrameNumberArray[i] = i;
            }

            //再生番号などの初期化
            this.Reset();
        }

Resetのオーバーライド

 メソッド「Reset」はアニメーションなどの状態を初期化するためのメソッドで、前項目「AnimatedTexture2D」の時点で実装されていました。配列を用いるようになった「AnimatedTexture2DSelectFrames」では、初期化処理が異なるためオーバーライドして実装を上書きします。

 まず現在のフレームは0番ではなく、設定された配列の最初の値になる点に注意してください。つまり「FrameNumberArray[0]」です。値「CurrentNumberOfArray」は、現在の配列のどこを参照するのかを示す値であり、この値が0になります。あとは前回同様、合計経過時間を初期化することで全ての状態が初期化されます。

        public override void Reset()
        {
            this.currentFrame = FrameNumberArray[0];
            this.CurrentNumberOfArray = 0;
            this.TotalElapsedSecond = 0;
        }

Updateのオーバーライド

 基本的な処理は前回と同じです。先のメソッド「Reset」で説明した通り、値「CurrentNumberOfArray」は配列の内、どこを参照するかを示す値であるため、これを1つ送ります。例によって最大値を超えないようにするために剰余算を利用しています。最後にcurrentFrameを配列の示す値に設定すれば処理は完了です。

        public override void Update(float totalElapsedSecond)
        {
            //一時停止中ならUpdateを実行しない
            if (Paused)
                return;

            //合計時間を加算
            TotalElapsedSecond += totalElapsedSecond;

            //合計時間が1フレームあたりの時間を超えたら
            if (TotalElapsedSecond > SecondPerFrame)
            {
                
                //フレームを次のフレームへ
                CurrentNumberOfArray++;
                CurrentNumberOfArray = CurrentNumberOfArray % FrameNumberArray.Length;
                currentFrame = FrameNumberArray[CurrentNumberOfArray];

                //合計時間から、フレームあたりの時間を引く
                TotalElapsedSecond -= SecondPerFrame;
            }
        }

この様な実装方法になった理由

 当初は開始フレームと終了フレームを指定して、特定のフレーム間のみを再生するような状況しか想定していませんでした。そのため実装形態も異なっていたのですが、その様な実装であると、今回扱った歩行アニメーションの画像素材の様に「0→1→2→1」とフレームを繰り返す場合に対応できなくなります。かと言って「0→1→2→1」と並んだ画像を用意するのは「1」が重複して非効率だし、その分画像の編集ミスの可能性と手間も増えてきます。結果的に配列を使う実装に落ち着いていますが、結果的にこの実装方法の方が、アルゴリズムがシンプルで強力になったと思います。例えばそうそうあることではないですが「0→9→10→11→10→9→2→1→0」などの複雑なフレーム順であっても指定することが出来ます。計算量もそう増えた物ではないし、悪くない実装ではないでしょうか。

Reference