Managed DirectX (その 1 DirectDraw)

~ Cutting Edge DX 9 - 第 2 回目 ~

Hiroyuki Kawanishi (川西 裕幸)
マイクロソフト株式会社
テクニカル エバンジェリスト

February 17, 2003

目次

  • VB.NET AppWizard
  • SpriteAnimate サンプル コード

Managed DirectX は、C# や Visual Studio .NET から使うことのできる DirectX API です。C++ マネージ拡張を使えば、C++ からも使えます。Managed とついているのは、.NET Framework で提供されている API と同じように、ポインタ、例外処理、セキュリティなどをカプセル化してシステム側で管理する API だからです。Managed DirectX は .NET Framework とその共通タイプを可能な限り利用しています。例えば、Managed Direct3D テクスチャを作る際、.NET Framework の Bitmap オブジェクトから FromBitmap メソッド経由で作成できます。Managed DirectX を使えばコード数が減らせ、開発効率も向上します。

今回は Managed DirectX でのみサポートされている DirectDraw について Visual Basic .NET をベースに紹介します。ご存知のように DirectDraw は DirectX 8 からは Direct3D に統合されました、ただし DirectX 8 では DirectX for Visual Basic の形で残っていました。同様に DirectX 9.0 でも Managed DirectX として DirectDraw がサポートされています。C# と Visual Basic .NET のサンプル コードも入っています。さらに AppWizard を使った、C# と Visual Basic .NET の DirectDraw アプリケーションのスケルトン作成も可能になっています。

Dd188528.wizard(ja-jp,MSDN.10).gif

DirectDraw を使う既存のアプリケーションは、Direct3D を使って書き換える選択肢と、C# あるいは Visual Basic .NET を使って書き換える選択肢を持つことになります。実際には 2D で十分なアプリケーションは多いので、Managed DirectX は魅力的な選択肢と言えるでしょう。逆に .NET Framework ベースのアプリケーションから、グラフィックス ハードウェアを使ったフルスクリーンの 2D アニメーションや、高速なブリットやフリップが使えることも大きなメリットです。ただし機能的には以前のものと変わっていないので、ハードウェアによる半透明などのエフェクトが必要な場合は、Direct3D を使う必要があります。

VB.NET AppWizard

Dd188528.wizard1(ja-jp,MSDN.10).gif

ここでは、Visual Basic .NET 用の AppWizard を使ってみます。C# 用もまったく同じ手順ですし、生成されるコードもほとんど同じです。まず、Visual Studio .NET から、[ファイル] → [新規作成] → [プロジェクト] を選択して、[テンプレート] から [DirectX 9 Visual Basic Wizard] を選択してください。AppWizard の [Project Setting] をクリックして、[DirectDraw] ボタンをオンにしてから、[Finish] ボタンを押せば、スケルトンが生成されます。何も変更せずビルドすると次のようなウィンドウが起動します (実際には円の色は黒になるはずです)。

Dd188528.Ellipse(ja-jp,MSDN.10).gif

矢印キーで左上にある円を動かすことができます。このキー入力の処理は main.vb で行われていますが、実際の描画は ddraw.vb で処理されています。それほど長くないので、ここにリストします。協調レベルの設定や、サーフェイスの作成などはすっとばして (それらも重要なのですが)、最後の RenderGraphics を見てください。その中で、Surface クラスの DrawEllipse メソッドを呼び出して、楕円を描画しています。その直前にある FillColor プロパティの設定は、スケルトンにはありません、円に色を付けるために、後で追加したものです。これを追加すると、上の画像のように赤い楕円が描画されます。最後の Draw メソッドがセカンダリ サーフェイスからプライマリ サーフェイスへのブリット処理を行います。Draw メソッドに DrawEffect 構造体を渡してやれば、カラー キーーイングをしたり、回転させたりするエフェクトも可能になります。


<rem>
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports Microsoft.DirectX
Imports Microsoft.DirectX.DirectDraw

