2.クラス化・1


0.ざっくり解説

ということで今回は前回までに作ったDirect3Dの初期化とフォントの作成までをクラスとしてまとめてみます。
まず気になるのは前回まででグローバル変数になっていたポインタ達です。
ソースの色々な所でこれを直接参照していると後々DirectXのバージョンが変わった場合などに面倒なのでとりあえずこれをクラスで囲みます。
ポインタをメンバ変数に
class Graphic
{
private:
    LPDIRECT3D9 pD3D_;              //!<D3Dオブジェクト
    LPDIRECT3DDEVICE9 pDevice_;     //!<デバイスポインタ
};
class DebugFont
{
private:
    LPD3DXFONT pFont_;  //!<フォントオブジェクトへのポインタ
};
メンバ変数を持っているだけだと何にもならないので、
次に前回のサンプルで使っていた機能を使用出来るようにクラスに関数を追加していきます。
グラフィック
class Graphic
{
public:
    Graphic() : pD3D_(0), pDevice_(0){}
    virtual ~Graphic(){ MMM_DEBUG_CHECK(pD3D_ == 0); MMM_DEBUG_CHECK(pDevice_ == 0); }
public:
    //色生成インライン関数
    inline static unsigned int ColorRGB(unsigned char r, unsigned char g, unsigned char b)
    {
        return (0xff << 24) | (r << 16) | (g << 8) | (b);
    }
    inline static unsigned int ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
    {
        return (a << 24) | (r << 16) | (g << 8) | (b);
    }
public:
    //!クリアの動作フラグ
    enum
    {
        CLEAR_IMAGE = 0x01//!<画面クリア
        CLEAR_Z = 0x02,     //!<Zバッファクリア
    };
public:
    //!生成関数
    bool Create(HWND hWnd, int screenWidth, int screenHeight)
    {
        pD3D_ = Direct3DCreate9(D3D_SDK_VERSION);
        if(pD3D_ == 0) return false;

        //ディスプレイモードの取得
        D3DDISPLAYMODE dm ={0};
        if(FAILED(pD3D_->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dm)))
        {
            SafeReleaseD3D(pD3D_);
            return false;
        }

        //デバイスの生成用パラメータの初期化
        D3DPRESENT_PARAMETERS pp;
        ZeroMemory(&pp, sizeof(pp));
        pp.Windowed = TRUE; 
        pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
        pp.BackBufferFormat = dm.Format;
        pp.BackBufferWidth = screenWidth;
        pp.BackBufferHeight = screenHeight;
        pp.BackBufferCount = 1;
        pp.EnableAutoDepthStencil = TRUE;
        pp.AutoDepthStencilFormat = D3DFMT_D16;

        //デバイスの生成
        //条件が厳しいものから試す
        if(FAILED(pD3D_->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
            hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &pp, &pDevice_)))
        {
            if(FAILED(pD3D_->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF,
                hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &pp, &pDevice_)))
            {
                if(FAILED(pD3D_->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_SW,
                    hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &pp, &pDevice_)))
                {
                    SafeReleaseD3D(pD3D_);
                    return false;
                }
            }
        }

        return true;
    }
    //!解放関数
    bool Release()
    {
        SafeReleaseD3D(pD3D_);
        SafeReleaseD3D(pDevice_);

        return true;
    }
public:
    bool IsCreated(){ return pDevice_ != 0; }
public:
    //!内部用の取得関数
    LPDIRECT3DDEVICE9 GetDevice()
    {
        return pDevice_;
    }
public:
    //!クリア関数
    void Clear(int clearFlag, unsigned int color, float z)
    {
        MMM_DEBUG_CHECK(pDevice_);

        DWORD flag = 0;
        if(clearFlag & CLEAR_IMAGE) flag |= D3DCLEAR_TARGET;
        if(clearFlag & CLEAR_Z) flag |= D3DCLEAR_ZBUFFER;
        if(flag == 0) return;

        pDevice_->Clear(0, 0, flag, color, z, 0);
    }
