📄 3d游戏编程入门经典.txt
字号:
现在游戏构想已经完成,对象模型也有了足够的信息,并且列出了游戏的规范,您已经做好了充足的准备转入您期望的内容:编写游戏的代码。通常,在游戏开发之前,您需要编写许多“样板”代码,这些代码需要完成的任务包括枚举系统中的设备并选择最适合的设备绘制场景;创建并维护这些设备。因为directx sdk summer 2004 update中包含了一个样本框架,您可以在代码中直接使用。该样本框架能够为您处理大部分上面提到的任务,它能够简化您的工作并节约大量的时间。
本书中的例子使用了这个框架。
在本章中,您将了解:
● 如何创建项目
● 如何使用样本框架枚举设备
3.1 创建项目 提示:
在本书的剩余部分中,设定您在所有的开发中使用了visual studio .net 2003。如果您不想使用这种环境,可以回到第1章“游戏的开发和托管代码”,查看关于在命令行中编译代码的内容,它允许您使用任何文本编辑器或者集成开发环境。
启动visual studio .net 2003程序,单击起始页的new project按钮。如果您不使用起始页面,则单击file | new | project菜单项,或者使用快捷键ctrl+shift+n。选择visual c# projects部分中的windows application项(参考第1章中的图1.3)。将该项目命名为blockers,它将是游戏的名字。
在查看自动生成的代码之前,首先将样本框架的代码文件添加到您的项目中。通常,我将这些文件放置到一个独立的文件夹中。鼠标右键单击solution explorer中的项目,选择add菜单中的new folder项。将该新文件夹命名为framework。鼠标右键单击framework文件夹,选择add菜单中的add existing item项。查看directx sdk文件夹,您将发现样本框架文件位于samples\managed\common文件夹中。选择所有文件并将它们添加到您的项目中。
添加样本框架之后,您可以删除自动生成的代码。这些代码主要用于生成windows forms应用程序,因此它与您将编写的游戏代码没有任何关系。用程序清单3.1中的代码替换已存在的代码和类(名字为form1)。
程序清单3.1 空框架
using system;
using system.configuration;
using microsoft.directx;
using microsoft.directx.direct3d;
using microsoft.samples.directx.utilitytoolkit;
public class gameengine : idevicecreation
{
///
/// entry point to the program.initializes everything and goes into a
/// message processing loop. idle time is used to render the scene.
///
static int main()
{
using(framework sampleframework = new framework())
{
return sampleframework.exitcode;
}
}
}
对于这段新代码,应当强调三点。首先,除了static main方法之外,所有的内容都删除了,而且static main方法也被修改了。剩余的代码是windows窗体设计器的支持代码。因为您在该应用程序中不使用这个设计器,所以这些代码是不相关的,可以删除。第二,这段代码无法通过编译,因为游戏引擎类应该完成的两个界面还没有实现。第三,这段代码没有任何功能。
在您开始解决后面两个问题时,需要添加一些引用。您将在该项目中绘制独特的3d图形,所有需要为完成绘制工作的程序集添加引用。本书使用managed directx程序集来完成该工作,单击project | add | reference菜单项,弹出如图3-1所示的对话框。
如果已经安装了directx 9的summer 2004 sdk更新(您应当安装,因为本书中的代码需要),您将注意到每个managed directx程序集都可能有多个版本。选择最新的版本(版本1.0.2902.0)。对于该项目,您需要在引用中添加三个不同的程序集:
● microsoft.directx
● microsoft.directx.direct3d
● microsoft.directx.direct3dx
图3-1 add reference对话框
directx程序集包含了一些数学构造,用于组成绘图所需要的计算。其他的两个程序集分别包含了direct3d和d3dx的功能。添加引用后,查看程序清单3.1中的using子句,确保命名空间被引用。这一步确保了您不需要完全限定类型。例如,如果不添加using子句,为了声明一个direct3d设备的变量,您需要按如下格式:
microsoft.directx.direct3d.device device = null;
这些using子句减少了大量的录入工作(没有人愿意为声明每一个变量录入所有的字符)。您已经添加了using子句,所以可以采用如下方式声明相同的设备:
private device device = null;
如您所见,采用这种方式声明设备更加简洁。完成这些任务之后,您可以开始修改应用程序中的一些编译错误,并准备编写第一个3d游戏。您已经实现的接口只有idevicecreation,它能够让您控制设备的枚举和创建。
您可能会思考:“枚举设备?我只有一个显示器!”先进的图形卡支持多个显示器,即使您只有一个,您仍然需要从许多不同的模式中选择一个。显示的格式可以不同 (您可能已经在windows桌面的设置中看到这种不同,选项包括16位颜色或者32位颜色) 。全屏模式的宽度和高度可以设为不同的值,甚至可以控制屏幕的刷新率。总之,需要考虑大量的事情。
为了修改应用程序中的编译错误,添加程序清单3.2中的代码。
程序清单3.2 实现接口
///
/// called during device initialization, this code checks the device for a
/// minimum set of capabilities and rejects those that don’t pass by
/// returning false.
///
public bool isdeviceacceptable(caps caps, format adapterformat,
format backbufferformat, bool windowed)
{
// skip back buffer formats that don’t support alpha blending
if (!manager.checkdeviceformat(caps.adapterordinal, caps.devicetype,
adapterformat, usage.querypostpixelshaderblending,
resourcetype.textures, backbufferformat))
return false;
// skip any device that doesn’t support at least a single light
if (caps.maxactivelights == 0)
return false;
return true;
}
///
/// this callback function is called immediately before a device is created
/// to allow the application to modify the device settings. the supplied
/// settings parameter contains the settings that the framework has
/// selected for the new device, and the application can make any desired
/// changes directly to this structure. note however that the sample
/// framework will not correct invalid device settings so care must
/// be taken to return valid device settings; otherwise, creating the
/// device will fail.
///
public void modifydevicesettings(devicesettings settings, caps caps)
{
// this application is designed to work on a pure device by not using
// any get methods,so create a pure device if supported and using hwvp.
if ( (caps.devicecaps.supportspuredevice) &&
((settings.behaviorflags & createflags.hardwarevertex
processing) != 0 ) )
settings.behaviorflags |= createflags.puredevice;
}
查看声明的第一个方法,isdeviceacceptable方法。当样本框架忙于枚举系统中的设备时,它为所发现的每一个组合调用了该方法。注意该方法是如何返回一个布尔值的。通过这个返回值可以向样本框架指出该设备是否满足需求。然而,在您查看第一个方法中的代码之前,注意已经声明的第二个方法,modifydevicesettings。在创建设备之前样本框架会调用该方法,使您能够选择您需要的任何选项。仔细选择这些选项,因为这可能造成设备创建失败。
现在,回到第一个方法。首先查看它的参数。第一个参数的类型是caps,是capabilities的缩写。该结构用于保存关于特定设备的信息,这些信息将帮助您确定该设备是否是您希望使用的设备类型。后面两个参数是该设备的专用格式:一个用于反向缓冲区,另一个用于设备的格式。
提示:
绘图数据(像素)在发送到显卡显示之前存储在反向缓冲区。反向缓冲区的格式确定了能够显示的颜色数目。大多数格式遵守特定的命名协议,每一个字符后面紧跟着一个数字,例如a8r8g8b8。字符所声明的部件具有数字所表示的位数。在a8r8g8b8中,该格式的颜色包含32位信息,其中alpha、red、green和blue各8位。最通用的部件是:
a alpha
r red
g green
b blue
x unused
关于格式的信息,您可以查看directx sdk文档。
了解设备是否能生成窗体是很重要的,所以该方法的最后一个参数用于指示设备是否生成窗体。尽管大多数游戏运行于全屏模式,但是编写调试一个运行于全屏模式的游戏比较困难。所以在调试期间,该应用程序以窗体模式运行,而不是全屏模式。
警告:
窗体模式是您打开的大多数应用程序的运行方式。大多数窗体模式的应用程序具有边框、控制菜单、最小化和最大化按钮、以及右上角的关闭按钮。在全屏模式中,应用程序覆盖整个屏幕,大多数情形下没有边框。如果全屏模式使用了与当前桌面不同的屏幕尺寸,您可以改变桌面的分辨率。
您将注意到,默认的行为是接受设备,但是在接受设备之前,该设备需要进行两个特别的检查。第一个检查确保被传入的设备能够执行alpha混合(游戏的用户界面需要该功能),否则返回false,表明不能接受该设备。接着,检查是否支持活动光源。没有光照的场景看上去是平面的、不真实的,因此至少需要一个光源。
还有在创建之前实际修改设备的代码。即使您在本章不创建设备,但也可能希望知道这些代码的用途。设备可以进行需要的处理,从而以各种方式生成顶点:通过硬件计算、软件计算或者采用两种方法的混合。如果是纯硬件方式,则另外一种称为纯硬件设备的模式将提供更好的性能。这段代码检查您当前是否希望创建一个硬件处理设备。如果希望,并且纯设备可用,那么该代码将切换使用该设备。只有在计划调用该设备的get方法或者属性时,您不能创建一个纯硬件设备(该设备可用)。因为在本书的所有应用程序中,不会那样处理,所以可以放心的使用这种功能强大的设备。
在继续之前,还有最后一件事情需要做。样本框架中具有某些不安全代码,因此您需要更新项目来解决该问题。如图3-2所示。
图3-2 允许不安全代码
现在让框架开始枚举您系统中的设备。首先,为游戏的引擎类声明一个构造函数,将您在main中创建的样本框架的实例传入。如程序清单3.3所示。
程序清单3.3 添加一个构造函数
private framework sampleframework = null; // framework for samples
/// create a new instance of the class
public gameengine(framework f)
{
// store framework
sampleframework = f;
}
该构造函数只负责存储样本框架实例,因为在游戏中发生的每一件事情几乎都需要样本框架。在调用该构造函数之后,样本框架首先枚举系统中的所有设备。在项目文件中,您将在前面创建的framework文件夹中看到dxmutenum.cs文件。该文件包含了枚举系统设备所必需的所有代码。明白如何以及为什么要枚举设备是非常重要的,所以现在打开该文件。
您应当注意的第一点是,enumeration类本身无法被创建,并且每一个成员变量和方法都声明为static类型。因为(至少在目前)运行应用程序(且计算机打开)时,图形设备不能改变,所以只在应用程序的开始执行一次枚举代码是合理的。
大多数枚举工作起始于enumerate方法,样本框架在设备创建之前调用该方法。注意,该方法惟一接受的参数是您已经在游戏引擎类中实现的接口。存储该接口,因为在后面当枚举设备组合时,调用isdeviceacceptable方法确定设备是否应当被添加到有效设备列表中。
设备是如何被枚举的呢?大多数功能在managed directx的manager类中。如果您熟悉非托管的directx应用编程接口,则这个类镜像了idirect3d9组件对象模型(com)接口。注意程序清单3.4中enumerate方法的第一个循环。
程序清单3.4 枚举设备
// look through every adapter on the system
for each(adapterinformation ai in manager.adapters)
{
enumadapterinformation adapterinfo = new enumadapterinformation();
// store some information
adapterinfo.adapterordinal = (uint)ai.adapter; // ordinal
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -