Visual C#编写3D游戏框架示例

2016-02-19 16:15 50 1 收藏

get新技能是需要付出行动的,即使看得再多也还是要动手试一试。今天图老师小编跟大家分享的是Visual C#编写3D游戏框架示例,一起来学习了解下吧!

【 tulaoshi.com - 编程语言 】

  你可能对实际地编写游戏代码期待已久了。由于DirectX SDK 2004年夏季更新包含了一个牢固的示例框架组件,并且它被设计成能在你自己的代码中直接使用,同时还为你处理了很多事务,所以你只要简单的使用它,就可以节省大量的时间和精力。

  本文中的例子使用的就是这个示例框架组件,在本文中,你将学习到的内容有:

   如何建立自己的项目

   如何使用示例框架组件来列举设备

  建立项目

  在本文中,我假定你的所有开发工作都将使用Visual Studio .NET 2003来完成。如果你不希望使用这个环境,可以使用命令行编译代码,它允许你使用任意的文本编辑器或集成开发环境(IDE)。

  启动Visual Studio .NET 2003并点击起始页面中的"新建项目"按钮。如果你没有使用起始页面,可以点击"文件"菜单下的"新建"子菜单中的"项目"菜单项,或者使用Ctrl+Shift+N。选择"Visual C#项目"区域中的"Windows项目"数据项。把这个项目命名为Blockers,这是游戏的名称。

  在你查看自动生成的代码之前,首先把示例框架组件添加到你的项目中。一般情况下,我会在"解决方案浏览器"中建立一个新文件夹,并把这些文件放入一个这个独立的文件夹中(把这个文件夹的名字取为Framework)。右键点击这个新建的文件夹,从"添加"菜单中选择"添加已有的项"。导航到DirectX SDK文件夹,你会发现该示例框架文件位于SamplesManagedCommon文件夹中,选择每个文件并添加到你的项目中。

  在示例框架组件被添加到项目中以后,你就可以去掉自动生成的代码了。这些代码中的大部分都是用于建立别致的Windows窗体应用程序的,因此,它与我们编写游戏的代码是无关的。用列表1中的代码替换已有的代码和类(Form1)。

  列表1:空的框架组件

  

