ホーム < ゲームつくろー! < クラス構築編 < 線形補間テンプレートクラスを作ろう!(補間ストラテジ実装編)

区間補間テンプレートクラスを作ろう!(補間ストラテジ実装編)



 仕様の構想から始まって、コンテナの実装、イテレータの実装を見てきました。この章では、三つ巴の最後の部分である「補間ストラテジ」の実装を行います。これまでの章と少し異なりまして、もうどのようなメソッドが必要かはわかっています(前章のイテレータ実装をご覧下さい)。必要なのは「どのように補間計算を行うか」という点です。まずはメソッドを確認してから、補間ストラテジの具体的な実装をしてみます。



@ 補間ストラテジのメソッド

 補間ストラテジのメソッドは非常に限られていますし、基本的にとてもシンプルです。前章のイテレータの実装に出てきた補間ストラテジのメソッドは、

 ・ NeedPointNumメソッド
 ・ Calcメソッド
 ・ GetPtrAryメソッド

の2つです。補間ストラテジの宣言は次のようになります:

CInterpStrategy補間ストラテジクラス宣言
template< class VAL, class X=double, class W=double >
class CInterpStrategy
{
   typedef typename trio<VAL, X, W> TRI;

public:
   // 必要な点の数を取得
   virtual size_t NeedPointNum() = 0;

   // 補間値を計算
   bool Calc( VAL *pOut, X &t) = 0;

   // 点を登録する配列を取得
   TRI** GetPtrAry() = 0;
};

 補間ストラテジテンプレートクラスには3つの型引数が渡されます。VALが点の型、Xが補間距離、そしてWが付加情報(重みや方向)です。これらの抽象的な型から補間計算を行います。これにより様々な型の補間計算がサポートされるわけです。

 メソッドに目を向けます。NeedPointNumメソッドは補間計算に必要な点の数を返します。GetPtrAryメソッドは実際に補間計算をするための点を格納する配列を返します。この配列にinterpolating::iteratorが点を格納してくれます。そしてCalcメソッドによって補間計算がなされます。

 Calcメソッドの第1引数には補間結果が返ります。第2引数は補間割合です。0(に相当する値)だと基点、1だと次の点の補間値が返る事になります。

 後はこのメソッドのルールに従った派生クラスを実装するだけです。




A 線形補間ストラテジクラスの実装

 一例として、もっとも基本的な補間である線形補間を行える補間ストラテジクラス(CLinerInterpクラス)を実装してみましょう。

 まずは実装プログラムをご覧下さい:

CInterpStrategy補間ストラテジクラス宣言
///////////////////////////////////
// 線形補間ストラテジクラス
//////////
template< class VAL, class X=double, class W=double >
class CLinerInterp : public CInterpStrategy<VAL, X, W>
{
   typedef typename trio<VAL, X, W> TRI;

protected:
   TRI* Points[2];   // キー値ポインタ配列

public:
   // コンストラクタ
   CLinerInterp()
   {
      Points[0] = NULL;
      Points[1] = NULL;
   }

   // 必要な点の数を取得
   virtual size_t NeedPointNum()
   {
      return 2; // 線形補間は2つの点が必要
   }

   // 補間値を計算
   virtual bool Calc( VAL *pOut, X &t)
   {
      // 線形補間値を計算
      *pOut = (Points[0]->_val)*(1-t) + (Points[1]->_val)*t;
      return true;
   }

   // 点を登録する配列を取得
   virtual TRI** GetPtrAry()
   {
      return (TRI**)&Points;
   }
};

 これは線形補間ストラテジクラスの全ソースです。
 まずテンプレートクラスの継承方法ですが、上記のように親クラスの名前の後に型引数を渡します。Points[2]というのはTRI*型のポインタ変数を2つ格納できる配列です。要素0が基点、1が次の点である事は言うまでもありません。この配列へのポインタを渡すのがGetPtrAryメソッドです。returnの前にTRI**にキャストしているのは、静的配列のポインタをTRI**として直接返せないためです。

 補間計算の部分が注目点です。これは次の式を一般化しています:

 Points[0]がViそしてPoints[1]がVi+1に相当します。引数のtには0〜1に相当する値がやってくると考えています。計算結果は*pOutとして返されます。やっている事は単純ですが、Points[0]はあらゆる型(VAL)であるため、ありとあらゆる線形補間がここで確立されることになります。

 上の実装に習って、他の補間ストラテジクラスも作成する事が出来ます。補間ストラテジクラス群はinterpolatingやiteratorと独立していますので、割と作りやすいと思います。




