ゲームフレームワーク的なものを作る。(6)〜スプライト描画〜

今回はDirect3D側で用意されているID3DXSpriteインターフェイスを使って、画像ファイルからテクスチャを生成し、画像の描画を行います。

スプライト描画オブジェクトの生成

Graphicsクラスにスプライト描画オブジェクト生成メソッドを追加します。前述のデバイスやフォントと同様、std::shared_ptrを使って自動的に解放します。

typedef std::shared_ptr< ID3DXSprite > SpritePtr;

class Graphics
{
/*略*/
public:
    /**
     *  スプライト描画オブジェクトの生成
     */
    SpritePtr CreateSprite();

};
/**
 *  スプライト描画オブジェクトの生成
 *
 *  @return スプライト描画オブジェクトを指すスマートポインタ
 */
SpritePtr Graphics::CreateSprite()
{
    ID3DXSprite* sprite = nullptr;
    HRESULT hr = D3DXCreateSprite( device.get(), &sprite );
    if( FAILED( hr ) )throw std::runtime_error( "D3DXCreateSprite" );

    return SpritePtr( sprite, com_deleter() );
}

テクスチャの生成

スプライト描画オブジェクト同様、Graphicsクラスにテクスチャ生成メソッドを追加します。

typedef std::shared_ptr< ID3DXSprite > SpritePtr;

class Graphics
{
/*略*/
public:
    /**
     *  画像ファイルからテクスチャを生成
     */
    TexturePtr CreateTexture( const std::string& filename );
/**
 *  画像ファイルからテクスチャを生成
 *
 *  @param  filename    ファイル名
 *  @return 生成したテクスチャを指すスマートポインタ
 */
TexturePtr Graphics::CreateTexture( const std::string& filename )
{
    IDirect3DTexture9* texture = nullptr;
    HRESULT hr = D3DXCreateTextureFromFile( device.get(), filename.c_str(), &texture );
    if( FAILED( hr ) )throw std::runtime_error( "D3DXCreateTextureFromFile[" + filename + "]" );

    return TexturePtr( texture, com_deleter() );
}

使い方

まずはGameクラスのメンバに持たせて、ID3DXSpriteインターフェイスの機能を直接使ってみましょう。

/**
 *  Gameクラス
 */
class Game
{
/*略*/
private:
    Graphics graphics;
    SpritePtr sprite;
    TexturePtr texture;
};
Game::Game( const WindowWeakPtr& window )
    : graphics( window )
    , sprite( graphics.CreateSprite() )
    , texture( graphics.CreateTexture( "image\\bg.bmp" ) )  //画像ファイル名を指定
{
/*略*/
}

Beginメソッド〜Endメソッド内でDrawメソッドを呼び出すことで、複数のスプライトを描画することができます。(レンダーステートを変更しない描画物は、1度のBegin〜Endで一気に描画した方が効率が良い)

void Game::Run()
{
    //シーンの実行
    ;

    //バックバッファをクリアし、シーンのレンダリングを開始する
    //レンダリング終了後、画面を更新する
    graphics.Clear();
    if( graphics.BeginScene() )
    {
        //シーンの描画
        ;

        //Begin〜End内で描画処理を記述
        sprite->Begin( 0 );
        {
            RECT rect = { 0, 0, 720, 480 };        //描画元画像の矩形を指定
            D3DXVECTOR3 pos( 0.0f, 0.0f, 0.0f );   //描画先座標を指定

            sprite->Draw( texture.get(), &rect, nullptr, &pos, 0xffffffff );
        }
        //以下同様にDrawメソッドを呼び出す。
        {
            /*略*/
        }
        sprite->End();

        sprite->Begin( 0 );
        /*略*/
        sprite->End();

        graphics.EndScene();
    }
    graphics.Update();
}

行列(スケーリング、回転、平行移動)を利用する。

ID3DXSpriteインターフェイスには、いわゆるアフィン変換(スケーリング、回転、平行移動など)を行うためのSetTransformメソッドが用意されています。
行列の設定には

  • D3DXMATRIXクラス
  • D3DXMatrixScaling
  • D3DXMatrixRotationX/Y/Z/YawPitchRoll/Axis
  • D3DXMatrixTranslation
  • D3DXMatrixMultiply
  • D3DXMatrixIdentity

などを使用します。
行列を使用する際、Drawメソッドには描画座標指定せず、矩形と回転の中心座標のみ指定した方が使いやすいです。