'/ <summary>
'/ This class is where the Drawing routines
'/ for non-Direct3D and non-DirectDraw 
'/ applications reside.
'/ </summary>
Public Class GraphicsClass
    Private owner As Control = Nothing

    Private Const spriteSize As Integer= 50
    Private localDevice As Device = Nothing
    Private localClipper As Clipper = Nothing
    Private surfacePrimary As Surface = Nothing
    Private surfaceSecondary As Surface = Nothing

    Public Sub New(ByRef owner As Control)
        Me.owner = owner

        localDevice = New Device()
        localDevice.SetCooperativeLevel(owner, CooperativeLevelFlags.Normal)
        
        CreateSurfaces()
    End Sub

    Private Sub CreateSurfaces()
        Dim desc As SurfaceDescription = New SurfaceDescription()
        Dim caps As SurfaceCaps = New SurfaceCaps() 

        localClipper = New Clipper(localDevice)
        localClipper.Window = owner

        desc.SurfaceCaps.PrimarySurface = true
        surfacePrimary = New Surface(desc, localDevice)
        surfacePrimary.Clipper = localClipper
        
        desc.Clear()
        desc.SurfaceCaps.OffScreenPlain = true
        desc.Width = surfacePrimary.SurfaceDescription.Width
        desc.Height = surfacePrimary.SurfaceDescription.Height
        
        surfaceSecondary = New Surface(desc, localDevice)            
        surfaceSecondary.FillStyle = 0
    End Sub

    Public Sub RenderGraphics(ByVal destination As Point)
        If (Not owner.Created) Then
            Return
        End If

        Dim dest As Rectangle = 
            New Rectangle(owner.PointToScreen(New Point(destination.X, destination.Y)), 
                New Size(spriteSize, spriteSize))

        If (Nothing Is surfacePrimary Or Nothing Is surfaceSecondary) Then
            Return
        End If

        Try
            surfaceSecondary.ColorFill(Color.Blue)
            surfaceSecondary.FillColor() = Color.Red
            surfaceSecondary.DrawEllipse(dest.Left, dest.Top, dest.Right, dest.Bottom)
            surfacePrimary.Draw(surfaceSecondary, DrawFlags.DoNotWait)
        Catch e As SurfaceLostException
            ' The surface can be lost if power saving
            ' mode kicks in, or any other number of
            ' reasons.
            CreateSurfaces()
        End Try
    End Sub
End Class
</rem>

SpriteAnimate サンプル コード

C# にも Visual Studio .NET にも同じ 6 つの DirectDraw サンプル コードが提供されています。デバイス列挙のサンプル EnumDrawDevices、パレット アニメーションとスプライト アニメーションのサンプル AnimatePalette と SpriteAnimate、ウィンドウ モードのサンプル Windowed と MDIWindow、そしてフルスクリーンで Windows ダイアログを使うサンプル FullScreenDialog です。ここでは、SpriteAnimate を紹介します。

以前からあるサンプルですのでご存知の方も多いと思いますが、このサンプルは次の画像 (スプライト) を使って、アニメーションを作成する典型的な 2D アニメーションの例です。左上の画像から右下の画像に替えていけば、回転しているようなアニメーション効果が得られます。スクリーン上の表示位置も移動させて、回転しながら進むアニメーションになっています。

Dd188528.animate(ja-jp,MSDN.10).gif

このサンプルでは 3 つの DirectDraw サーフェイスを使っています、1 つは上のビットマップを格納するサーフェイス、1 つは背後で描画するバックバッファ、3 つ目は表示されるフロントバッファです。3 つとも InitDirectDraw() の中で定義されています。ただしバックバッファはフロントバッファを作るときに BackBufferCount を 1 に設定して、GetAttachedSurface メソッドで作成しています。