B 独自クラスを補間するための演算子のオーバーロード

 この段階で、もう好きな型を好きな補間方法で補間する事が可能になっています。既存のD3DXVECTOR3やD3DXMATRIXなどももちろん可能です。これだけでも凄い恩恵があるのですが、時に自分が作成したオリジナルな型を補間したくなる事もあります。その場合、interpolatingの第1型引数にそのまま渡してもうまく動きません。それは、補間計算に必ず含まれている*や+などの演算子が不定だからです。オリジナルのクラスを補間するには、それら演算子のオーバーロードが必要になります。

 割と切実に必要になる補間として、2Dアニメーションがあります。これは、スクリーン(レイヤ)の位置を変化させたり、回転やスケール変換などを連続的に行います。そういう情報をまとめた_2DANIMATIONKEY構造体があるとします:

_2DANIMATIONKEY構造体
struct _2DANIMATIONKEY
{
   float w,h;   // 幅と高さ(基準値)
   float x,y;   // 位置
   float sx, xy;   // スケール
   float angle;   // 回転角度(ラジアン)
};

この構造体が沢山あって、interpolatingテンプレートで線形補間したい場合、

_2DANIMATIONKEY K1, K2, Res;
double t;
Res = K1*(1-t) + K2*t;

という計算を行う必要があります。K1*(1-t)というのは、_2DANIMATIONKEY構造体に対して乗算演算子の右辺にdouble型が来れる事を意味します。また_2DANIMATIONKEY構造体同士の足し算も必要です。線形補間の場合、double型(距離型)に対する掛け算と補間オブジェクト同士の足し算をオーバーロードさせる必要があります。



○ 演算子のオーバーロード例

 _2DANIMATION構造体の演算子のオーバーロードの例を挙げておきます。

_2DANIMATIONKEY構造体に対するdouble型の乗算
// double型を右辺に置く
_2DANIMATIONKEY operator *( double val )
{
   _2DANIMATIONKEY res;
   res.x = x * val;
   res.y = y * val;
   res.sx = sx * val;
   res.sy = sy * val;
   res.angle = angle * val;
   res.w = w;
   res.h = h;

   return res;
}


// double型を左辺に置く
friend _2DANIMATIONKEY operator *( double val, _2DANIMATIONKEY &src )
{
   _2DANIMATIONKEY res;
   res.x = src.x * val;
   res.y = src.y * val;
   res.sx = src.sx * val;
   res.sy = src.sy * val;
   res.angle = src.angle * val;
   res.h = src.h;
   res.w = src.w;

   return res;
}


この手のオーバーロードでは、double型を右辺にも左辺にも置けるようにしておいた方が便利です。double型を右辺に置く場合は引数にdouble型を、左辺におく場合はfriend宣言をして引数にdouble型とクラスを渡します。オブジェクト同士の足し算についてはfriend宣言する必要はありません。

 このように演算子をオーバーロードすれば、もう_2DANIMATIONKEY構造体はinterpolatingテンプレートに渡して好きなように補間する事ができるようになります。




C interpolating補間テンプレートクラスを公開します!!

 ここまで4章に分けてinterpolating補間テンプレートクラスを説明してきました。実際示してきましたプログラムを組めば自由な補間が可能になります。とは言うものの実装は大変です。そこで、ここまで紹介してきたinterpolatingテンプレートを全部公開致します(大盤振る舞いですよ〜(^-^))。試してみようかなという方は、こちらからinterpolating.lzhをダウンロードして下さい。テストプログラムも付けております。一応動くようにはしておりますが、バグ等がございましたらご報告頂けますと幸いです。また「もっと最適化ができますよ〜」「補間ストラテジを独自に開発しちゃったよ〜」という方で公開しても構わないという太っ腹な方がいらっしゃいましたら、是非ご一報下さい。

【一応以下を述べておきます】

 このプログラムの改変・コピーは許可します。使用に関して報告は必要ありません。ただし、プログラムの著作権は本サイト管理人であるIKDにあるものとします。本プログラムについてのご質問・バグレポートはサイト掲示板にて受付致しますが、ご使用によるいかなるトラブルも私IKDは一切の責任を負わないものとします。使用者の責任でご対処下さいますようお願い申し上げます(同様の事はプログラム本体及びアーカイブファイル内のReadme.txtにも記載されています).