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 アプリケーションのスケルトン作成も可能になっています。
DirectDraw を使う既存のアプリケーションは、Direct3D を使って書き換える選択肢と、C# あるいは Visual Basic .NET を使って書き換える選択肢を持つことになります。実際には 2D で十分なアプリケーションは多いので、Managed DirectX は魅力的な選択肢と言えるでしょう。逆に .NET Framework ベースのアプリケーションから、グラフィックス ハードウェアを使ったフルスクリーンの 2D アニメーションや、高速なブリットやフリップが使えることも大きなメリットです。ただし機能的には以前のものと変わっていないので、ハードウェアによる半透明などのエフェクトが必要な場合は、Direct3D を使う必要があります。
VB.NET AppWizard
ここでは、Visual Basic .NET 用の AppWizard を使ってみます。C# 用もまったく同じ手順ですし、生成されるコードもほとんど同じです。まず、Visual Studio .NET から、[ファイル] → [新規作成] → [プロジェクト] を選択して、[テンプレート] から [DirectX 9 Visual Basic Wizard] を選択してください。AppWizard の [Project Setting] をクリックして、[DirectDraw] ボタンをオンにしてから、[Finish] ボタンを押せば、スケルトンが生成されます。何も変更せずビルドすると次のようなウィンドウが起動します (実際には円の色は黒になるはずです)。
矢印キーで左上にある円を動かすことができます。このキー入力の処理は 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 アニメーションの例です。左上の画像から右下の画像に替えていけば、回転しているようなアニメーション効果が得られます。スクリーン上の表示位置も移動させて、回転しながら進むアニメーションになっています。
このサンプルでは 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 ダウンロード」 から利用できます。