2007-5-23 第一次亲密接触
这几天在网上看了一个系列文章的开头两章,按指示作出了一个hello world似的程序。
网址:(中文)
(英文) http://blogs.msdn.com/coding4fun/archive/2006/11/02/938703.aspx
还是像以前看到的那样,利用 OnPaint 来实现游戏循环。并且通过代码确保 Windows 只使用 OnPaint 事件处理程序来
重画屏幕:
this.SetStyle ( ControlStyles.AllPaintingInWmPaint|ControlStyles.Opaque, true );
在第二篇中,终于接触到了DirectX,搞了十多天,终于有点起步了。
对于DirectX而言,将图形硬件(适配器 Adapter)抽象成设备(Device),这样程序就可以通过Device来操作,而不论实际
的显示设备是什么。DirectX 支持三种类型的设备:Hardware、References 和 Software。
对于游戏程序而言,需要在任何时候都有一个可用的设备,因此,在所有代码之前就应该准备好Device对象。而如下的
代码则实现该目标:
int adapterOrdinal = Manager.Adapters.Default.Adapter;
// Get our device capabilities so we can check them to set up the CreateFlags
Caps caps = Manager.GetDeviceCaps(adapterOrdinal, DeviceType.Hardware);
CreateFlags createFlags;
// Check the capabilities of the graphcis card is capable of
// performing the vertex-processing operations
// The HardwareVertexProcessing choice is the best
if (caps.DeviceCaps.SupportsHardwareTransformAndLight)
... {
createFlags = CreateFlags.HardwareVertexProcessing;
}
else
... {
createFlags = CreateFlags.SoftwareVertexProcessing;
}
// If the graphics card supports vertex processing check if the device can
// do rasterization, matrix transformations, and lighting and shading operations
// This combination provides the fastest game experience
if (caps.DeviceCaps.SupportsPureDevice && createFlags == CreateFlags.HardwareVertexProcessing)
... {
createFlags |= CreateFlags.PureDevice;
}
// Set up the PresentParameters which determine how the device behaves
PresentParameters presentParams = new PresentParameters();
presentParams.SwapEffect = SwapEffect.Discard;
// Make sure we are in windowed mode when we are debugging
#if DEBUG
presentParams.Windowed = true ;
#endif
// Now create the device
device = new Device(adapterOrdinal, DeviceType.Hardware, this , createFlags, presentParams);
如同注释所述,该代码分成四部分。
1. 第一行代码只是获取默认适配器的名称(通常为 0)。与赌它为零不同,更安全的做法是使用 Manager 类来获取默
认适配器的名称。如果由于某种原因使得默认适配器的实际名称为 2,则采用这种方法不会出现问题。
2. 接下来的一节代码用于确定传递给 Device 构造函数的 CreateFlags 枚举的设置,以及哪个设置控制设备创建后的
行为。我们再次使用 Manager 来获取默认适配器的功能(简称为 Caps)列表。然后使用此功能列表来确定是在硬件中执行
顶点处理(较快),还是在软件中执行(较慢,但保证始终工作)。这其实用词不当,因为 SoftwareVertexProcessing 实
际指使用 CPU 而 HardwareVertexProcessing 指使用 GPU。然后执行另一个检查,查看适配器是否支持纯设备,即图形卡可
以处理光栅化、矩阵转换以及打光和阴影计算。如果设备可以,而且前一个检查确定我们可以使用硬件顶点处理,则将
PureDevice 设置添加到 CreateFlags 枚举中。HardwareVertexProcessing 和 PureDevice 的组合为我们提供可能的最佳性
能,所以如果可能,要尽量使用它。
3. 创建设备需要的最后一个参数是 PresentParameters 对象。这个对象确定设备向屏幕显示数据的方式,因此得名。
首先我们设置 SwapEffect 枚举,它确定缓冲和设备如何相互联系。我们通过选择 Discard 选项来简单地选择放弃后台缓冲
区,直接写入到前台缓冲区。在 If 语句中,我们确定应用程序是否在调试模式下运行。如果处于调试模式,我们不希望在
全屏模式下运行(这是默认情况),因为它使调试非常困难。使用这种方法确定配置好于对其硬编码而在发布游戏时忘了切
换回来。
4. 最后一步是真正创建设备。我们传入默认适配器的序号、想要将设备绑定到的窗口、设备类型,然后传入前面创建的
CreateFlags 和 PresentParameters 对象。
这段代码的实际结果就是使我们拥有一个有效的设备,可以用它在屏幕上进行绘制。
OK,终于可以在程序中使用DirectX了,总算有点兴奋的感觉了。
最后完成的代码如下:
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Samples.DirectX.UtilityToolkit;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
namespace BattleTank2005
... {
public class GameEngine : Form
...{
private double deltaTime;
private Device device;
private const DeviceType _deviceType = DeviceType.Reference;
/**//// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/**//// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
...{
if (disposing && (components != null))
...{
components.Dispose();
}
base.Dispose(disposing);
}
Windows Form Designer generated code#region Windows Form Designer generated code
/**//// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
...{
this.SuspendLayout();
//
// GameEngine
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 266);
this.Name = "GameEngine";
this.Text = "GameEngine";
this.ResumeLayout(false);
}
#endregion
public GameEngine()
...{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
// Get the ordinal for the default adapter
int adapterOrdinal = Manager.Adapters.Default.Adapter;
// Get our device capabilities so we can check them to set up the CreateFlags
Caps caps = Manager.GetDeviceCaps(adapterOrdinal, _deviceType);
CreateFlags createFlags;
// Check the capabilities of the graphcis card is capable of
// performing the vertex-processing operations
// The HardwareVertexProcessing choice is the best
if (caps.DeviceCaps.SupportsHardwareTransformAndLight)
...{
createFlags = CreateFlags.HardwareVertexProcessing;
}
else
...{
createFlags = CreateFlags.SoftwareVertexProcessing;
}
// If the graphics card supports vertex processing check if the device can
// do rasterization, matrix transformations, and lighting and shading operations
// This combination provides the fastest game experience
if (caps.DeviceCaps.SupportsPureDevice && createFlags == CreateFlags.HardwareVertexProcessing)
...{
createFlags |= CreateFlags.PureDevice;
}
// Set up the PresentParameters which determine how the device behaves
PresentParameters presentParams = new PresentParameters();
presentParams.SwapEffect = SwapEffect.Discard;
// Make sure we are in windowed mode when we are debugging
#if DEBUG
presentParams.Windowed = true;
#endif
// Now create the device
device = new Device(
adapterOrdinal,
_deviceType,
this,
createFlags,
presentParams
);
}
protected override void OnPaint(PaintEventArgs e)
...{
deltaTime = FrameworkTimer.GetElapsedTime();
this.Text = string.Format("The framerate is {0}", FrameRate.CalculateFrameRate());
FrameworkTimer.Start();
device.Clear(ClearFlags.Target, Color.DarkBlue, 1.0f, 0); device.Present();
this.Invalidate();
}
}
public class FrameRate
...{
public static int CalculateFrameRate()
...{
if (System.Environment.TickCount - lastTick >= 1000)
...{
lastFrameRate = frameRate;
frameRate = 0;
lastTick = System.Environment.TickCount;
}
frameRate++;
return lastFrameRate;
}
private static int lastTick;
private static int lastFrameRate;
private static int frameRate;
}
}