ゲームフレームワーク的なものを作る。(5)〜Point、Size、Rect、Color〜
今回はグラフィックス系プログラムでよく使われる点、サイズ、矩形、色クラスを作ります。
とりあえず、基本的な機能のみ。
Pointクラス
スクリーン上の点を表す。
class Point { public: Point( int x = 0, int y = 0 ); public: int x, y; }; Point::Point( int x, int y ) : x( x ), y( y ) { }
Sizeクラス
スクリーン上の矩形の縦横サイズを表す。
class Size { public: Size( int width = 0, int height = 0 ); public: int width, height; }; Size::Size( int width, int height ) : width( width ), height( height ) { }
Rectクラス
スクリーン上の矩形を表す。
class Rect { public: /** * コンストラクタ。左上と右下の点を指定 */ Rect( int left = 0, int top = 0, int right = 0, int bottom = 0 ); /** * コンストラクタ。左上と右下の点を指定 */ Rect( const Point& left_top, const Point& right_bottom ); /** * コンストラクタ。左上の点とサイズを指定 */ Rect( const Point& left_top, const Size& size ); public: //矩形の幅を取得 int Width()const{ return right - left; } //矩形の高さを取得 int Height()const{ return bottom - top; } //矩形のSizeを取得 Size GetSize()const{ return Size( Width(), Height() ); } //矩形の左上の点と右下の点にoffsetを加算 void Offset( const Point& offset ); public: int left, top, right, bottom; ///< 左上と右下の点 }; Rect::Rect( int left, int top, int right, int bottom ) : left( left ), top( top ) , right( right ), bottom( bottom ) { } Rect::Rect( const Point& left_top, const Point& right_bottom ) : left( left_top.x ), top( left_top.y ) , right( right_bottom.x ), bottom( right_bottom.y ) { } Rect::Rect( const Point& left_top, const Size& size ) : left( left_top.x ), top( left_top.y ) , right( left_top.x + size.width ), bottom( left_top.y + size.height ) { } void Rect::Offset( const Point& offset ) { left += offset.x; top += offset.y; right += offset.x; bottom += offset.y; }
Colorクラス
0〜1の成分(r,g,b,a)の色を表す。
class Color { public: Color( float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 0.0f ); public: DWORD to_A8R8G8B8()const { DWORD a_ = static_cast< DWORD >( a * 255 ) << 24; DWORD r_ = static_cast< DWORD >( r * 255 ) << 16; DWORD g_ = static_cast< DWORD >( g * 255 ) << 8; DWORD b_ = static_cast< DWORD >( b * 255 ) << 0; return a_ | r_ | g_ | b_; } public: float r, g, b, a; }; Color::Color( float r, float g, float b, float a ) : r( r ), g( g ), b( b ), a( a ) { }
文字列化インターフェイス
ゲームのデバッグでは、リアルタイムに変化するパラメータを常に画面上に表示することが多いと思いますが、例えばRectのメンバを表示するためには、以下のように非常に面倒な記述が必要になります。
Rect rect( 1, 2, 3, 4 ); std::stringstream ss; ss << "rect:" << "( " <<rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom << " )" << std::endl; graphics.DrawString( Point( 0, 0 ), ss.str(), Color( 1.0f, 1.0f, 1.0f, 1.0f ) );
以下のように、ストリームに直接渡せるようになると便利ですよね。
Rect rect( 1, 2, 3, 4 ); std::stringstream ss; ss << "rect:" << rect << std::endl; //"rect:( 1, 2, 3, 4 )" graphics.DrawString( Point( 0, 0 ), ss.str(), Color( 1.0f, 1.0f, 1.0f, 1.0f ) );
ということで、文字列化インターフェイスの作成と、operator<<のオーバーロードを行いましょう。
class IStringizable { /** * 文字列への変換 */ virtual std::string to_string()const = 0; /** * ostreamへの挿入 */ friend inline std::ostream& operator<<( std::ostream& os, const IStringizable& s ) { os << s.to_string(); return os; } };
使い方
デバッグ時にストリームに対して出力したいクラスにIStringizableインターフェイスを実装します。
class Rect : public IStringizable { /*略*/ private: /** * 文字列への変換 */ std::string to_string()const; }; /** * 文字列への変換 */ std::string Rect::to_string()const { std::stringstream ss; ss << "( " << left << ", " << top << ", " << right << ", " << bottom << " )"; return ss.str(); }
Point point( 1, 2 ); Size size( 1, 2 ); Rect rect( 1, 2, 3, 4 ); Color color( 1.0f, 1.0f, 1.0f, 1.0f ); std::stringstream ss; ss << "point:" << point << std::endl << "size:" << size << std::endl << "rect:" << rect << std::endl << "color:" << color << std::endl ; graphics.DrawString( Point( 0, 0 ), ss.str(), Color( 1.0f, 1.0f, 1.0f, 1.0f ) );
ダウンロード
- framework06.zip
- VC++2010 ExpressEdition、DirectX SDK(February 2010)で動作確認
stringizableの発音がわかりません><
explicit(06/21追記)
コメントでご指摘頂いた通り、コンストラクタにexplicitを付けなければ、暗黙の変換によって意図しない変換が行われてしまいます。
graphics.DrawString( 1, //Point( 1, 0 )が渡される。 ss.str(), Color( 1.0f, 1.0f, 1.0f, 1.0f ) );
あるいは、
Point point( 1, 2 ); point = 1; //explicitがなければ、これもOK。意味がわからないですね。
非常にまずいですね。ということで、コンストラクタに引数が1つ、または2つ以上ある時で2つ目以降にデフォルト引数が設定されている場合、原則としてexplicitを付けましょう。たとえば文字列クラスのように、暗黙の変換がなければ不便な場合だけexplicitを外すとよいです。
class Point : public IStringizable { public: /** * コンストラクタ */ explicit Point( int x = 0, int y = 0 ); /*略*/ };
class Size : public IStringizable { public: /** * コンストラクタ */ explicit Size( int width = 0, int height = 0 ); /*略*/ };
class Rect : public IStringizable { public: /** * コンストラクタ。左上と右下の点を指定 */ explicit Rect( int left = 0, int top = 0, int right = 0, int bottom = 0 ); /*略*/ };
class Color : public IStringizable { public: /** * コンストラクタ */ explicit Color( float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 0.0f ); /*略*/ };