DirectGraphicsの機能
DirectXの機能の中枢と言ってもいいかもしれませんね。(ゲーム製作者的にはですが)DirectGraphicsという呼び名はDirectX8以降からのもので、それ以前は2D描画用のDirectDrawと3D描画用のDirect3Dに分かれていましたが、3Dでも2Dが描画できてしまうのと、3Dの機能を使ったほうが綺麗に描画できることからGraphicsという形で統合され主に3D描画専門の機能となりました。DirectGraphicsを使えば、パソコンについているグラフィックカードの性能をフルに引き出し、GDI+とは比べ物にならないほど高速な描画が可能になります。また、3Dエンジンを利用するため半透明や光の効果など2D描画でも非常に綺麗なグラフィックを表現できます。もちろん、市販レベルの3Dゲームだって作れます。(ってか、市販のほとんどはこれを使ってますが)
Managed DrectXでは実はDirectDrawとDirect3Dとに分かれています。DirectDrawは7以前の簡単な2D高速描画用の機能。Direct3Dは通常のDirectGraphicsと同等の機能を持ったものとなっています。上にも書いたとおり、2Dをやるにしろ3Dをやるにしろ、Direct3Dを使ったほうが格段に綺麗なグラフィックを表現できるので、この講座では主にDirect3Dの方を取り扱うことにします。
Direct3Dの描画の流れ
Direct3Dでは、ハードウェアの機能を使うためか、ダブルバッファをするためかは知りませんが、独特の描画手順があります。イメージとしては、キャンバスに色々な機能を使って絵を描いて、描きあがった絵を最後に画面にお披露目する感じです。
このあと、コードを追いながら詳しく説明していきますが、まずは簡単に概要を。描画手順的には上の図のようになります。@BeginSceneメソッドを呼び出してこれから描画をすることをコンピュータ側に使えます。そうしたらA実際に描画を行います。描画を行う先はメモリ上(環境にもよりもますが、メインメモリにあったりVRAMにあったり)に作られたバックバッファに図形を描画していきます。描画が終了したらBEndSceneメソッドを呼び出して描画が終了したことを伝え、CPresentメソッドでバックバッファに完成した画面をモニタの実際の画面へと転送します。この手順をひたすら繰り返します。描画する先が画面に直接ではなく、メモリ上に作ったバックバッファであることはしっかりと意識して置いてください。
今回のサンプルプログラム
それでは、上の手順を実際にプログラムしていきます。Aの実際の描画は難しいので今回は画面を一色に塗りつぶす動作のみを行います。また、今後Direct3Dでゲームを作っていく都合上、描画用のクラスを作りDirect3D系の命令は全てそこに書き込んでいくことにします。
Direct3DSample1今回も画面だけ見ると全くありがたくないですが、一応Direct3Dを使って描画しています。あまりにも動き的に面白くないので、画面の色が徐々に変化していくようにしました。 これも本当に簡単に作ることが出来ます。DirectXを使ったプログラムは難しいと言われてきましたが、それは昔の話。今でも多少面倒ではありますが、そのリスクと性能を考えたら使わない手はないでしょう。
|
プロジェクトは前回同様通常のWindowsアプリで生成し、参照設定に「Microsoft.DirectX」と「Microsoft.DirectX.Direct3D」を追加してください。もちろん、ネームスペースも追加してくださいね。(CLKsDG.cs内にですよ)
グラフィック担当のCLKsDGクラス
サンプルの段階でこういうことはあまりしたくないのですが(説明が面倒になるので)、実際にゲームを作っていこうとすると描画形の命令は各メソッドにまとめておかないととても作ってられません。せっかく描画系の命令群を作るので、それをクラスとしてまとめておくのは悪くないでしょう。一度クラスを作ってしまえば、あとからいくらでも使いまわせますしね。
C#での新規クラスの作り方ですが…「ソリューションエクスプローラ」のプロジェクト名(今回はdgsample1)を右クリックして、「追加」-「新しい項目の追加」をクリックし、「クラス」を選択、ファイル名に「CLKsDG.cs」と入力してOKを押せばCLKsDGというクラスの大枠を作ってくれます。(そのうちC#基本講座でも取り上げよう)新しいクラスが出来たら、早速コードを書き込んでいきましょう。
Direct3Dの初期化(コンストラクタの作成)
DirectInputのときもやりましたが、DirectX系のプログラムを作るときには必ずデバイスクラスを生成しなくてはなりません。Direct3Dの場合はそのデバイスの生成前に多少設定が必要だったり、デバイス作った後もそのデバイスの設定をする必要があります。毎回同じような文を書くことになりますので、いっそのことCLKsDGクラスのコンストラクタに初期化系の処理をまとめて書いておき、CLKsDGインスタンスを作ったときにDirect3Dデバイスも初期化できるようにしてしまえば楽チンですね。
private Microsoft.DirectX.Direct3D.Device device = null; //コンストラクタ public CLKsDG(System.Windows.Forms.Control wind) { Microsoft.DirectX.Direct3D.PresentParameters param = new PresentParameters(); param.Windowed = true; param.SwapEffect = SwapEffect.Discard; param.PresentationInterval = PresentInterval.Immediate; device = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware,wind, CreateFlags.SoftwareVertexProcessing, param); } |
コンストラクタのコードは上のようになります。「Microsoft.DirectX.Direct3D.Device device」がDirect3Dの描画をつかさどるデバイスオブジェクトです。こいつはクラス内のあらゆるところからアクセスするのでインスタンス変数で宣言しておきます。コンストラクタの引数には「System.Windows.Forms.Control」オブジェクトを渡すようになっています。ここにはDirect3Dの描画先のコントロールを渡すようにします。通常はFormやらPictureBoxが渡されるのでしょう。
デバイスを生成する前に、生成時に渡す設定用のオブジェクトをつくり、事前に設定しておかなくてはなりません。「Microsoft.DirectX.Direct3D.PresentParameter」型のオブジェクトにその設定をしておきます。「param.Windowed = true;」はDirect3Dをウィンドウモードで起動することを設定します。あとの二つ「param.SwapEffect = SwapEffect.Discard;」と「param.PresentationInterval = PresentInterval.Immediate;」は…ごめんなさい。ここでは説明を控えさせてください…もう少し高度な話題になったときに詳しく触れましょう。(そのうち…)取り合えず、書いておきましょう。
paramオブジェクトに設定が完了したら、デバイスを生成します。普通にnewで作ります。コンストラクタには色々と引数を渡さなくてはいけないのですが、上のコードどおりに書いて置いてください。ここも結構高度だったり、今はサポートされてなかったりと厄介なので。簡単に見てみますと、第3引数には「wind」つまり描画先のコントロールオブジェクトを渡しています。こうするとdeviceはPresentしたときにこのコントロールの描画エリアに描画するようになります。第5引数には先ほど設定したparamオブジェクトを渡していますね。あとは、気にしないでください…
これでDirect3Dデバイスの初期化処理は最低限完了です。実際には3D描画したり、半透明処理をしたりするために色々と設定しないといけないのですが、それらの設定はその都度紹介することにします。
Direct7時代にDirect3Dを使おうと思うと、まず長ったらしいDirectDrawの初期化をし、バックバッファを自分で作って、メモリアドレスを設定して、ようやくDirect3Dの初期化をして〜〜〜〜と凄く面倒な処理をしなくてはいけなかったのですが、Managedを使えばたったこれだけで全てやってくれます。バックバッファも勝手に作って、deviceオブジェクトを介してアクセスできます。ホントに便利になりましたね…
残りのBeginScene〜Presentまで
DirectDraw時代の講座を書いていたときはここまでくるのに2回以上かかっていたのですが、Managedはもう画面に描画できるわけです。(まぁ、3Dを使う分実際の描画が難しいのですが)残りの手順のメソッドもさっさと書いてしまいましょう。
// 描画領域を指定した色で消去する public void Clear(System.Drawing.Color color) { device.Clear(ClearFlags.Target , color ,1.0f,0); } /// 描画を開始する public void Begin() { device.BeginScene(); } /// 描画を終了する public void End() { device.EndScene(); } /// ウィンドウにシーンを描画する public void Present() { if (device == null) return; try { device.Present(); } catch { } } |
残りは全てそのままの名前のメソッドが存在しています。それを呼んでいるだけです。描画開始を示す「device.BeginScene()」は「Begin()」メソッドに、「device.EndScene()」は「End」メソッドに、画面に表示する「device.Present()」は「Present」メソッドに書いてあります。わざわざこんなことしなくてもと思いますが、これがいわゆるカプセル化というやつです。deviceのアクセス範囲がprivateですしね。
さて、描画に使うメソッドですが、Direct3Dに画面全体を塗りつぶすなんて命令はないので(ってか、図形描画の命令は一つもありませんが)、バックバッファを消去するためのdevice.Clearメソッドを使って画面全体を塗りつぶしましょう。引数には「System.Drawing.Color color」構造体を渡して色指定をできるようにしてあります。device.Clearには色々と使い方があるのですが、今回は単にバックバッファを塗りつぶしているだけなのでこのまま書いておいてください。
CLKsDGを使って実際に描画してみる
これでCLKsDGクラスに最低限描画が出来る機能をもたせることが出来ました。あとはこのクラスを使って実際に描画してみるだけですね。今回もMainメソッド内にFPS安定型のループを作ってそこの中で描画しています。
static void Main() { //メインループ //講座で紹介したFPS安定タイマーを使用しています。 Form1 frm = new Form1(); double nextframe = (double)System.Environment.TickCount; float wait = 1000f/60f; frm.Show(); //フォームの表示 frm.ClientSize = new Size(800,600); CLKsDG dgdevice = new CLKsDG(frm); while(frm.Created) { if ((double)System.Environment.TickCount >= nextframe) { if ((double)System.Environment.TickCount < nextframe + wait) { //描画処理 dgdevice.Begin(); int r = (int)(130 + 125.0*Math.Sin( (double)System.Environment.TickCount/1000)); int g = (int)(130 + 125.0*Math.Sin( (double)System.Environment.TickCount/900)); int b = (int)(130 + 125.0*Math.Sin( (double)System.Environment.TickCount/1100)); dgdevice.Clear(Color.FromArgb(r,g,b)); dgdevice.End(); dgdevice.Present(); } nextframe += wait; } Application.DoEvents(); } } |
前回(DirectInput@)同様に、ほとんどがループ構造です。フォームを表示した後に、ClientSizeを変更しています。これは描画領域を800×600にプログラム側からしていしているもので、ゲームを作る際にきっと必要になるものでしょう。デザイナで変更するのも面倒なので。サイズを変更したら、先ほど作ったCLKsDGインスタンスを生成しています。frmを渡しているので、frmに描画されるはずですね。
あとは描画処理です。@描画開始のために「dgdevice.Begin();」を呼び出し、A「dgdevice.Clear(Color.FromArgb(r,g,b));」で描画(その前にちょっと色の計算をしてますが)、B「dgdevice.End();」描画終了を伝えてC「dgdevice.Present();」実際に画面へと転送しています。流れのとおり簡単です。これだけ短いと、わざわざクラスを作ったのがバカらしくなるくらいです。
色(Color構造体)について
さて、すっ飛ばしましたが色関係の説明も簡単にしておきます。本来DirectXには様々なカラーフォーマットがあったのですが、Managedでは.NET標準の「System.Drawing.Color」構造体で大体の色指定が可能です。この構造体(構造体についても、そのうちC#基本事項で取り上げます)は非常に便利なもので、システムカラーのプロパティを大体持っていたり(実は構造を良く知りません)、色を生成するメソッドを持っています。今回は「Color.FromArgb(r,g,b)」メソッドを使って任意の色を作り出しています。このメソッドは、R(赤)・G(緑)・B(青)のレベルを0〜255の256段階で設定することで様々な色のColor構造体を生成してくれます。RGBカラーについては他のWebサイトで調べてみてください。また、オーバーロードとして透明度Aも設定でき、アルファ値(透明度)つきの色を扱うことが出来ます。今回は完全不透明なので引数3つで十分ですが。色指定は描画の中でも良く使う方法だと思いますので、覚えておくといいでしょう。
まとめ
なんとも、たったこれだけで画面に描画できてしまいました。世の中便利になりましたね。とはいえ、画面を一色に塗っただけでは全くゲームが成立しません。(前回のDirectInputのサンプルのほうがまだゲームっぽいか)次回は、3Dの機能を使って(まぁ、3Dではありませんが)画面に四角形を描画してみましょう。頂点の処理などちょっと面倒ですが、まぁManagedなんで簡単です。