文字の表示

Page creation date :

The page you are currently viewing does not support the selected display language.

本来はポリゴンの作成などからはじめるのが普通なのですが、情報を知るのに文字が表示されたほうが都合がいいので、Direct3D 上で文字を表示させたいと思います。

文字の表示

今回のメインコードファイルを載せます。

MainSample.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MDXSample
{
    /// <summary>
    /// メインサンプルクラス
    /// </summary>
    public partial class MainSample : IDisposable
    {
        /// <summary>
        /// Direct3D 用フォント
        /// </summary>
        private Microsoft.DirectX.Direct3D.Font _font = null;


        /// <summary>
        /// アプリケーションの初期化
        /// </summary>
        /// <param name="topLevelForm">トップレベルウインドウ</param>
        /// <returns>全ての初期化がOKなら true, ひとつでも失敗したら false を返すようにする</returns>
        /// <remarks>
        /// false を返した場合は、自動的にアプリケーションが終了するようになっている
        /// </remarks>
        public bool InitializeApplication(MainForm topLevelForm)
        {
            // フォームの参照を保持
            this._form = topLevelForm;

            try
            {
                // Direct3D デバイス作成
                this.CreateDevice(topLevelForm);


                // フォントデータの構造体を作成
                FontDescription fd = new FontDescription();

                // 構造体に必要なデータをセット
                fd.Height = 24;
                fd.FaceName = "MS ゴシック";

                // フォントを作成
                this._font = new Microsoft.DirectX.Direct3D.Font(this._device, fd);
            }
            catch (DirectXException ex)
            {
                // 例外発生
                MessageBox.Show(ex.ToString(), "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            return true;
        }

        /// <summary>
        /// メインループ処理
        /// </summary>
        public void MainLoop()
        {
            // 描画内容を単色でクリア
            this._device.Clear(ClearFlags.Target, Color.DarkBlue, 1.0f, 0);

            // 「BeginScene」と「EndScene」の間に描画内容を記述する
            this._device.BeginScene();


            // 座標 x=0 y=0 の位置に白色で描画
            this._font.DrawText(null, "DirectX Tips サンプル", 0, 0, Color.White);

            // Point 構造体を使用して位置を設定
            this._font.DrawText(null, "Point 構造体を使用しています。",
                new Point(250, 40), Color.Yellow);

            // Rectangle 構造体とテキストフォーマットを使用して描画
            this._font.DrawText(null, "Rectangle 構造体とテキストフォーマット",
                new Rectangle(100, 260, 500, 400),
                DrawTextFormat.Left | DrawTextFormat.Top, Color.LightPink);

            // 複数行も可能
            this._font.DrawText(null, "複数行も" + Environment.NewLine + "可能",
                new Rectangle(400, 320, 500, 400),
                DrawTextFormat.Left | DrawTextFormat.Top, Color.LightGreen);


            // 描画はここまで
            this._device.EndScene();

            // 実際のディスプレイに描画
            this._device.Present();
        }

        /// <summary>
        /// リソースの破棄をするために呼ばれる
        /// </summary>
        public void Dispose()
        {
            // フォントのリソースを解放
            if (this._font != null)
            {
                this._font.Dispose();
            }

            // Direct3D デバイスのリソース解放
            if (this._device != null)
            {
                this._device.Dispose();
            }
        }
    }
}
MainSamplePartial.cs ファイルのコードはこちらです。

MainSamplePartial.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MDXSample
{
    partial class MainSample
    {
        /// <summary>
        /// メインフォーム
        /// </summary>
        private MainForm _form = null;

        /// <summary>
        /// Direct3D デバイス
        /// </summary>
        private Device _device = null;


        /// <summary>
        /// Direct3D デバイスの作成
        /// </summary>
        /// <param name="topLevelForm">トップレベルウインドウ</param>
        private void CreateDevice(MainForm topLevelForm)
        {
            // PresentParameters。デバイスを作成する際に必須
            // どのような環境でデバイスを使用するかを設定する
            PresentParameters pp = new PresentParameters();

            // ウインドウモードなら true、フルスクリーンモードなら false を指定
            pp.Windowed = true;

            // スワップ効果。とりあえず「Discard」を指定。
            pp.SwapEffect = SwapEffect.Discard;

            try
            {
                // デバイスの作成
                this.CreateDevice(topLevelForm, pp);
            }
            catch (DirectXException ex)
            {
                // 例外発生
                throw ex;
            }
        }
        /// <summary>
        /// Direct3D デバイスの作成
        /// </summary>
        /// <param name="topLevelForm">トップレベルウインドウ</param>
        /// <param name="presentationParameters">PresentParameters 構造体</param>
        private void CreateDevice(MainForm topLevelForm, PresentParameters presentationParameters)
        {
            // 実際にデバイスを作成します。
            // 常に最高のパフォーマンスで作成を試み、
            // 失敗したら下位パフォーマンスで作成するようにしている。
            try
            {
                // ハードウェアによる頂点処理、ラスタライズを行う
                // 最高のパフォーマンスで処理を行えます。
                // ビデオカードによっては実装できない処理が存在します。
                this._device = new Device(0, DeviceType.Hardware, topLevelForm.Handle,
                    CreateFlags.HardwareVertexProcessing, presentationParameters);
            }
            catch (DirectXException ex1)
            {
                // 作成に失敗
                Debug.WriteLine(ex1.ToString());
                try
                {
                    // ソフトウェアによる頂点処理、ハードウェアによるラスタライズを行う
                    this._device = new Device(0, DeviceType.Hardware, topLevelForm.Handle,
                        CreateFlags.SoftwareVertexProcessing, presentationParameters);
                }
                catch (DirectXException ex2)
                {
                    // 作成に失敗
                    Debug.WriteLine(ex2.ToString());
                    try
                    {
                        // ソフトウェアによる頂点処理、ラスタライズを行う
                        // パフォーマンスはとても低いです。
                        // その代わり、ほとんどの処理を制限なく行えます。
                        this._device = new Device(0, DeviceType.Reference, topLevelForm.Handle,
                            CreateFlags.SoftwareVertexProcessing, presentationParameters);
                    }
                    catch (DirectXException ex3)
                    {
                        // 作成に失敗
                        // 事実上デバイスは作成できません。
                        throw ex3;
                    }
                }
            }
        }
    }
}

public partial class MainClass : IDisposable

class の前に「partial」というキーワードを付けました。C# が分かる人なら意味は分かると思いますが後ほど説明します。


/// <summary>
/// Direct3D 用フォント
/// </summary>
private Microsoft.DirectX.Direct3D.Font _font = null;

フィールドで Direct3D 用フォントを宣言しています。描画する文字列の数に関係なくフォントはひとつでいいです。実行速度を考える場合や文字の大きさが異なる場合は、あらかじめ複数作成しておいて描画時に切り替える方法もありますが、今回は文字が描画できればいいのでひとつにします。

今回、フォントを宣言するのに「Microsoft.DirectX.Direct3D.Font」とネームスペースをすべて記述していますが、これは「System.Drawing」の「Font」と「Microsoft.DirectX.Direct3D」の「Font」を区別するため、このように明確に識別しています。


try
{
    // Direct3D デバイス作成
    this.CreateDevice(topLevelForm);
        ;
        // (省略)
        ;
}
catch (DirectXException ex)
{
    // 例外発生
    MessageBox.Show(ex.ToString(), "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
    return false;
}

前回まであった Direct3D デバイス作成コードがなくなっています。実は、それらのコードを「CreateDevice」メソッドにまとめてしまいました。理由はデバイス作成処理は毎回ほとんど同じなので、いちいちメインの Tips に入れると見るときの邪魔になってくるからです。

では、そのメソッドはどこに書いてあるかというと、「MainSamplePartial.cs」にあります。このファイルを見るとなぜか同じ「MainSample」クラスがあり、普通にクラス内にメソッドとフィールドが記述されています。「C# 2.0」では「partial」修飾子によってクラスを複数のファイルに分割できる機能があります。そのため、別なファイルにコードをまとめる場合でも、わざわざ新しいクラスを作成する必要がなくなります。

もし、「C# 2.0」以前のバージョンを使用している場合は、分けているコードを一つのファイルにまとめれば大丈夫です。

MainSamplePartial.cs」ファイルを追加するには下の図のようにファイルを追加してください。ファイル名は基本的に何でもいいですが一応サンプルでは「MainSamplePartial.cs」とします。ただし、クラス名は必ず一緒(この場合「MainSample」)にしてください。

クラス追加

ファイルを作成したら下のように書き換え、フィールドやメソッドなどを追加していきます。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MDXSample
{
    public partial class MainSample
    {
    }
}

この Tips 以降でも、同じようなコードが出てきた場合は、このように「MainSamplePartial.cs」ファイルに移していくようにしたいと思います。これによって Tips を見ている方が重要な部分のみを見れるようになるかと思います。

後、Tips では別途、クラスを作成する予定はありません。もちろんクラスが必須な Tips もあるので、その場合は作ります。もし必要であれば各自クラスを作ってみるのもいいかもしれません。

さて、上記コードですが、Direct3D デバイスを作成し、失敗した場合はメソッド内で例外を飛ばすようにしていますので、例外が発生した場合はメッセージボックスを表示して false を返すようにしています。false を返した場合はそのままアプリケーションが終了するようになっています。


// フォントデータの構造体を作成
FontDescription fd = new FontDescription();

// 構造体に必要なデータをセット
fd.Height = 24;
fd.FaceName = "MS ゴシック";

// フォントを作成
this._font = new Microsoft.DirectX.Direct3D.Font(this._device, fd);

ここでフォントを作成しています。本来は、1行で済ませることもできるのですが、その場合、「Font」コンストラクタですべてのパラメータを設定しなければいけないので、今回は「FontDescription」構造体にデータを設定してそれを渡すようにしています。

FontDescription 構造体には文字の大きさ、フォント名、太さなど様々な項目があるのでヘルプなどで調べてみてください。

とりあえず「文字の高さ」と「フォント名」を指定すれば十分なので、それを指定して「Font」コンストラクタに渡します。必要であれば「文字の太さ」や「イタリック」なども設定できます。

Font コンストラクタ
Font クラスのインスタンスを作成します。
device Direct3D デバイスを渡します。
description 設定済みの FontDescription 構造体を渡します。

// 座標 x=0 y=0 の位置に白色で描画
this._font.DrawText(null, "DirectX Tips サンプル", 0, 0, Color.White);

// Point 構造体を使用して位置を設定
this._font.DrawText(null, "Point 構造体を使用しています。",
    new Point(250, 40), Color.Yellow);

// Rectangle 構造体とテキストフォーマットを使用して描画
this._font.DrawText(null, "Rectangle 構造体とテキストフォーマット",
    new Rectangle(100, 260, 500, 400),
    DrawTextFormat.Left | DrawTextFormat.Top, Color.LightPink);

// 複数行も可能
this._font.DrawText(null, "複数行も" + Environment.NewLine + "可能",
    new Rectangle(400, 320, 500, 400),
    DrawTextFormat.Left | DrawTextFormat.Top, Color.LightGreen);

これが文字を描画するコードになります。本来は1行でいいのですが、いろんなパターンで描画してみることにしました。

1行目は文字列を描画する位置を X、Y 別々に指定して描画しています。位置は文字列の左上が基準になります。色は描画するときに指定できるので、わざわざ別のフォントを用意する必要はありません。

2行目は位置を「Point」構造体を使用して指定しています。

3行目は矩形を使用して描画範囲を設定します。矩形内でどのように描画するかを「DrawTextFormat」で指定します。ここでは矩形の「左上」に描画するように指定しています。もちろん真ん中や右下などの位置を指定することができます。また、テキストフォーマットに「DrawTextFormat.NoClip」を指定しない限りは、矩形外にはみ出た文字は描画されません。

4行目は文字列に改行を入れています。改行コードは「\r\n」ですが、「Environment.NewLine」を使用することが推奨されています。理由は環境によって改行コードが違う場合に対処できるからです。テキストフォーマットで「DrawTextFormat.SingleLine」を指定した場合は、改行コードが入っていても改行できないようにできます。

Font.DrawText メソッドの第1引数は null を指定していますが、ここには「Sprite」を指定できます。これを指定すると描画速度を若干向上させることができますが、今回は文字を描画できればいいので省略しています。

Font.DrawText メソッド
テキストを描画します
sprite Sprite クラスを使用して描画効率を上げます。使用しない場合は null を指定
text 描画する文字列
rect 描画する範囲
format 書式設定。DrawTextFormat 列挙型の組み合わせを指定
color 文字の色

// フォントのリソースを解放
if (this._font != null)
{
    this._font.Dispose();
}

フォントも使用し終わったらリソースを解放します。