        sprite->Begin( D3DXSPRITE_ALPHABLEND );
        {
            RECT rect = { 0, 0, 128, 128 };              //描画元画像の矩形を指定
            D3DXVECTOR3 center( 64.0f, 64.0f, 0.0f );    //矩形の左上を基準とした回転の中心座標を指定

            D3DXMATRIX mat, m;
            D3DXMatrixIdentity( &mat );
            D3DXMatrixScaling( &m, 1.0f, 1.0f, 1.0f );
            D3DXMatrixMultiply( &mat, &mat, &m );
            D3DXMatrixRotationZ( &m, D3DXToRadian( 30.0f ) );
            D3DXMatrixMultiply( &mat, &mat, &m );
            D3DXMatrixTranslation( &m, 360.0f, 240.0f, 0.0f );
            D3DXMatrixMultiply( &mat, &mat, &m );

            sprite->SetTransform( &mat );
            sprite->Draw( texture2.get(), &rect, &center, nullptr, 0xffffffff );
        }
        sprite->End();


DDSファイルを使用する。

上記の例ではαチャンネル付き画像としてDDSファイルを使用しています。αチャンネル付き画像はPhotoshopGIMPなどのソフトやDirectXSDK付属のTextureToolで作成可能です。
作り方はgoogle先生に聞いてください。「DDSファイル

行列スタック(ID3DXMatrixStackインターフェイス)を使用する。

関節などの階層構造を持つオブジェクトの描画には、行列スタックを使うと便利です。Direct3Dには行列スタックを扱うためのID3DXMatrixStackインターフェイスが用意されています。

typedef std::shared_ptr< ID3DXMatrixStack > MatrixStackPtr;

class Graphics
{
/*略*/
public:
    /**
     *  行列スタックオブジェクトの生成
     */
    MatrixStackPtr CreateMatrixStack();

};
/**
 *  行列スタックオブジェクトの生成
 *
 *  @return 行列スタックオブジェクトを指すスマートポインタ
 */
MatrixStackPtr Graphics::CreateMatrixStack()
{
    ID3DXMatrixStack* matrix_stack = nullptr;
    HRESULT hr = D3DXCreateMatrixStack( 0, &matrix_stack );
    if( FAILED( hr ) )throw std::runtime_error( "D3DXCreateMatrixStack" );

    return MatrixStackPtr( matrix_stack, com_deleter() );
}

一例として、マウス座標で回転するオブジェクト(はちゅねミク)のまわりを自転しながら公転するオブジェクト(ネギ)を描画してみます。

class Game
{
/*略*/
private:
    Graphics graphics;              ///<    描画機能
    SpritePtr sprite;               ///<
    MatrixStackPtr matrix_stack;    ///<    
    TexturePtr texture;
    TexturePtr texture2;

    float rotate;
};
Game::Game( const WindowWeakPtr& window )
    : window( window )
    , graphics( window )
    , sprite( graphics.CreateSprite() )
    , matrix_stack( graphics.CreateMatrixStack() )
    , texture( graphics.CreateTexture( "image\\bg.bmp" ) )      //画像ファイル名を指定
    , texture2( graphics.CreateTexture( "image\\miku.dds" ) )   //画像ファイル名を指定
    , rotate( 0.0f )
{
/*略*/
}
        rotate += D3DXToRadian( 1.0f );

        //ミクの描画
        //  マウス位置に描画
        matrix_stack->LoadIdentity();
        {
            RECT rect = { 0, 0, 128, 128 };
            D3DXVECTOR3 center( 64.0f, 64.0f, 0.0f );
            matrix_stack->TranslateLocal( mouse_pos.x, mouse_pos.y, 0.0f );
            matrix_stack->RotateYawPitchRollLocal( 0.0f, 0.0f, rotate );  //自転角
            matrix_stack->ScaleLocal( 1.0f, 1.0f, 1.0f );

            //ネギの描画
            //  ミクを中心に自転しながら公転する
            matrix_stack->Push();
            {
                RECT rect = { 128, 0, 256, 128 };
                D3DXVECTOR3 center( 64.0f, 64.0f, 0.0f );
                matrix_stack->RotateYawPitchRollLocal( 0.0f, 0.0f, rotate );  //公転角
                matrix_stack->TranslateLocal( 100.0f, 0.0f, 0.0f );         //公転半径
                matrix_stack->RotateYawPitchRollLocal( 0.0f, 0.0f, rotate );  //自転角
                matrix_stack->ScaleLocal( 1.0f, 1.0f, 1.0f );
                
                sprite->SetTransform( matrix_stack->GetTop() );
                sprite->Draw( texture2.get(), &rect, &center, nullptr, 0xffffffff );
            }
            matrix_stack->Pop();

            sprite->SetTransform( matrix_stack->GetTop() );
            sprite->Draw( texture2.get(), &rect, &center, nullptr, 0xffffffff );
        }

download


今回はDirectXの機能を直接利用してスプライト描画を行いましたが、DirectXに依存したプログラムは出来るだけ書きたくないので、前回作成したRectクラス、Colorクラスの利用、また今後の3D描画のためのベクトルクラス、行列クラスの作成なども行っていきたいと思います。