using System;using System.Configuration;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;using Microsoft.Samples.DirectX.UtilityToolkit;public class GameEngine : IDeviceCreation{ ///程序入口。初始化所有部分并进入一个消息处理循环。用空闲时间显示场景 static int Main() {  using(Framework sampleFramework = new Framework())  {   return sampleFramework.ExitCode;  } }}

  这段新代码中有三个地方比较突出。首先,你可能注意到了除了静态的main方法之外,删除了所有东西,而且main方法也被修改过。剩余的代码是Windows窗体设计器的支撑代码。由于这个应用程序不需要使用该设计器,因此这些代码就没有用了,可以被删除。其次,这段代码不能编译,因为游戏引擎希望实现的两个接口还未实现。再次,这段代码实际上没有做任何事务。

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  在你开始解决后面两个问题之前,你必须添加一些引用。由于你准备在这个项目中显示奇特的3D图像,你就必须给项目添加能执行这样的显示操作的组件的引用。本文采用受控DirectX来执行这种操作,因此你需要在"项目"菜单中选择"添加引用"。图1显示了弹出的对话框。

  图1:添加引用对话框

  如果你安装了DirectX 9 SDK 2004年夏季更新,你会发现有多个版本的受控DirectX组件可供使用。请选择最新的版本(1.0.2902.0版本)。对于这个项目来说,你需要添加三个不同的组件引用:

   Microsoft.DirectX

   Microsoft.DirectX.Direct3D

   Microsoft.DirectX.Direct3DX

  DirectX根(root)组件包含了辅助显示计算的数学结构。其它两个组件相应地包含了Direct3D和D3DX的功能。添加这些引用之后,你可以简单地查看一下列表1中添加的using语句,以确保名字空间被正确地引用了。这个步骤可以确保你不需要完整地限定类型。例如,如果不添加using语句,那么声明一个Direct3D设备变量,就必须使用下面的语句:

  

Microsoft.DirectX.Direct3D.Device device = null;

  Using语句可以减少很多输入内容(没有人希望在声明一个变量时输入全部的内容)。由于你已经增加了using语句,就可以使用如下所示的声明语句了:

  

private Device device = null;

  你可以看到,用这种方式声明变量简单多了,节省了大量的输入。在了解这些信息之后,你可以开始修补应用程序编译过程中的错误了,并准备好编写第一个3D游戏了。你现在必须实现的唯一一个接口是IDeviceCreation,它控制着设备的列举和建立。

  你可能会想"列举设备做什么?我只有一个监视器"!尽管一般情况下是这样的,但是现在的显卡实际上是支持多个监视器的,即使你只有一个设备,你仍然拥有多个可选择的模式。显示器的格式可能不同(你可以在Windows桌面设置中看到这些种类,例如16位色和32位色)。全屏幕模式下的高度和宽度也可能有不同的值,你甚至于还可以控制屏幕的刷新率。总而言之,还是有些事情需要解决。

  列表2中的代码修补的应用程序中的编译错误。

  列表2:实现接口

  

/// 设备初始化的时候调用。这段代码检查设备最小的性能,/// 如果没有通过检查,就返回falsepublic bool IsDeviceAcceptable(Caps caps, Format adapterFormat,Format backBufferFormat, bool windowed){ if (!Manager.CheckDeviceFormat(caps.AdapterOrdinal, caps.DeviceType,   adapterFormat, Usage.QueryPostPixelShaderBlending,   ResourceType.Textures, backBufferFormat))  return false; if (caps.MaxActiveLights == 0)  return false; return true;}/// 在建立某个设备之前,这个回调函数会被立即调用,以允许应用程序修改设备的/// 设置信息。它提供的设置参数包含了框架组件为新设备挑选的设置, 并且应用程序/// 可以直接对这个结构进行任何需要的修改。请注意,示例框架没有纠正无效的/// 设备设置信息,因此必须小心地返回有效的设备设置,否则建立设备就会失败。public void ModifyDeviceSettings(DeviceSettings settings, Caps caps){ // 这个应用程序没有使用任何get方法,它被设计成在一个纯设备上工作。 // 因此如果受到支持并且使用HWVP,就建立一个纯设备。 if ( (caps.DeviceCaps.SupportsPureDevice) && ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) != 0 ) )  settings.BehaviorFlags |= CreateFlags.PureDevice;}

  请你查看一下声明的IsDeviceAcceptable方法。在示例框架忙着枚举系统中的设备的时候,它会在每个找到的组合上调用该方法。注意到该方法返回一个布尔型值吗?这使得你有权利告诉示例框架你认为某个设备是否符合需求。但是,在你仔细查看第一个方法中的代码的时候,请先看一下声明的第二个方法(ModifyDeviceSettings)。某个设备被建立之前,示例框架会立即调用这个方法,允许你加入任何希望的选项。但是你要小心地使用参数选项,因为它可能导致设备建立失败。

  现在我们回到第一个方法:我们先看一下它的参数。首先,它带有一个Caps类型的参数,它是设备性能的简单结构体。该结构体包含了具体设备的巨量信息,可以帮助你决定某个类型是不是你正在使用的设备类型。其后的两个参数都是特定设备的格式参数:一个是后台的缓冲区格式,另一个是设备的格式。

  请注意

  后台缓冲区是实际要显示的数据(象素)在发送给显卡处理并输出到屏幕之前所存储的地方。后台缓冲区的格式决定了可以显示多少种色彩。大多数格式遵循特定的命名习惯--每个字符跟着一个数字,例如A8R8G8B8。字符所指定的构成部分拥有与其后面的数字相同数量的位(bit)。在A8R8G8B8中,该格式可以包含32位色彩信息,alpha、red、green和blue各用8位。最常见的构成是:

  A Alpha

  R Red

  G Green

  B Blue

  X Unused

  你可以查看DirectX SDK文档得到更多关于格式的信息。由于我们还需要知道该设备时候可以显示在窗体中,所以这个方法还有一个参数(最后一个)。尽管大多数游戏都运行在全屏模式下,但是编写和调试在全屏模式下运行的游戏却很困难。在调试过程中,这个应用程序在窗体模式而不是全屏模式下显示。

  请注意

  窗体模式是我们运行的大多数应用程序的显示方式。其中大多数应用程序带有边框和控制菜单,右上角带有最小化、最大化和关闭按钮。在全屏模式下,应用程序覆盖了整个屏幕,并且在大多数情况下没有边框。如果全屏模式使用了另外的屏幕大小(你当前使用的桌面),你可以改变桌面的分辨率。

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  你可能注意到了默认行为是接受该设备,但是在接受之前进行了两项检查。第一项检查确保了传递进来的设备可以进行alpha混合(游戏的用户界面需要这个),如果它不能够实现,就返回false表明这个设备不能被接受。接着,它检查是否支持活动光源(active light)的能力。没有光源的屏幕看起来是平面的、是假的,因此一般至少需要一个光源。

  还有一些代码在设备建立之前的确修改了该设备,你可能想知道这段代码的作用。能够执行处理过程的设备需要用多种方法来显示顶点,要么在硬件中计算或软件中计算,或者两者都使用。如果处理过程完全在硬件中进行,这就是另外一种模式,就叫做"纯硬件设备",它潜在地提供了更高的性能。这段代码检查你当前是否要建立一个硬件处理设备,如果你正准备这样做,并且该纯设备是可以使用的,那么它就切换到这种模式中。你不能建立纯设备(如果该设备是可用的)的唯一情形是你计划调用该设备上的某个get方法或属性。由于在这个例子中你不需要这样操作,所以你可以自由地使用能力更加强大的设备。

  示例框架中有一些不安全(unsafe)的代码,因此你需要更新自己的项目并处理这些问题。见图2。

  图2:允许不安全的代码

  列举所有设备选项

  现在你可以让框架组件开始列举系统中的设备了。首先,为游戏引擎类声明一个构造函数,并把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); }}

