【ゲーム制作】No.19 DirectXTK::SpriteFont①(Direct3D11)

3Dのワールド空間で表示します。

 

2Dのワールド空間でも表示できます。

 

テキスト枠に使用した画像です。ペイントで15分ぐらいで作成しました。

これをDDSファイルに変換し使用します。

 

今回は テキストの描画になります。
テキストを描画する機能は既に実装しておりましたが、
ASCII(アスキー)コードを使用する仕様だった為、
英語しか使用出来ず、ひらがなや漢字は使えませんでした。
今回実装するにあたり、ひらがなや漢字も使用できるようにします。

 

元になるライブラリをどうするか探したところ、
DirectXTKのSpriteFontが良さそうだったので
これで行くことにしました。
(理由としてはC++であること、とてもシンプルな仕様だったことです)

 

DirectXTKは古いライブラリである為か、問題が結構出ました。
その内容を以下に列挙します。

 

1、MakeSpriteFont

DirectXTKのSpriteFontでテキストを表示する場合、
別途spritefontビットマップファイルという文字情報が必要になります。
これはDirectXTKに含まれるMakeSpriteFont.exeで作成出来ますが
ひとつ問題があるとのことです。
Googleで検索すると多くサイトで出てきますが、
どうもファイルの作成に多くの時間がかかり
12時間使っても完成しないとのこと。

この修正にはカスタマイズしたMakeSpriteFont.exeを使うようですが
このファイルを配布していた方は現在作業を停止しており、
ファイルを得ることは出来ないようです。

詰んだかと思いましたが、実際に処理に時間がかかるのか確認したところ
問題無く普通に終了出来ました。
どうやら、以前に修正がされていたようです。

ただしポイントとして、/FastPackコマンドラインオプションを使う必要があるようです。

// 使用例
./MakeSpriteFont.exe "MS ゴシック" myfile.spritefont /FontSize:12 /FastPack /CharacterRegion:32-126 /CharacterRegion:0x3000-0x30ff /CharacterRegion:0xff01-0xffe5 /CharacterRegion:0x4e00-0x9fff


2、LNK2019: 未解決の外部シンボル 

SpriteFontの動作確認を行なう為、仮のプロジェクトを組んだところ

上記エラーが発生しビルドに失敗するというもの。

原因としては、DirectXTKの関数の呼び出し規則(__fastcallなど)は、
XM_CALLCONVで管理されています。
XM_CALLCONVは、VisualStudioの”拡張命令セットを有効にする”オプションで変更出来ます。
これの値がDirectXTKとプロジェクトで一致していなかった為、

LNK2019: 未解決の外部シンボルが発生していました。


3、DirectX::BasicEffect

DirectX::BasicEffectクラスを使用する場合、
以下のファイルをインクルードする必要がありますが
これをそのまま行うとエラーになりました。

#include <Effects.h>

WindowsSDKの同名のヘッダファイルがあり、
そっちを参照する為のようです。
設定を変更するのも良いですが、
フルパスで書いた方が楽なのでそうしました。

#include <C:\Development Environments\DirectXTK\Inc\Effects.h>    // フルパスで記入する。


4、DirectX::PrimitiveBatch

PrimitiveBatchクラスは
#include <VertexTypes.h> も必要です。


基本的にDirectXTKは、以前のXNA版の情報と混ざって
情報が公開されているようで、細かいところが少しづつ
違っているように見えます。
それを踏まえて情報収集する必要がありました。

 

以下は、テキストとテキスト枠を表示する例になります。
表示形式は、3Dのワールド空間と2Dのワールド空間を選択出来ます。

 

