Visual C#编写3D游戏框架示例
列举所有设备选项
现在你可以让框架组件开始列举系统中的设备了。首先,为游戏引擎类声明一个构造函数,并把main中建立的示例框架实例作为参数传递进去。如列表3所示。
列表3:添加构造函数
private Framework sampleFramework = null; // 示例的框架组件 /// 建立该类的一个新的实例 public GameEngine(Framework f) { // 存储框架组件 sampleFramework = f; }
该构造函数除了存储示例框架实例之外没有做其它的任何操作,这是因为这个实例是游戏中其它的一切东西几乎都需要使用的。在你调用示例框架之后,它所做的第一件事情是试图列举系统中的所有设备。在你的项目文件中,你在Framework文件夹中可以看到dxmutenum.cs文件。这个文件包含了列举系统中所有设备所需要的全部代码。由于理解如何列举和为什么列举设备是非常重要的,所以请你打开这个文件。
你首先应该注意到Enumeration类自身是不能被创建的,并且每个可用的成员和方法都是用static(静态的)关键字声明的。由于在正常情况下(最少是现在)应用程序在运行的时候,你的图形硬件是不会改变的,因此这些列举代码只需要在应用程序开头运行一次。
列举工作是从Enumerate方法开始的,该方法在设备建立之前被示例框架调用。请注意,这个方法的唯一参数是你自己在游戏引擎类中所实现的接口。这个接口被保存下来,因为随后,随着设备组合的列举,会调用IsDeviceAcceptable方法来决定某个设备是否应该添加到有效设备列表中。
那么设备到底是怎样列举出来的呢?这些功能都位于受控DirectX的Manager类中。如果你非常熟悉非受控的DirectX应用程序编程接口(API),那么我告诉你,这个类映射了IDirect3D9组件对象模型(COM)接口。请留意列表4中的Enumerate方法中的第一个循环。
列表4:列举设备
// 查找系统中的每个适配器
for each(AdapterInformation ai in Manager.Adapters)
...{
EnumAdapterInformation adapterInfo = new EnumAdapterInformation();
// 存储一些信息
adapterInfo.AdapterOrdinal = (uint)ai.Adapter; // 序号
adapterInfo.AdapterInformation = ai.Information; // 信息
![]()
// 获取这个适配器上的所有显示模式
// 建立一个所有显示适配器格式的临时列表
adapterFormatList.Clear();
![]()
// 现在检测支持哪种格式
for(int i = 0; i < allowedFormats.Length; i++)
...{
// 检查这种格式的每一种可支持的显示模式
for each(DisplayMode dm in ai.SupportedDisplayModes[allowedFormats[i]])
...{
if ( (dm.Width < minimumWidth) ||
(dm.Height < minimumHeight) ||
(dm.Width > maximumWidth) ||
(dm.Height > maximumHeight) ||
(dm.RefreshRate < minimumRefresh) ||
(dm.RefreshRate > maximumRefresh) )
...{
continue; // 这种格式是无效的
}
![]()
// 添加到列表中
adapterInfo.displayModeList.Add(dm);
![]()
// 如果先前并不存在就把它添加到格式列表中
if (!adapterFormatList.Contains(dm.Format))
...{
adapterFormatList.Add(dm.Format);
}
}
}
![]()
// 获取适配器显示模式
DisplayMode currentAdapterMode = ai.CurrentDisplayMode;
// 检查这种格式是否在列表中
if (!adapterFormatList.Contains(currentAdapterMode.Format))
...{
adapterFormatList.Add(currentAdapterMode.Format);
}
![]()
// 对显示模式列表进行排序
adapterInfo.displayModeList.Sort(sorter);
![]()
// 获取这个适配器上每个设备的信息
EnumerateDevices(adapterInfo, adapterFormatList);
![]()
// 如果适配器上至少有一个设备,并且它是兼容的,就把它添加到列表中
if (adapterInfo.deviceInfoList.Count > 0)
...{
adapterInformationList.Add(adapterInfo);
}
}
Manager类的Adapters(适配器)属性是一个包含了系统中所有"适配器"信息的集合。"适配器"这个术语可能有点不恰当,但是它的基本定义是指任何监视器可以连接到的东西。例如,假设你有一块ATI Radeon 9800 XT显卡。虽然只有一块显卡,但是可能把两个不同的监视器连接到它上面(通过视频图形适配器[VGA]端口和后面的数字视觉接口[DVI]端口)。当用这两种监视器的时候,这块显卡就有两个适配器,因此是两种设备。这是一种通过把设备创建为适配器组的方式在"不同的"设备之间共享资源的方法。这种方法受到了少许限制。你可以查阅DirectX文档了解更多的信息。
这个循环至少会迭代一次,这依赖于你的系统。在把当前活动的适配器的基本信息存储起来以后,代码必须找到在全屏模式下这个适配器可以支持的所有显示模式。你可能发现了受到支持的模式都可以直接从当前正在列举的适配器信息中直接列举出来,代码也是这样做的。
列举某个适配器模式的时候,第一步是检查最小和最大的范围集合。大多数设备支持很多模式,但是其中很多我们现在不会使用了。很多年前,你可能见过在320x200全屏窗口中运行游戏,但是现在不会发生这种情况(除非你正好在玩手持式游戏,例如Gameboy Advance)。示例框架选择的最小的大小为640x480窗体,没有设置最大的尺寸。
示例框架选择的最小尺寸为640x480并不意味着在全屏模式下它就会选择最小的尺寸。在全屏模式下,示例框架选择最好的可用尺寸,它一般是当前桌面的大小(通常不是640x480的)。
在符合框架组件需求的受到支持的模式被添加到列表中后,当前的显示模式就会被添加进来,因为这个模式肯定受到支持。最后,通过实现IComparer接口,这些模式会被排序。见列表5。
列表5:对显示模式进行排序
public class DisplayModeSorter : IComparer
...{
/**//// 比较两种显示模式
public int Compare(object x, object y)
...{
DisplayMode d1 = (DisplayMode)x;
DisplayMode d2 = (DisplayMode)y;
![]()
if (d1.Width > d2.Width)
return +1;
if (d1.Width < d2.Width)
return -1;
if (d1.Height > d2.Height)
return +1;
if (d1.Height < d2.Height)
return -1;
if (d1.Format > d2.Format)
return +1;
if (d1.Format < d2.Format)
return -1;
if (d1.RefreshRate > d2.RefreshRate)
return +1;
if (d1.RefreshRate < d2.RefreshRate)
return -1;
![]()
// 它们一定相同,所以返回0
return 0;
}
}
IComparer接口允许我们在数组或集合上执行简单的、快速排序算法。这个接口提供的唯一的方法是Compare,它必须返回整型值--也就是如果左边的数据项大于右边的就返回+1,如果左边的数据项小于右边的就返回-1,如果相等就返回0。你可以看到,在上面的实现中,显示模式的宽度有最高的优先级,接着是高度、格式和刷新率。这个次序规定了在比较两种模式(例如1280x1024和1280x768)的时候正确的操作方法。
这些模式被排序之后,就调用EnumerateDevices方法。列表6显示了这个方法。
列表6:列举设备类型
private static void EnumerateDevices(EnumAdapterInformation adapterInfo,
ArrayList adapterFormatList)
...{
// 在查找设备类型的时候忽略任何异常
DirectXException.IgnoreExceptions();
// 列举每个Direct3D设备类型
for(uint i = 0; i < deviceTypeArray.Length; i++)
...{
// 建立一个新设备信息对象
EnumDeviceInformation deviceInfo = new EnumDeviceInformation();
![]()
// 存储该类型
deviceInfo.DeviceType = deviceTypeArray[i];
![]()
// 试图获取其性能
deviceInfo.Caps = Manager.GetDeviceCaps((int)adapterInfo.AdapterOrdinal,
deviceInfo.DeviceType);![]()
// 获取该设备上每个设备组合的信息
EnumerateDeviceCombos( adapterInfo, deviceInfo, adapterFormatList);
![]()
// 我们有设备组合吗?
if (deviceInfo.deviceSettingsList.Count > 0)
...{
// 有,把它添加到列表中
adapterInfo.deviceInfoList.Add(deviceInfo);
}
}
// 打开异常处理开关
DirectXException.EnableExceptions();
}
0
相关文章
