いよいよDirectDrawで挫折した2Dです。妖精のことではないので注意。
スプライトは2Dを高速描画するための仕組みです。高速かどうかはともかく、Direct3Dにはそのような機能があります。クラスの名前も「Sprite」です。しかも、ただ2Dが描画できるだけにとどまらず、0~1の間の奥行きも設定することが出来ます。とりあえず使ってみましょう。
その前に1つ。ちょっと小さめの画像が欲しいです。無い方は下のを使っても良いです。
勝手に書き換えても何しても良いです。もっと良いアイコンが出来て私のサイトで使っても良いという方がいれば一報下さい。
テクスチャは2種類使っています。「texture_」と「spriteTexture_」です。createTexureにパスがあるので書き換えて使ってください。
using System; using System.Drawing; using System.Windows.Forms; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; namespace Project2 { /// <summary> /// md3d2 の概要の説明です。 /// </summary> public class md3d2:Form { public md3d2():base() { //最小サイズを設定 this.MinimumSize=new Size(80,60); this.Size = new Size(300,300); this.Text ="Direct3D-My"; } private Device device_; private PresentParameters presentParam_; /// <summary> /// スプライトを行うためのもの /// </summary> private Sprite sprite_; /// <summary> /// Direct3Dの初期化を行います。 /// </summary> /// <returns>初期化が成功したかどうか</returns> public bool DXInitialize() { try { //プレゼンテーションパラメータを作成 presentParam_ = new PresentParameters(); //ウィンドウモード presentParam_.Windowed =true; //スワップエフェクトを設定。 presentParam_.SwapEffect = SwapEffect.Discard; //デバイスを作成 device_ = new Device(0,DeviceType.Hardware,this ,CreateFlags.HardwareVertexProcessing,presentParam_); createVertex(); createTexture(); sprite_ = new Sprite(device_); //初期化成功 return true; } catch { //初期化失敗 return false; } } /// <summary> /// 頂点バッファ /// </summary> private VertexBuffer vertexBuffer_; /// <summary> /// 頂点バッファ作成関数 /// </summary> private void createVertex() { //頂点バッファ領域を確保 vertexBuffer_ = new VertexBuffer( typeof(CustomVertex.PositionTextured),4, device_ , 0, CustomVertex.PositionTextured.Format, Pool.Managed); //頂点データの配列を作成 CustomVertex.PositionTextured[] verts = new CustomVertex.PositionTextured[4]; //頂点データ verts[0].Position =new Vector3(1, 1,0); verts[0].Tu = 0.0f; verts[0].Tv =0.0f; verts[1].Position =new Vector3(-1, 1,0); verts[1].Tu = 1.0f; verts[1].Tv =0.0f; verts[2].Position =new Vector3(1, -1,0); verts[2].Tu = 0.0f; verts[2].Tv =1.0f; verts[3].Position =new Vector3(-1, -1,0); verts[3].Tu = 1.0f; verts[3].Tv =1.0f; //バッファをロック GraphicsStream stm = vertexBuffer_.Lock(0,0,0); //頂点データをバッファに書き込み stm.Write(verts); //バッファのロックを解除 vertexBuffer_.Unlock(); } /// <summary> /// テクスチャ /// </summary> private Texture texture_; private Texture spriteTexture_; /// <summary> /// /// </summary> private void createTexture() { string openTexture = @"H:\Documents and Settings\All Users\" + @"Documents\My Pictures\Sample Pictures\" + @"Blue hills.jpg"; texture_ = TextureLoader.FromFile(device_ ,openTexture); openTexture = @"I:\temp\Atbl.png"; //ここを書き換える。絶対パスの方が扱いやすいです。 spriteTexture_ = TextureLoader.FromFile(device_ ,openTexture); } public void Render() { if(device_==null)return; if(this.WindowState ==FormWindowState.Minimized)return; device_.Transform.World = Matrix.RotationY(Environment.TickCount /1500f); device_.RenderState.CullMode = Cull.None; //カメラの設定を行う device_.Transform.View = Matrix.LookAtLH( new Vector3( 0.0f, 0.0f, 2.5f ), new Vector3( 0.0f, 0.0f, 0.0f ), new Vector3( 0.0f, 1.0f, 0.0f ) ); device_.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, (float)this.ClientSize.Width / (float)this.ClientSize.Height, 0.0f, 100.0f); device_.RenderState.Lighting=false; device_.Clear(ClearFlags.Target,Color.Blue,1.0f,0); device_.BeginScene(); device_.SetTexture(0,texture_); device_.SetStreamSource(0,vertexBuffer_,0); device_.VertexFormat = CustomVertex.PositionTextured.Format; device_.DrawPrimitives(PrimitiveType.TriangleStrip,0,2); //スプライト描画 sprite_.Begin(SpriteFlags.None); sprite_.Draw2D(spriteTexture_,new PointF(0,0) ,0,new PointF(10,10),Color.Transparent); sprite_.End(); device_.EndScene(); try { //更新 device_.Present(); } catch(DeviceLostException) { resetDevice(); } } /// <summary> /// デバイスのリセットを行う /// </summary> private void resetDevice() { int result; if(!device_.CheckCooperativeLevel(out result)) { if(result ==(int)ResultCode.DeviceLost) { //ちょっと待つ System.Threading.Thread.Sleep(10); } else if(result ==(int)ResultCode.DeviceNotReset) { device_.Reset(presentParam_); } } } } /// <summary> /// エントリクラス /// </summary> class Program { public static void Main() { using(md3d2 dxform =new md3d2()) { if(!dxform.DXInitialize()) { MessageBox.Show("Diret3Dの初期化に失敗しました。" ,"初期化の失敗"); return; } dxform.Show(); while(dxform.Created) { dxform.Render(); Application.DoEvents(); } } } } }
ここは簡単です。「Device」作成後にnewするだけです。特に設定は要らないです。
スプライトの描画は「Device.BegineScene」~「Device.EndScene」の中で行います。さらに、実際の描画命令は「Srite.Begin」~「Sprite.End」の中で行わなければいけません。
device_.BeginScene(); sprite_.Begin(SpriteFlags.None); sprite_.Draw2D(spriteTexture_,new PointF(0,0),0,new PointF(10,10),Color.Transparent); sprite_.End(); device_.EndScene(); device_.Present();
簡単に書くとこんな感じです。
引数を取ります。ここを設定するとさらに多彩なことが出来るのですが、今回はパスです。やりません。というわけでnoneです。
スプライトのメソッドをざっと眺めると、「Draw2D」と「Draw」で描画が行えそうな雰囲気です。そして実際その通りです。
これらについて説明しようと考えたのですが、このメソッドにはたくさんのオーバーロードがあります。でも、今回はその中から1つを選んで説明したいと思います。今回は画像の大きさを変えない。回転しない。という状況での描画を行います。他のオーバーロードされたメソッドについてはTipsにアップする予定です。
public void Draw2D( Texture srcTexture, //描画するテクスチャ PointF rotationCenter,//回転の中心座標 float rotationAngle, //回転角度(アングルです) PointF position, //どこに描画するかの位置。ウィンドウの左上が(0,0)の(x,y)座標です Color color //色。テクスチャに色の補正をかけることが出来る。 );
ヘルプやこのページと使う構造体が若干違う場合には、インテリセンスに従ってください(PointとPointFとか)。
各値については自分でいじって遊んでみてください。ワールド変換と同じ要領で画像を回転させるのも面白いと思います。
色についてちょっと説明すると、「Color.Transparent」または「Color.White」を使うと、元のままの色で表示されます。Clor.FromArgb()で好きな値を入れて遊んでみると感覚がつかめます。
実行すると、前回の回転するオブジェクトの横でずっと静止したままの状態で絵が書かれると思います。これこそがスプライトの威力です(ワールド変換しているのに場所が変わらない))。また、スプライトでは半透明も扱うことが出来るので2Dゲームで在ればこのスプライトで十分に作れると思います。スプライトの半透明はTipsか、後に扱うと思います。
とりあえず今回までで、ある程度の基礎は出来るようになってきたかと思います。次回は、文字を描画することにしましょう。