// テキストの表示テスト
HOUZMETHOD_VD sprite_font_effect_t::text_out_test(const ID3D11DeviceContextPtr&    device_context)
{
    try
    {
        // テストの為にテキストとテキスト枠を表示させます。

        HRESULT    hr    = S_OK;

        // d3ddeviceポインタが必要な場合、コメントアウトして使ってください。
        // (今のところ必要はありません。)
        //ID3D11DevicePtr d3ddevice;
        //device_context->GetDevice(&d3ddevice);

        // InputLayoutでワーニングが出る場合、コメントアウトしてください。
        // (今のところ他のところでその処理が入っている為、必要はありません。)
        //if( m_input_layout)
        //    device_context->IASetInputLayout(m_input_layout);


        // フォントの表示方式を3Dまたは2Dに切り替えます。

        DirectX::XMMATRIX    world_view_proj;
        DirectX::XMFLOAT2    batch_position, font_position;
        float_t                batch_scale, font_scale;

        extern bool    gui_b_directxtk_font_world_on_3d;

        if( gui_b_directxtk_font_world_on_3d)
        {    // 3D World

            DirectX::XMMATRIX    world    = DirectX::XMMatrixScaling( 1, -1, 1);    // World座標はY座標が逆になっている為修正します。
            world_view_proj    = world * m_view * m_proj; // View行列と射影行列は普段使用しているものを流用します。(この例ではTranspose()による転置が必要でした。

            // 入力している値はどれも適当です。
            batch_position    = { -3, -5};
            font_position    = { -3 + 8 * 0.01, -5 + 8 * 0.01};
            batch_scale        = 0.01;
            font_scale        = 0.01;

            m_p_SpriteBatch->SetRotation( DXGI_MODE_ROTATION_UNSPECIFIED); // これで行列が適用されます。
        }
        else
        {    // 2D World

            world_view_proj    = DirectX::XMMatrixIdentity();

            // 入力している値はどれも適当です。
            batch_position    = { 32,180};
            font_position    = { 32 + 16,180 + 16};
            batch_scale        = 1.0;
            font_scale        = 1.0;

            m_p_SpriteBatch->SetRotation( DXGI_MODE_ROTATION_IDENTITY); // これで行列が適用されなくなります。
        }


        // Begin()関数により、描画の開始を行います。

        m_p_SpriteBatch->SpriteBatch::Begin(
            DirectX::SpriteSortMode_Deferred,
            nullptr,
            nullptr,
            nullptr,
            m_p_rasterizer_state,
            nullptr,
            world_view_proj);    // world_view_proj行列はここで渡します。


        // SpriteBatchによりテキスト枠を描画します。

        m_p_SpriteBatch->Draw(
            m_p_srv_diffuse_map,        //_In_ ID3D11ShaderResourceView* texture,
            batch_position,                // XMFLOAT2 const& position,
            nullptr,                    // _In_opt_ RECT const* sourceRectangle,
            DirectX::Colors::White,        // FXMVECTOR color = Colors::White,
            0,                            // float rotation = 0,
            DirectX::XMFLOAT2( 0, 0),    // XMFLOAT2 const& origin = Float2Zero,
            batch_scale,                // float scale = 1,
            DirectX::SpriteEffects_None,// SpriteEffects effects = SpriteEffects_None,
            0.0                            // float layerDepth = 0
            );


        // 表示するテキストを作成します。
        // テキストの内容は適当に、1文字づつ表示させます。

        tstring_t        str1        = _T("念願の アイスソードを\n手にいれたぞ!");
        const size_t    str1_size    = str1.size();

        static int_t    s_int    = str1_size;
        str1.erase( str1_size - s_int);
        s_int    = s_int > 0 ? s_int - 1 : str1_size;


        // SpriteFontによりテキストを描画します。

        m_p_SpriteFont->DrawString(
            m_p_SpriteBatch.get(),
            str1.c_str(),
            font_position,                // XMFLOAT2 const& position,
            DirectX::Colors::Black,        // FXMVECTOR color = Colors::White,
            0,                            // float rotation = 0,
            DirectX::XMFLOAT2( 0, 0),    // XMFLOAT2 const& origin = Float2Zero,
            font_scale,                    // float scale = 1,
            DirectX::SpriteEffects_None,// SpriteEffects effects = SpriteEffects_None,
            0.0                            // float layerDepth = 0
            );
        
        // End()関数により、描画の終了を行います。
        m_p_SpriteBatch->End();
    }
    catch( const general_exception&)
    {
        throw;    }
}

 

次回も引き続きSpriteFontを行う予定です。