纯备忘,刚刚好找到的例子
将一张图片转呀转,WSAD可以移动,ESC退出
用到的程序集
System
System.Data
System.Drawing
System.Windows.Forms
Microsoft.DirectX
Microsoft.DirectX.Direct3D
Microsoft.DirectX.Direct3DX
Microsoft.DirectX.DirectInput
一张显示小球的图片 my.bmp,放在运行exe相同目录:Game2D\bin\Debug
Game.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
namespace Game2D
{
class Game : Form
{
///代表了你的显示卡,也代表了你的绘图空间。
Microsoft.DirectX.Direct3D.Device d3dev = null;
///DX9 Graphics的灵魂所在,它提供了一系列的运算和绘画功能,让你可以轻松的把想呈现在荧幕上的画面画出来
Sprite sprite;
///就是一般3D游戏中贴在3D模型上的材质,在这里,它成了你的物件的图像的寄身所在。
///在DX9 Graphics里面,每一个Sprite事实上是一个平面的正方形polygon,
///其normal永远和z-轴平行(就像以前Doom 2 时代的 Billboard object 一般永远对着镜头。
///只在这里,我们的镜头也是永远不变的跟z-轴平行着的),
///其画像就是贴在上面的Texture了。
Texture myTexture;
///从起源点(Origin, X=0, Y=0, Z=0),发射向某个点(X=nx, Y=ny, Z=nz)的假想线
/// 在2D 环境下,我们可以把它当作一个点(point),因为我们只对其X和Y值有兴趣(Z值无视),
/// 我们有两个Vector3,pos和ctr。
/// pos是用来代表我们的Sprite在这个2D空间的位置,
/// ctr则是Sprite的中心点。为何要使用中心点呢?那是因为,
/// 如果你要使用Sprite的Origin(X=0, Y=0)来代表它的位置的话,
/// 你得同时keep track 它的宽和高,十分麻烦,而使用中心点的话,你只需注意一个point,方便很多。
/// pos被设定为 X=400,Y=300,正处在画面中间(我们的画面,也就是Direct3D.Device.Viewport, 会被设定为 800x600)
Vector3 ctr;
Vector3 pos = new Vector3(400.0f, 300.0f, 0.0f);
float angle = 0.0f;
/// 用来进行transfrmation的,例如旋转(rotation),放大缩小(scaling),以及转移位置(translation)。
/// Sprite 的Transformation功能有个Bug, 导致任何Sprite.Rotation 的运算都会以viewport的(0,0)作为中心点,
/// 而忽略了你所选择的中心点(例如 X=400, Y=300),所以我们不能使用Sprite的回旋功能。
/// 其解决方法就是使用两个transformation,先 rotate 再 translate 到你想要的位置。
Matrix transformmatrix, rotatematrix, finalmatrix;
//--input---
Microsoft.DirectX.DirectInput.Device kb;
public Game()
{
this.ClientSize = new Size(800, 600);
this.Text = "The Game - Managed DirectX";
}
/// 启动和初始化Direct3D
public bool InitD3D()
{
try
{
//PresentParameters (发音 Pri-sent,“呈现”的意思)是用来决定Direct3D如何描绘画面的设定的structure
PresentParameters pp = new PresentParameters();
//pp.Windowed决定你的游戏是Full Screen还是Windowed。
//要注意的是,如果你把Windowed设定为true,所有关于backbuffer的设定将会被无视,
//而viewport size和color depth将会跟随现有windows的Client Size和deskto color depth
// 全屏时,不知道是不是因为我的是宽屏,出错了,改成窗口显示
pp.Windowed = true;
pp.BackBufferWidth = 800;
pp.BackBufferHeight = 600;
pp.SwapEffect = SwapEffect.Discard;
//BackBuffer Format采用Alpha8, Red8, Green8, Blue8,表示Sprite可以有256阶段的透明度(8-bit Alpha)。
//如果你的BackBuffer Format选择了错误的设定,会导致你的D3D初始化失败的,要注意。
pp.BackBufferFormat = Format.A8R8G8B8;
//设定好PresentParameters 后就可以制造一个D3D Device了
d3dev = new Microsoft.DirectX.Direct3D.Device(0, //adapter id, 0 for default, 也就是你的主显示卡
Microsoft.DirectX.Direct3D.DeviceType.Hardware,//如果你的显示卡友支援完整的DX9功能,你可以使用硬体加速,不然就只能使用软件模拟(很慢的)
this, //这个游戏的视窗本身
CreateFlags.SoftwareVertexProcessing, //不支援Shader的显示卡只能使用软件模拟的VP了
pp //PresentParameter
);
//接下来是制造Sprite和Texture物件,由于它们都会用到D3D Device物件,所以必须在制造了D3D Device 才能制造这两个物件。
sprite = new Sprite(d3dev);
//在制造Texture物件前,你得先把一个图画加入在project里面,作为Texture的画像
//我个人喜欢使用PNG格式,因为它支援透明背景,同时也不会像BMP般占大量空间,
//却也不会像JPG般压缩了之后会出现边缘模糊的情形。记着你的图形最好是正方形,
//而且尺寸必须是2的倍数(2,4,8,16,32,64,128,256。。。。),
//不然D3D会把你的图画缩小到最近似的2的倍数,例如你的图画是300x300的话,
//会被缩小成256x256。而在某些function中,不是2的倍数的texture尺寸会导致function fail。
myTexture = TextureLoader.FromFile(d3dev, "my.bmp");
//通过一个Bitmap object来获取texture图像的尺寸(奇怪的是我无法通过Texture object来获得这些讯息,
//有知道如何直接从Texture object获得图像来源的尺寸的高手请指教),一边计算出Texture的中心点。
System.Drawing.Image bmp = System.Drawing.Image.FromFile("my.bmp");
ctr = new Vector3(bmp.Width / 2, bmp.Height / 2, 0.0f);
//最后,我们制造一个键盘物件,并通过SetCooperativeLevel设定其运作个性(Behavior),
//以避免当玩家按 Alt-Tab 跳回桌面时,游戏仍然死抓着键盘控制权不放,造成操作上的不便。
kb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
kb.SetCooperativeLevel(this,
CooperativeLevelFlags.NonExclusive |
CooperativeLevelFlags.Background);
//Acquire() 是确定键盘处在准备妥当的状态(没有别的program正在抓着键盘控制权)。
kb.Acquire();
return true;
}
catch (DirectXException)
{
return false;
}
}
/// 以上Function我只检测键盘的5个键:WASD 移动物件(Sprite)的 pos,Escape键结束游戏。
/// 要注意的是,UpdateGame 里面就只纯粹更新状态,
/// 别把描绘的工作掺杂在里面,以免你无法迅速的完成工作而导致游戏出现控制反应迟缓的现象。
internal void UpdateGame()
{
KeyboardState ks = kb.GetCurrentKeyboardState();
if (ks[Key.Escape])
{
this.Close();
return;
}
if (ks[Key.A])
{
pos.X -= 5;
if (pos.X < 0)
pos.X = 0;
}
if (ks[Key.D])
{
pos.X += 5;
if (pos.X > d3dev.Viewport.Width)
pos.X = d3dev.Viewport.Width;
}
if (ks[Key.W])
{
pos.Y -= 5;
if (pos.Y < 0)
pos.Y = 0;
}
if (ks[Key.S])
{
pos.Y += 5;
if (pos.Y > d3dev.Viewport.Height)
pos.Y = d3dev.Viewport.Height;
}
//图片旋转的角度
angle += 0.1f;
if (angle >= 360)
{
angle = 0;
}
Render();
}
/// 最后,把游戏状态描绘成画面,然后呈现在荧幕
public void Render()
{
//首先清空画面,填上Color.xxxx 的颜色
d3dev.Clear(ClearFlags.Target, Color.SkyBlue, 1.0f, 0);
//然后就启动D3D Device的描绘机制,当你呼叫Device.Begin()时,
//系统就会自动的锁定你的Frame Buffer(显示卡里面属于你的游戏的记忆空间),
//以免被其它程式,或你的游戏里的其他Thread (如果你采用 Multi Threaded 设计)干扰
d3dev.BeginScene();
//Sprite本身也采用类似的locking来锁定自己的Texture Buffer。
//SpriteFlags.AlphBlend是告诉显示卡,我们要使用Alph channel, 来制造出透明效果
sprite.Begin(SpriteFlags.AlphaBlend);
//由于Sprite的Bug,我们不能使用它内建的Rotation功能,所以我们使用了两个Matrix,
//把它们Multiply起来,再直接把结果set在Sprite.Transformation,
//Sprite 就会乖乖的完成你指定的变形(以 X=400,Y=300 为中心点,绕着自己的中心缓缓的旋转)。
rotatematrix.RotateZ(angle);
transformmatrix.Translate(pos);
finalmatrix = Matrix.Multiply(rotatematrix, transformmatrix);
sprite.Transform = finalmatrix;
sprite.Draw(myTexture, Rectangle.Empty, ctr, Vector3.Empty, Color.White);
//指令告诉显示卡,你已经完成工作,可以解开Frame Buffer的锁定
sprite.End();
d3dev.EndScene();
//Flip Buffer, 因为这游戏是 Double-buffered 的,也是说它有两个Frame Buffer,
//一个在前也就是显示在荧幕上的Front Buffer, 另一个是在荧幕显示之外的 Back Buffer。
//你的一切更新都是在 Back Buffer 里进行的,当完成更新之后,Present() 就会把两个Buffer调换,
//荧幕上就会显示出最新的画面,而你就继续将已经过时了的画面清掉,描绘下一个画面。
//这么做得好处是你不必等待荧幕刷新时才能进行描绘,而且也能避免画面撕扯(tearing)的现象。
d3dev.Present();
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace Game2D
{
class Program
{
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (Game MainForm = new Game())
{
if (!MainForm.InitD3D())
{
MessageBox.Show("Failed to initialize Direct 3D Device");
return;
}
MainForm.Show();
while (MainForm.Created)
{
MainForm.UpdateGame();
Application.DoEvents();
}
}
//Application.Exit();
}
}
}