public:
    bool BeginScene()
    {
        MMM_DEBUG_CHECK(pDevice_);

        return SUCCEEDED(pDevice_->BeginScene());
    }
    bool EndScene()
    {
        MMM_DEBUG_CHECK(pDevice_);
        return SUCCEEDED(pDevice_->EndScene());
    }
    void Present()
    {
        MMM_DEBUG_CHECK(pDevice_);

        pDevice_->Present(0, 0, 0, 0);
    }
private:
    LPDIRECT3D9 pD3D_;              //!<D3Dオブジェクト
    LPDIRECT3DDEVICE9 pDevice_;     //!<デバイスポインタ
};
デバッグフォント
class DebugFont
{
public:
    //!テキストの揃え方の種類
    enum
    {
        ALIGN_LEFT = 0x00,
        ALIGN_RIGHT = 0x01,
    };
public:
    DebugFont() : pFont_(0){}
    virtual ~DebugFont(){ MMM_DEBUG_CHECK(pFont_); }
public:
    //!生成関数
    bool Create(Graphic* pGrp, LONG width, LONG height, LONG weight, const char* faceName)
    {
        Release();

        D3DXFONT_DESC fd;
        fd.Width = width;
        fd.Height = height;
        fd.Weight = weight;
        fd.MipLevels = D3DX_DEFAULT;
        fd.Italic = FALSE;
        fd.CharSet = DEFAULT_CHARSET;
        fd.OutputPrecision = OUT_DEFAULT_PRECIS;
        fd.Quality = DEFAULT_QUALITY;
        fd.PitchAndFamily = DEFAULT_PITCH;
        strcpy_s(fd.FaceName, faceName);

        if(FAILED(D3DXCreateFontIndirect(pGrp->GetDevice(), &fd, &pFont_)))
        {
            return false;
        }

        return true;
    }
    //!解放関数
    bool Release()
    {
        SafeReleaseD3D(pFont_);
        return true;
    }
    //!文字列描画関数
    void DrawText(float x, float y, int dtFormat, DWORD color, const char* format, ...)
    {
        MMM_DEBUG_CHECK(pFont_ != 0);

        char va_str[512];
        va_list list;
        va_start(list, format);
        vsprintf_s(va_str, format, list);

        LPDIRECT3DDEVICE9 pDev = GetDevicePtr(pFont_);
        if(pDev == 0) return;

        D3DVIEWPORT9 vp;
        pDev->GetViewport(&vp);

        RECT rect;
        SetRect(&rect, (int)x, (int)y, vp.Width, vp.Height);

        int flag = 0;
        if(dtFormat & ALIGN_RIGHT) flag |= DT_RIGHT;
        pFont_->DrawText(0, va_str, -1, &rect, flag, color);

        pDev->Release();
    }
private:
    LPD3DXFONT pFont_;  //!<フォントオブジェクトへのポインタ
};
と関数を追加するとこんな感じになりました。
微妙にDirectXっぽさを隠そうとしている以外は単純なラッパーになっていますがまずはこんな形で進めてみようと思います。
後、今回から幾つかのポインタを扱うようになったのでデバッグ時用に条件式が偽であれば強制終了するマクロを用意しました。
デバッグフォント
//!デバッグ用ビルドかどうか
#define MMM_DEBUG

//デバッグ中とリリースで動作を変えるマクロ定義
#ifdef MMM_DEBUG
#define MMM_DEBUG_CHECK(expr)   {assert(expr);}
#else
#define MMM_DEBUG_CHECK //
#endif
です。
これを関数の上の方に書いて置けばポインタがnullでその関数に入ってきた場合に強制終了してくれ、VCなどを使っている場合には、
不正な操作が行われたソースと行番号も教えてくれ、非常にデバッグ時に役立つので、面倒ですが関数を作った時に入れておくといいと思います。
次回は三角形を描いてみようと思います。
今回のソース。
戻る。