<rem>
Private Sub InitDirectDraw()
'-----------------------------------------------------------------------------
' Name: InitDirectDraw()
' Desc: DirectDraw とすべてのサーフェイスの作成
'-----------------------------------------------------------------------------
Dim description As New SurfaceDescription() ' サーフェイスの記述
Dim caps As New SurfaceCaps()
Dim randomNext As Integer
Dim ck As New ColorKey() ' 新しいカラーキーを作成.
Dim i As Integer

draw = New Device() ' Create a new DirectDrawDevice.
draw.SetCooperativeLevel(Me, CooperativeLevelFlags.FullscreenExclusive) ' 協調レベルの設定
draw.SetDisplayMode(widthScreen, heightScreen, 16, 0, False) ' ディスプレイ モードの設定
description.SurfaceCaps.PrimarySurface = True
description.SurfaceCaps.Flip = True
description.SurfaceCaps.Complex = True
description.BackBufferCount = 1 ' バックバッファを 1 つ作る
front = New Surface(description, draw) ' フロントバッファの作成
caps.BackBuffer = True ' サーフェイスの Caps
back = front.GetAttachedSurface(caps) ' バックバッファを取得
back.ForeColor = Color.White
description.Clear() ' SurfaceDescription 構造体をクリア
surfaceAnimation = New Surface(nameFile, description, draw) ' スプライト サーフェイスの作成
surfaceAnimation.SetColorKey(ColorKeyFlags.SourceDraw, ck) ' カラーキーの設定
...
</rem>

毎フレーム、表示する位置や表示するストライプを計算してバックバッファに描画し、最後にフロントバッファにブリット (このサンプルでは Flip を使用) して表示します。この処理は DisplayFrame() で行われています。このサンプルはフルスクリーンなので、Flip を使っています。Flip を使った場合、ハードウェアがサポートしていれば、メモリ転送を行わずにポインタ切り替えのみでバックバッファとフロントバッファを切り替え、より高速に表示します。ウィンドウ モードではほとんどの場合ブリットしか機能しません。


<rem>
Private Sub DisplayFrame()
'-----------------------------------------------------------------------------
' Name: DisplayFrame()
' Desc: スプライトとテキストをスクリーンに表示
'-----------------------------------------------------------------------------
If Nothing Is front Then
    Return
End If
If False = draw.TestCooperativeLevel() Then
    needRestore = True
    Return
End If

If True = needRestore Then
    needRestore = False
    ' サーフェイスが消失していたら、再作成
    RestoreSurfaces()
End If

' バックバッファを黒で塗りつぶす
back.ColorFill(0)

' カラーキーを使ってバックバッファに全スプライトを描画
' フリップするまでエラーを無視。すべてのスプライトが同じ 
' DirectDraw サーフェイスを使っているので注意すること
Try
    Dim i As Integer
    For i = 0 To numSprites - 1
        back.DrawFast(sprite(i).posX, _
                      sprite(i).posY, _
                      surfaceAnimation, _
                      frame(sprite(i).frame), _
            DrawFastFlags.DoNotWait Or DrawFastFlags.SourceColorKey)
    Next i
    back.DrawText(10, 30, "Press escape to exit", True)

    ' フルスクリーンモードなので、フリップを行う
    front.Flip(back, FlipFlags.DoNotWait)
Catch
    End Try
End Sub 'DisplayFrame
</rem>

注意 : Managed DirectX の開発には、Visual Studio .NET と .NET framework の両方が必要です。ランタイムは Managed DirectX をインストールするので、 .NET framework がインストールされていればアプリケーションは動作します。.NET framework は 「.NET Framework ダウンロード情報」 からダウンロード可能です。ここには、開発ツールがインストールされていないマシンで、Managed DirectX アプリケーションを含む .NET アプリケーションの実行に必要な .NET Framework 再頒布モジュールが置かれています。.NET Framework FAQ は、「Microsoft .NET Framework よく寄せられる質問」 にあります。.NET Framework SP2 へのアップデートを推奨します、これは 「Microsoft .NET Framework Service Pack 2 ダウンロード」 から利用できます。