来源:https://www.tulaoshi.com/n/20160219/1611473.html

延伸阅读
一、引言 1. 问题的引入 假设你设计的程序已经部署到用户的计算机上,并且能够正常运行了。但是有一天,用户打来了电话他们要求增加新的功能。确定了用户的需求后,你竟然发现原有的软件架构已经无法胜任新增任务的需求你需要重新设计这个应用了!但问题是,就算你又用了一个开发周期完成了用户需要的应用,却不能保证用户的需求...
摘要:本文论述了各种模式的线程(单线程、单元线程和自由线程)以及每种模式的使用方法。同时,还提供了一个使用线程的 C# 语言代码示例,以帮助您编写使用线程的应用程序。本文还讨论了多线程代码中的一些重要问题。 简介 编写多线程 Microsoft® 消息队列 (MSMQ) 触发器应用程序向来是一件让人畏惧的事情。不过,.NET 框架...
摘要:本文介绍了Excel对象、C#中的受管代码和非受管代码,并介绍了COM组件在.NET环境中的使用。 关键词:受管代码;非受管代码;Excel对象;动态连接库 引言 Excel是微软公司办公自动化套件中的一个软件,他主要是用来处理电子表格。Excel以其功能强大,界面友好等受到了许多用户的欢迎。在设计应用系统时,对于不同的用户...
命名空间提供了一种组织相关类和其他类型的方式。与文件或组件不同,命名空间是一种逻辑组合,而不是物理组合。在C#文件中定义类时,可以把它包括在命名空间定义中。以后,在定义另一个类,在另一个文件中执行相关操作时,就可以在同一个命名空间中包含它,创建一个逻辑组合,告诉使用类的其他开发人员这两个类是如何相关的以及如何使用它们: n...

经验教程

265

收藏

80
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部