赞助广告

 

年份

最新评论

评论 RSS

XNA默认支持的字体

clock 一月 28, 2015 09:11 by author alex

解决:XNA Framework Reach profile requires TextureAddressMode to be Clamp when using texture sizes that are not powers of two

clock 五月 8, 2013 22:20 by author alex
今天终于成功将广告平台加进了windows phone 7+silverlight+cocos2d-x xna项目。广告虽然成功载入,但发现点击广告跳转浏览器,然后再按返回键返回会造成程序崩溃。报: XNA Framework Reach profile requires TextureAddressMode to be Clamp when using texture sizes that are not powers of two 错误。 这个是xna载入资源重复造成的。 解决办法:在Draw中加入SharedGraphicsDeviceManager.Current.GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

XNA版的BOX2D物理引擎

clock 十月 29, 2012 09:37 by author alex
最近在制作一款Windows Phone游戏,用到了XNA版的BOX2D。 BOX2D这个引擎,是个很好的模拟真实物理世界的引擎。愤怒的小鸟就是基于这个引擎做的。 下载地址: http://box2dxna.codeplex.com/ 使用方式:由于xna的BOX2D是源码发布所有需要用Visual Studio 2010生成一下。 生成后将Box2D.XNA.TestBed\bin\Windows Phone\Debug的BOX2D.XNA.DLL复制出来。引用到自己的项目中即可使用。 使用方式: http://blog.csdn.net/fengyun1989/article/details/7521642

XNA中使用SpriteFont汉字

clock 十月 22, 2012 10:34 by author alex
在XNA中,如果要使用文字,就需要先指定字体,对于字体XNA使用了SpriteFont和 SpriteFontTexture两种方式来指定字体。 XNA默认是不支持中文的,如果在SpriteFont中使用中文会抛出错误异常。 想使用中文需要修改SpriteFont文件(xml配置文件)。 找到 <CharacterRegions>                  <CharacterRegion>                       <Start>&#32;</Start>                       <End>&#126;</End>                  </CharacterRegion> </CharacterRegions> 改为 <CharacterRegions>                  <CharacterRegion>                       <Start>&#19968;</Start>                       <End>&#40869;</End>                  </CharacterRegion> </CharacterRegions> 编码区间是4E00(19968)—9FA5(40869),有2万多字,所以编译起来特别的慢,有时还会让Visual Studio无响应。由此看来,要开发XNA中文版的游戏,机器一定要好,要不然连编译都过不去。

monogame携XNA决战windows8

clock 九月 15, 2012 14:52 by author alex
windows 8视乎抛弃了XNA,但Mono没有,monogame跨平台XNA游戏引擎来了~~! 准备工作: 1.安装Windows 8 Pro或Windows 8 Release Preview 2.安装Visual Studio 2012或Visual Studio 2012 RC 3.下载MonoGame Template for Metro Style Apps 【VS11MGWindowsMetroTemplate.zip (70.42 kb)】 4.将MonoGame Template for Metro Style Apps复制到【%UserProfile%\My Documents\Visual Studio 2012\Templates\ProjectTemplates\Visual C#\】 5.打开Visual Studio 2012就可以发现有了MonoGame Metro Application for Windows

如何将XNA游戏中的元素设为透明

clock 九月 14, 2011 14:04 by author alex
XNA游戏开发中,有两种方法可以使元素透明: 1.图像文件本身设置成透明的背景。 2.将图像中您希望显示为透明的部分设置为洋红色(255, 0, 255)——XNA会自动将洋红色渲染成透明的。透明度可以存储到某些文件格式中(如.png文件)用作alpha13通道。这种格式不仅仅包含RGB值,每个像素还有一个额外的alpha通道(RGBA中的A)来确定像素的透明度。

Learning XNA 4.0 中文版下载

clock 九月 13, 2011 09:37 by author alex
目前XNA方面的中文书籍非常缺乏,可能是因为XBOX在国内不流行的缘故,游戏开发大多采用C或者是C++。本人相信在不久的将来XNA必将成为游戏开发的中流砥柱。在这里提供一本XNA中文教程供大家下载,于此同时非常感谢本书的译者,为我们翻译了这么好的一本书。当然更鼓励大家购买本书,支持正版。 下载:《XNA_4.0_学习指南》.pdf (6.31 mb)

3D 游戏控制

clock 八月 1, 2011 10:13 by author alex
  摘要 了解如何制作 XNA 游戏使用的 3D 模型,以及于 XNA 游戏中显示 3D 模型的基本做法之后,这一次我们将要为大家介绍 3D 模型的控制技巧,包括旋转、放大/缩小、平移、以及相机控制等游戏常见的操作。有关如何制作 XNA 游戏使用的 3D 模型,以及于 XNA 游戏中显示 3D 模型的基本做法可以参考 [3D 游戏设计] 一文的详细说明。 3D 游戏设计基础 在执行控制与显示 3D 模型之前,首先我们必须了解以 XNA 为基础的 3D 游戏采用的坐标系统,表示坐标与控制坐标转换常用的数据型态,并认识 Object Space、World Space、View Space、和 Screen Space 四种空间代表的意义与用途。 以 XNA 为基础的 3D 游戏采用的是右手坐标系统 (Right-Handed System),其X轴往右递增,Y 轴往上递增,而 Z 轴则是往使用者的方向递增,如图1 所示: 图1:以 XNA 为基础的 3D 游戏采用的右手坐标系统 控制或显示 3D 模型,我们需要用到描述坐标的 Vector2 和 Vector3 结构,以及用来协助坐标计算的 Vector4 结构与 Matrix 结构。有关 Vector2、Vector3、和 Vector4 等和坐标有关的结构型态的详细功能可以参考 [XNA Framework 常用的类别] 一文的说明。 除了描述坐标和进行坐标计算需要用到的结构型态以外,要显示和控制 3D 模型,我们还需要了解 3D 模型的 Object Space (亦称 Model Space)、World Space、View Space、和 Screen Space (亦称 Projection Space) 四种空间表示法的意义。 简单来说,3D 模型在 3D 的世界中有几种不同的空间表示法,3D 模型中的坐标点未经任何的转置处理前称为物体空间 (object space),当 3D 模型经过旋转,放大/缩小等转置处理之后就会得到世界空间 (world space),经过转置处理过的世界空间在显示给用户检视之前,还需要加入相机的位置和角度的处理,形成检视空间 (view space),最后的检视空间在显示之前必须投射到屏幕的2维空间,称为屏幕空间 (screen space),而屏幕空间显示的内容就是用户最终看到的结果。 要将 3D 模型的物体空间转换成世界空间、检视空间、和屏幕空间,我们需要借助于 Matrix 结构及其提供的属性和方法。Matrix 结构负责定义一个 4x4 的矩阵,其常用的属性可以参考表1 的说明: 表1:Matrix 结构常用的属性 属性名称 说明 Backward 描述后退的向量。 Down 描述下移的向量。 Forward 描述前进的向量。 Identity 传回单位矩阵。单位矩阵的定义就是任何矩阵和单位矩阵相乘之后的结果会得到原来的矩阵。 Left 描述左移的向量。 Right 描述右移的向量。 Translation 描述平移的向量。 Up 描述上移的向量。 [说明] 单位矩阵的定义是矩阵的元素中,除了列编号和栏编号内容值相同的元素的内容值为 1 以外,其他的元素的内容值皆为 0。例如 2x2 的单位矩阵的内容如下: 3x3 的单位矩阵如下: 而 n*n 的单位矩阵如下: Matrix 结构常用的方法请参考表2 的说明: 表2:Matrix 结构常用的方法 方法名称 说明 Add 支持矩阵相加的方法。 CreateBillboard 依据指定的物体的位置建立球形的广告牌。 CreateConstrainedBillboard 依据指定的轴建立圆柱体广告牌。 CreateFromAxisAngle 依据指定的旋转轴心和角度建立 Matrix 结构。 CreateFromQuaternion 依据 Quaternion 结构指定的向量和角度建立用来旋转物体的 Matrix 结构。 CreateFromYawPitchRoll 依据X轴旋转角度、Y 轴旋转角度、和 Z 轴旋转角度建立用来旋转物体的 Matrix 结构。 CreateLookAt 建立以相机为原点的 View Space,并表示成 Matrix 结构。 CreateOrthographic 建立直角的 Projection Space,并表示成 Matrix 结构。 CreateOrthographicOffCenter 建立可客制化的直角 Projection Space,并表示成 Matrix 结构。 CreatePerspective 建立透视的 Projection Space,并表示成 Matrix 结构。 CreatePerspectiveFieldOfView 依据视角 (Field of View-简称 FOV) 建立透视的 Projection Space,并表示成 Matrix 结构。 CreatePerspectiveOffCenter 建立可客制化的透视 Projection Space,并表示成 Matrix 结构。 CreateReflection 利用指定的平面的坐标系统的反射建立 Matrix 结构。 CreateRotationX 建立能够以 X 轴为轴心旋转指定角度的 Matrix 结构。 CreateRotationY 建立能够以 Y 轴为轴心旋转指定角度的 Matrix 结构。 CreateRotationZ 建立能够以 Z 轴为轴心旋转指定角度的 Matrix 结构。 CreateScale 建立可以用来执行放大/缩小 3D 模型的 Matrix 结构。 CreateShadow 依据指定的光源建立可以用来表示阴影的 Matrix 结构。 CreateTranslation 建立可以用来平移 3D 模型的 Matrix 结构。 CreateWorld 建立可以用来表示 World Space 的 Matrix 结构。 Decompose 从 Matrix 结构取出放大/缩小、平移、和旋转的成份值。 Determinant 计算 Matrix 结构的 determinant 值。以 2x2 的 Matrix 结构为例,其 determinant 值为 ad-bc。 Divide 计算 Matrix 结构除以一个数值或是另外一个 Matrix 结构的结果,并表示成 Matrix 结构。 Invert 取得反矩阵,并表示成 Matrix 结构。 Lerp 对两个 Matrix 结构对应的元素值执行线性内插。 Multiply 计算 Matrix 结构除以一个数值或是另外一个 Matrix 结构的结果,并表示成 Matrix 结构。 Negate 将 Matrix 结构每一个元素值改成负数,并将执行的结果表示成 Matrix 结构。 op_Addition 对两个 Matrix 结构执行加法,并将执行的结果表示成 Matrix 结构。 op_Division 将 Matrix 结构除以一个数值,或是另外一个 Matrix 结构的元素,并将执行的结果表示成 Matrix 结构。 op_Equality 判断两个 Matrix 结构是否相等。 op_Inequality 测试 Matrix 结构和另外一个 Matrix 结构是否不相等。 op_Multiply 将 Matrix 结构乘以一个数值,或是另外一个 Matrix 的元素,并将执行的结果表示成 Matrix 结构。 op_Subtraction 对两个 Matrix 结构执行减法,并将执行的结果表示成 Matrix 结构。 op_UnaryNegation 将 Matrix 结构的每一个元素都设定成负值。 Subtract 支持 Matrix 结构相减的方法。 Transform 依据 Quaternion 结构的内容对 Matrix 结构执行旋转。 Transpose 对 Matrix 结构执行转置 (Transpose) 运算,也就是列变成行,行变成列的转换。 [说明] 建立 3D 场景的时候,将 2D 的物体显示成 3D 的效果以得到较好的效能的做法称之为 Billboarding (广告牌),而 Matrix 结构提供的 CreateBillboard 方法与 CreateConstrainedBillboard 方法就是支持建立广告牌的方法。将 2D 的物体显示成 3D 的效果主要的概念就是由物体图案组成的纹理套用在矩形的元素上,由程序视需要旋转矩形的元素,使其面向使用者的目光。请注意物体的图案的形状可以不是矩形的形状,而且广告牌还可以允许某一部分形成透明的效果,以隐藏对象图案的内容。 许多游戏程序都是利用广告牌技巧显示 3D 动画,例如游戏的主角在 3D 的迷宫内走动,所看到,而且可以取用的武器和宝物都是由 2D 的图案套用在矩形元素形成的效果。3D 游戏显示树林、灌木丛、或是天空的云的时候,常常都是靠广告牌技术呈现的效果。当游戏程序将图案套用到广告牌的时候,矩形元素会先旋转至面对用户的视线,然后再执行套用纹理的动作。 广告牌的效果适合套用在对称的物体,特别是垂直对称的物体,但是物体的视角不可以太高,否则当物体由上往下审视广告牌的时候,其效果会变成 2D 的效果,而不再具有 3D 的视觉效果。 3D 模型转置计算 Matrix 结构是游戏程序显示与控制 3D 模型极常用的型态,当游戏程序需要平移、旋转、或是放大/缩小 3D 模型时,就可以利用 Matrix 结构提供的功能建立可以将 3D 模型从 Object Space 转换到 World Space 的 World Matrix。例如游戏程序可以呼叫 Matrix 结构的 CreateTranslation 方法建立可以平移 3D 模型的 World Matrix,当您想将 3D 模型平移至 X 坐标为 10,Y 坐标为 0,Z 坐标为 50 的位置,就可以利用以下的程序代码建立 World Matrix: Matrix WorldTranslation = Matrix.CreateTranslation(new Vector3(10, 0, 50)); 做好之后,游戏程序只要将所建立的 World Matrix 套用至 Mesh 的 Effect,就可以达到平移 3D 模型的效果。 如果游戏程序需要旋转 3D 模型,可以呼叫 Matrix 结构的 CreateRotationX 方法并传入旋转角度表示要依据 X 轴为轴心旋转指定的角度,呼叫 Matrix 结构的 CreateRotationY 方法并传入旋转角度表示要依据 Y 轴为轴心旋转指定的角度,或是呼叫 Matrix 结构的 CreateRotationZ 方法并传入旋转角度表示要依据 Z 轴为轴心旋转指定的角度。请注意 CreateRotationX、CreateRotationY、和 CreateRotationZ 方法需要的角度的单位是弪度量,游戏程序可以视需要先利用 MathHelper 类别的 ToRadians 方法将单位为度度量的角度转换成弪度量,以便执行旋转的动作。 以下的程序代码会建立可以将 3D 模型以 Y 轴为核心旋转 180 度的 Matrix: Matrix worldRotation=Matrix.CreateRotationY(MathHelper.Pi); 请注意上述的程序代码会利用 MathHelper 类别的 Pi 属性指定旋转的角度为 180 度。游戏程序除了可以利用 CreateRotationX、CreateRotationY、和 CreateRotationZ 方法来旋转 3D 模型以外,也可以利用 Matrix 结构提供的 CreateFromYawPitchRoll 方法指定以 X 轴、Y 轴、和 Z 轴为轴心的旋转角度,其中的 Yaw 代表以 Y 轴为轴心的旋转角度,Pitch 代表以 X 轴为轴心的旋转角度,而 Roll 代表以 Z 轴为轴心的旋转角度,如图2 所示: 图2:Pitch 代表以 X 轴为轴心的旋转角度,而 Roll 代表以 Z 轴为轴心的旋转角度 例如以下的程序代码中呼叫 Matrix 结构的 CreateRotationX、CreateRotationY、和 CreateRotationZ 方法的语法: Matrix modelWorldMatrix = Matrix.CreateScale(2.0f) * Matrix.CreateRotationX(MathHelper.Pi) * Matrix.CreateRotationY(MathHelper.Pi) *                           Matrix.CreateRotationZ(MathHelper.Pi) *                       Matrix.CreateTranslation(10, 0, 50); 就可以改呼叫 Matrix 结构的 CreateFromYawPitchRoll 方法来代替,如下: Matrix modelWorldMatrix = Matrix.CreateScale(2.0f) * Matrix.CreateFromYawPitchRoll(MathHelper.Pi, MathHelper.Pi, MathHelper.Pi) *     Matrix.CreateTranslation(10, 0, 50); [提示] 游戏程序可以利用 XNA Framework 提供的 MathHelper 类别的方法完成常用的数学运算,包括计算角度,例如游戏程序需要用到 180 度角,就可以利用 MathHelper.Pi 来取得,需要 360 度角,可以直接利用 MathHelper.TwoPi 来取得,不需要执行 MathHelper.Pi * 2,需要 90 度角的时候则使用 MathHelper.PiOver2 来取得,不需执行 MathHelper.Pi / 2,需要 45 度角时可以利用 MathHelper.PiOver4 来取得,不需要执行 MathHelper.Pi / 4,以获得最佳的执行效能。 除了可以协助游戏程序取得角度以外,MathHelper 类别也可以协助游戏程序执行度度量和弪度量之间的转换,例如 MathHelper 类别的 ToDegree 方法可以将弪度量转换成度度量,而 MathHelper 类别的 ToRadians 方法可以将度度量转换成弪度量,不需要游戏程序自行执行转换的动作。 如果游戏程序需要放大/缩小 3D 模型,可以善用 Matrix 结构提供的 CreateScale 方法,例如以下的程序代码便会建立可以将 3D 模型放大两倍的 Matrix 结构: Matrix worldSclae = Matrix.CreateScale(2.0f); 透过上述放大/缩小、旋转、和平移等操作,游戏程序能够将 3D 模型定位在指定的位置和角度。请注意 Matrix 结构进行乘法运算的时候必须按照 SRT (Scale * Rotation * Translation) 顺序来执行,也就是必须先执行放大/缩小的动作,再执行旋转的动作,最后再执行平移的动作,因为 Matrix 结构的乘法不会有累积的效果。 学会将 3D 模型的物体空间 (Object Space) 转换成世界空间 (World Space) 之后,接下来我们就要引入相机的位置,计算出 3D 模型的检视空间 (View Space),也就是从使用者的角度检视 3D 模型的位置。 游戏程序可以呼叫 Matrix 结构提供的 CreateLookAt 方法,依据包括相机位置在内的相关信息,建立可以将世界空间转换成检视空间的 Matrix 结构。例如以下的程序代码就会利用 Matrix 结构的 CreateLookAt 方法建立可以将 3D 模型世界空间转换成检视空间的 Matrix 结构: Matrix viewMatrix = Matrix.CreateLookAt(new Vector3(0, 3, 5), new Vector3(0, 0, 0), new Vector3(0, 1, 0)); 请注意 CreateLookAt 方法的第一个参数代表相机的位置,第二个参数代表欲检视的物体的位置,而第三个参数则是代表 Up 向量,也就是相机旋转依据的轴心,传入 (0,1,0) 表示相机可以以 Y 轴为轴心旋转 (即图3 中的 UP 轴),传入 (0,0,-1) 表示相机可以以 Z 轴为轴心旋转 (即图3 中的 LOOK 轴),而传入 (1,0,0) 表示相机可以以 X 轴为轴心旋转 (即图3 中的 RIGHT 轴),如图3 所示: [提示] X、Y、Z 三个元素值皆为 0 的 Vector3 结构可以表示成 Vector.Zero,而 X、Y、Z 三个元素值分别为 0,1,0 的 Vector3 结构可以表示成 Vector.Up,所以上述建立可以将 3D 模型的世界空间转换成检视空间的 Matrix 结构的程序代码可以简写成以下的样子: Matrix viewMatrix = Matrix.CreateLookAt(new Vector3(0, 3, 5), Vector3.Zero, Vector3.Up); 计算好 3D 模型的检视空间之后,游戏程序还需要将检视空间转换成屏幕空间,也就是将 3D 模型的检视空间投影到 2D 的平面空间的位置,完成呈现 3D 模型的动作,因此屏幕空间 (Screen Space) 又称为投影空间 (Projection Space)。要完成从检视空间至投影空间的转换,游戏程序可以利用 Matrix 结构提供的 CreatePerspectiveFieldOfView 方法,建立能够执行转换工作的 Matrix 结构 (即 Projection Matrix)。 例如以下的程序代码就会呼叫 Matrix 结构的 CreatePerspectiveFieldOfView 方法,建立可以将 3D 模型的检视空间投射到屏幕空称的 Matrix 结构: float aspect = (float)Window.ClientBounds.Width / (float)Window.ClientBounds.Height;Matrix projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspect, 1, 100); Matrix 结构的 CreatePerspectiveFieldOfView 方法需要的第一个参数是单位为弪度量的视角,其角度必须介于 0~p (即 0~180 度) 之间,第二个参数是高宽比例,您可以将游戏窗口的寛度除以游戏窗口的高度当做高宽比例,或是直接使用 GraphicsDeviceManager 的 GraphicsDevice 属性的 Viewport 属性的 AspectRatio 属性的内容值当做高宽比例,第三个参数是与近平面 (Near Plane) 的距离,请注意距离比与近平面还近的内容都不会呈现在游戏窗口中,第四个参数是与远平面 (Far Plane) 的距离,请注意距离比与远平面还远的内容都不会呈现在游戏窗口中。 有关近平面与远平面在 3 度空间的意义可以参考图4: 图4:近平面与远平面在3度空间的意义 [注意] 以 XNA 为基础的游戏程序必须为每一个欲显示的 3D 模型建立 World Matrix,每一次改变相机的位置或方向都必须重新计算 View Matrix,而 Projection Matrix 则只要在游戏程序开始的时候计算一次即可。 3D 模型显示控制 了解 3D 模型的显示与控制原理,以及如何建立将 3D 模型的物体空间转换成世界空间的 Matrix 结构,如何建立将世界空间转换成检视空间的 Matrix 结构,以及如何建立将检视空间投射在屏幕空间的 Matrix 结构之后,接下来我们就要制作可以控制 3D 模型显示的游戏程序。 首先请启动 Visual Studio 2010 Express for Windows Phone,建立 [Windows Phone Game(4.0)] 型态的项目,并将制作好的 3D 模型加入到 Content Pipeline 项目中。有关如何制作 3D 模型可以参考 [3D 游戏设计] 一文的说明。 准备好游戏程序欲显示的 3D 模型之后,请编辑 Game1 类别的建构函式,加入以下的变量宣告,负责管理 3D 模型、3D 模型的显示位置、旋转角度、3D 模型大小、相机位置、以及记载能够将 3D 模型的检视空间投射到屏幕空间的 Matrix 结构: Model SnowMan;                                            //管理 3D 模型的變數Vector3 modelPosition = Vector3.Zero;                //記錄 3D 模型的顯示位置的變數float modelRotation = 0.0f;                        //記錄 3D 模型的旋轉角度的變數float modelScale=2f;                                    //記錄 3D 模型大小的變數Vector3 cameraPosition = new Vector3(0.0f, 50.0f, 5000.0f);         //記錄相機位置的變數Matrix ProjectionMatrix;            //能夠將 3D 模型的檢視空間投射到螢幕空間的 Matrix 結構 宣告妥必要的变量之后,请将 Game1 类别的建构函式编辑成以下的样子,负责定义 XNA 游戏的窗口大小: public Game1(){    graphics = new GraphicsDeviceManager(this);    Content.RootDirectory = "Content";    graphics.PreferredBackBufferHeight = 800;                //設定遊戲視窗的高度為 800    graphics.PreferredBackBufferWidth = 480;                //設定遊戲視窗的寬度為 480    TargetElapsedTime = TimeSpan.FromTicks(333333);} 设定好游戏窗口的高度和宽度之后,请编辑 Game1 类别的 Initialize 方法,加入初始化触控面板,以及计算能够将 3D 模型的检视空间投射到屏幕空间的 Matrix 结构的程序代码: protected override void Initialize(){    TouchPanel.EnabledGestures =                GestureType.FreeDrag |                GestureType.Pinch;            //初始化觸控面板,啟用 FreeDrag 和 Pinch 功能    ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 100000.0f);    //計算能夠將 3D 模型的檢視空間投射到螢幕空間的 Matrix 結構,//指定視角為 45 度,使用遊戲視窗的高寬比例,與近平面的距離為 //1,與遠平面的距離為 100000    base.Initialize();} 完成初始化触控面板,以及计算能够将 3D 模型的检视空间投射到屏幕空间的 Matrix 结构之后,请编辑 Game1 类别的 LoadContent 方法,加入加载 3D 模型资源的程序代码: protected override void LoadContent(){    spriteBatch = new SpriteBatch(GraphicsDevice);    SnowMan = Content.Load<Model>("SnowMan");            //載入名稱為 SnowMan 的 3D 模型資源} [注意] 读者必须视需要修改上述程序代码所指定的 3D 模型资源名称,以加载正确的 3D 模型资源。 加载妥游戏程序欲显示的 3D 模型之后,请将 Game1 类别的 Update 方法编辑成以下的样子,负责依据使用者的触控操作改变旋转 3D 模型的角度,以及放大/缩小 3D 模型的比例: protected override void Update(GameTime gameTime){    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)        this.Exit();    while (TouchPanel.IsGestureAvailable)            //判斷使用者是否執行了手勢操作    {        GestureSample gesture = TouchPanel.ReadGesture();    //讀取使用者的手勢操作   switch (gesture.GestureType)                    //判斷使用者的手勢操作型態        {            case GestureType.FreeDrag:                            //FreeDrag 操作                modelRotation += gesture.Delta.X;        //依據使用者拖曳的軌跡旋轉 3D 模型                break;            case GestureType.Pinch:                                    //Pinch 操作                Vector2 a = gesture.Position;                       //取得第一個觸碰點                Vector2 aOld = gesture.Position – gesture.Delta;                   //取得第一個觸碰點的起始位置                Vector2 b = gesture.Position2;                   //取得第二個觸碰點                Vector2 bOld = gesture.Position2 – gesture.Delta2;                   //取得第二個觸碰點的起始位置float d = Vector2.Distance(a, b);            //計算兩個觸碰點之間的距離                float dOld = Vector2.Distance(aOld, bOld);  //計算兩個原始座標之間的距離float scaleChange = (d - dOld) * .01f;               //計算距離的變化量                 modelScale += scaleChange;     //將距離變化量的 1/10 當做縮放 3D 模型的比例                 break;        }    }    base.Update(gameTime);} 制作好利用触控操作改变 3D 模型大小以及旋转角度的程序代码之后,接下来我们就可以依据 3D 模型的旋转角度和大小显示 3D 模型。请将 Game1 类别的 Draw 方法编辑成以下的样子: protected override void Draw(GameTime gameTime){    GraphicsDevice.Clear(Color.CornflowerBlue);    Matrix[] transforms = new Matrix[SnowMan.Bones.Count];     //依據 3D 模型內含的骨骼數量建立 Matrix 陣列    SnowMan.CopyAbsoluteBoneTransformsTo(transforms);          //取出骨骼的轉置矩陣資料    foreach (ModelMesh mesh in SnowMan.Meshes)        //逐一取出 3D 模型中所有的網格資料    {        foreach (BasicEffect effect in mesh.Effects)    //逐一設定網格資料的所有 Effect        {            effect.EnableDefaultLighting();                        //啟用預設光源            effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateScale(modelScale) * Matrix.CreateRotationY(modelRotation) * Matrix.CreateTranslation(modelPosition);        //設定Effect的World Spaceeffect.View = Matrix.CreateLookAt(cameraPosition, Vector3.Zero, Vector3.Up);            //設定 Effect 的 View Space               effect.Projection = ProjectionMatrix;//計算 3D 模型投影到 2D 空間的投影位置        }        mesh.Draw();                                            //繪製網格的內容    }    base.Draw(gameTime);} 做好之后请将游戏程序部署到 Windows Phone 7 智能型手机上执行,读者可以利用单点触控的方式在游戏窗口拖曳,控制 3D 模型的旋转角度,也可以利用多点触控的方式放大/缩小 3D 模型的大小。允许用户控制 3D 模型旋转角度和大小的游戏程序的执行画面如图5 所示: 图5:允许用户控制 3D 模型旋转角度和大小的游戏程序的执行画面 [范例档案下载] Control3DModel.zip

3D 游戏设计

clock 八月 1, 2011 10:12 by author alex
  摘要 XNA Framework 不只能够支持开发2D的游戏,也能够支持开发3D的游戏,而且有 Windows Phone 7 强大的处理器做为后盾,所开发的 3D 游戏能够在 Windows Phone 7 智能型手机流畅地执行,让使用者享受 3D 游戏带来的进阶声光感受。在这篇文章中,我们将要讨论如何使用 XNA Framework 提供的功能设计 3D 游戏,但是在设计 3D 游戏之前,我们必须先建立妥3D游戏欲使用的 3D 模型,做为 3D 游戏的主体,供 3D 游戏加载显示。 3D 模型设计工具 XNA Framework 预设支援扩展名为 .FBX 和 .X 的 3D 模型,其他格式的 3D 模型必须透过自定义的 Importer,才能够被 Content Pipeline 项目成功地建置成可以在执行时期被 XNA 游戏加载使用的资源。 要建立扩展名为 .FBX 和 .X 的 3D 模型,您可以利用知名的 Maya 或 3Ds Max 工具,或是利用 trueSpace 或 Blender 等功能不错,而且又免费的工具,进行设计 3D 模型的工作。例如 Blender 就是一个提供开放原始码的 3D 模型设计工具,而且支持各大主流的操作系统,包括 Microsoft Windows,有兴趣的读者可以到以下的网址下载并安装 Blender (网址:http://www.blender.org/download/get-blender/)。请注意,要使用或自定义 Blender 支持的 Plug-In,读者的计算机中必须安装有 Python。有需要读者可以视需要到以下的网址下载并安装 Python (网址:http://www.python.org/)。 准备 3D 模型 安装妥 Blender 工具之后,我们就要利用 Blender 建立一个简单的 3D 雪人模型,供以 XNA 为基础的 3D 游戏显示。 首先请启动 Blender 工具,按下 A 键取消目前选取的物体,再按下 A 键,选取所有的物体,然后按下 Del 键,将目前选取的物体全部删除,您将会看到要求您确认删除物体的提示,如图1 所示: 图1:要求确认删除物体操作的提示 请按下 Enter 键,确认要将所有的物体全部删除。 做好之后请选择 Blender 工具左下角的 View 菜单 (View 菜单的外观如:) 中的 Front 功能,切换至正视图检视,然后执行 [Add | Mesh | UV Sphere] 功能,加入一个球体到正视图中,您就会看到如图2 的窗口,要求您设定欲加入的 UV Sphere 的规格: 图2:要求您设定欲加入的 UV Sphere 的规格的窗口 您可以视需要设定 UV Sphere 的区段、环数、和半径,或是直接点选 OK 键接受默认值,Blender 就会于正视图中加入一个 UV Sphere。 加入球体之后请使用鼠标的右键点中刚加入的球体不放,从出现的菜单选择 [Transform | Scale] 菜单,执行放大功能,将球体放大,当做雪人的身体。 做好之后,请点选 Blender 工具左下方的 [Link and materials] 窗口中的 [Set Smooth] 按钮,将球体的表面设定成平滑效果。[Link and materials] 窗口的操作画面如图3 所示: 图3:[Link and materials] 窗口的操作画面 将球体的表面设定成光滑效果之后,请点选 Blender 工具左下角的 Panels 工具栏中,功能提示为 Shading 的按钮 (Panels 菜单的外观如:),或是直接按下 F5 功能键,为球体设定浓淡效果,Blender 工具的下方就会出现 [Links and Pipeline] 窗口,如图4 所示: 图4:[Links and Pipeline] 窗口的操作画面 请点选 [Links and Pipeline] 窗口中的 [Add New] 按钮,开启设定样式的操作画面,请利用 [Material] 窗口中的 R、G、B 色彩设定滑杆设定球体材质的色彩,[Material] 窗口的操作画面如图5 所示: 图5:[Material] 窗口的操作画面 请将 R、G、B 三种色彩的内容都设定成 1,表示要将球体设定成白色的材质。做好之后请利用 [Material] 窗口右方的 [Links and Pipeline] 窗口为所设定的材质命名。设定材质的操作画面如图6 所示: 图6:设定材质的操作画面 请将 [Links and Pipeline] 窗口中的 Link to Object 字段的内容值设定成:MA:Snow,将所设定的材质命名成 Snow,未来如果有其他的物体需要设定相同的材质,可以直接套用名称为 Snow 的材质,不必再为物体设定各种属性。 设定妥材质的名称之后请利用 [Links and Pipeline] 窗口右方的 [Shaders] 窗口设定物体的浓淡效果。[Shaders] 窗口的操作画面如图7 所示: 图7:[Shaders] 窗口的操作画面 请将 [Shaders] 窗口中的Ref设定值调整成 1,表示将反射值设定成 1,将 Spec 设定值调整成 2,表示将反射面设定成 2,再将 Hard 设定值调整成 1,表示将反射面的硬度设定成 1。做好之后就完成雪人身体的设计工作了。 设计妥雪人的身体之后,请按下 A 键解除雪人身体的选取状态,然后执行 [Add | Mesh | UV Sphere] 功能,看到设定 UV Sphere 规格的画面后按下 OK 键,加入一个新的 UV Sphere 当做雪人的头部。 做好之后请按下 G 键抓取新加入的 UV Sphere,并移动鼠标,将新加入的球体移至原球体的上方,做好的画面如图8 所示: 图8:加入雪人头部后的画面 放置妥雪人的头部之后,请点选 Blender 工具下方的 Panels 菜单,再从出现的菜单选择 Editing 功能,如图9 所示: 图9:选择 [Panels] 菜单中的 Editing 功能的做法 Blender 工具的下方就会出现 [Link and materials] 窗口,请点选 [Link and materials] 窗口中的 [Set Smooth] 按钮,将雪人的头部设定成平滑的效果,然后再为雪人的头部设定浓淡效果。因为之前已经有设定妥名称为 Snow 的材质,所以可以直接套用名称为 Snow 的材质,不需要重新设定需要的材质的属性。请直接按下 F5 功能键,看到 [Links and Pipeline] 窗口后请点选 [Link to Object] 字段下方的箭头符号,再从出现的项目中选择名称为 Snow 的项目,表示要为雪人的头部套用和雪人身体一样的材质,如图10 所示: 图10:套用名称为 Snow 的材质到雪人头部的做法 设计妥雪人的头部之后请按下 A 键,解除雪人头部的选择状态,然后再执行 [Add | Mesh | UV Sphere] 功能,并按下 OK 键接受预设的设定,加入新的球体当做雪人的眼睛。 请使用鼠标的右键点中新加入的球体不放,从出现的菜单选择 [Transform | Scale] 功能,将新加入的球体缩小成眼睛的大小,然后选择 Panels 菜单中的 Editing 功能 (如图9 所示),再点选 [Link and Materials] 窗口 (如图3 所示) 中的 [Set Smooth] 按钮将新加入的球体设定成平滑的效果。 做好之后请按下 F5 功能键,开启 [Links and Pipeline] 窗口 (如图4 所示),设定新球体的浓淡效果。请按下 [Links and Pipeline] 窗口中的 [Add New] 按钮,再利用 [Material] 窗口 (如图5 所示) 中的 R、G、B 滑杆将 RGB 三种色彩的成份都设定成 0,将新加入的球体的色彩设定成黑色。 因为雪人需要两颗眼睛,所以请使用鼠标的右键点中新加入的球体不放,从出现的菜单选择 [Edit | Duplicate] 功能,对制作好的球体执行复制作动作,然后移动鼠标,将复制得到的复本移至编辑画面中其他的地方后放开鼠标,就可以得到第二个当做雪人眼睛的球体。 接下来请选择 Blender 工具左下角的 View 菜单 (View 菜单的外观如:) 中的 Side 功能,切换至侧视图检视,再使用鼠标的右键点中当做眼睛的球体不放,从出现的菜单选择 [Transform | Grab/Move] 功能,抓取球体后将当做眼睛的球体移至雪人头部的侧边,如图11 所示: 图11:设定雪人眼睛位置的侧视图 做好之后请使用相同的操作将第二个当做眼睛的球体也移至相同的位置。做好之后请再选择 Blender 工具左下角的 View 菜单 (View 菜单的外观如:) 中的 Front 功能,切换回正视图检视,如果眼睛的位置不正确,请使用鼠标的右键点中当做眼睛的球体不放,从出现的菜单选择 [Transform | Grab/Move] 功能,将当做眼睛的球体移至雪人头部适当的地方,设定妥眼睛位置的雪人正视图如图12 所示 图12:设定妥眼睛位置的雪人正视图 设定好眼睛之后我们还需要为雪人加入鼻子。首先请按下 A 键解除眼睛物体的选取状态,然后选择 [Add | Mesh | Cone] 功能,再按下 OK 键,接受预设的设定,Blender 工具就会加入一个圆锥体到编辑画面中。请使用鼠标的右键点中刚加入的圆锥体不放,从出现的菜单选择 [Transform | Rotate on Axis | Y Global] 功能,表示要利用 Y 轴为轴心旋转圆锥体,然后将圆锥体旋转成如图13 的样子: 图13:依据Y轴旋转后的圆锥体 做好之后请再使用鼠标的右键点中刚加入的圆锥体不放,从出现的菜单选择 [Transform | Rotate on Axis | Z Global] 功能,表示要利用 Z 轴为轴心旋转圆锥体,然后将圆锥体旋转成如图14 的样子: 图14:依据 Z 轴旋转后的圆锥体 完成旋转圆锥体的动作之后,请使用鼠标的右键点中刚加入的圆锥体不放,从出现的菜单选择 [Transform | Scale on Axis | X Global] 功能,表示要依据 X 轴进行放大缩小,然后将圆锥体缩小成如图15 的样子: 图15:依据X轴缩小后的圆锥体 做好之后请再使用鼠标的右键点中刚加入的圆锥体不放,从出现的菜单选择 [Transform | Scale on Axis | Z Global] 功能,表示要依据 Z 轴进行放大缩小,然后将圆锥体缩小成如图16 的样子: 图16:依据 Z 轴缩小后的圆锥体 完成设定雪人鼻子的大小和方向之后,请按下 F5 功能键,看到 [Links and Pipeline] 窗口后请点选 [Link to Object] 字段下方的箭头符号,再从出现的项目中选择名称为 Snow 的项目,表示要为雪人的鼻子套用和雪人身体一样的材质 (如图10 所示)。 设定妥雪人鼻子的材质之后,请选择 Blender 工具左下角的 View 菜单 (View 菜单的外观如:) 中的 Side 功能,切换到侧视图检视,再使用鼠标的右键点中雪人的鼻子不放,从出现的菜单选择 [Transform | Grab/Move] 功能,将雪人的鼻子移至雪人头部适当的地方,设定妥鼻子位置的雪人侧视图如图17 所示: 图17:设定妥鼻子位置的雪人侧视图 做好之后请再选择 Blender 工具左下角的 View 菜单 (View 菜单的外观如:) 中的 Front 功能,切换回正视图检视,再使用鼠标的右键点中雪人的鼻子不放,从出现的菜单选择 [Transform | Grab/Move] 功能,将雪人的鼻子移至雪人头部适当的地方,设定妥鼻子位置的雪人正视图如图18 所示: 图18:设定妥鼻子位置的雪人正视图 编辑好雪人的正视图之后,请选择 Blender 工具左下角的 View 菜单 (View 菜单的外观如:) 中的 Top 功能,切换至上视图检视,您可以视需要调整雪人鼻子的长度。设定好的雪人上视图如图19 所示: 图19:编辑妥的雪人上视图 如果各个角度的视图都没有问题,请再选择 Blender 工具左下角的 View 菜单 (View 菜单的外观如:) 中的 Front 功能,切换回正视图检视,并按下 A 键解除目前物体的选取状态,再按下 A 键选取所有的物体,然后按下 Ctrl+J 组合键,表示要将所选取的物体组合成一个单一的物体,屏幕上就会出现如图20 的画面,要求您确认是否要组合所选取的物体: 图20:要求确认是否要组合所选取的物体的画面 请按下 OK 键将选取的物体组合成单一的物体。做好之后请选取 [File | Save] 菜单,储存制作好的 3D 模型。储存成功之后请再按下 A 键选取雪人模型,然后选取 [File | Export | Autodesk FBX(.fbx)] 功能,表示要将所选取的 3D 模型导出成 Autodesk 的 FBX 文件格式供 XNA 游戏加载使用,屏幕上就会出现如图21 的画面,要求您选择欲汇出的内容: 图21:要求选择欲汇出的内容的画面 请确认所选取的是 [Selected Objects],意即要导出所有被选取的物体,然后按下 Export 键将所制作的 3D 模型导出成扩展名为 FBX 的档案。 读者可以视需要观看 [简易 3D 模型制作- 使用 Blender] 教学,了解制作 3D 模型的详细过程。 XNA 游戏与 3D 模型显示 完成制作 3D 模型的工作之后,我们就可以将制作好的 3D 模型交给以 XNA 为基础的游戏程序显示。 首先请启动 Visual Studio 2010 Express for Windows Phone,建立一个 [Windows Phone Game(4.0)] 型态的项目,并将前述制作好的 3D 模型加入到 Content Pipleline 项目中。做好之后请编辑项目中的 Game1.cs 档案,于 Game1 类别中加入以下的变量宣告,负责管理 3D 模型、3D 模型的显示位置、旋转角度、以及相机的位置: Model SnowMan;                                            //管理 3D 模型的變數Vector3 modelPosition = Vector3.Zero;                //記錄 3D 模型的顯示位置的變數float modelRotation = 0.0f;                        //記錄 3D 模型的旋轉角度的變數Vector3 cameraPosition = new Vector3(0.0f, 50.0f, 5000.0f);        //記錄相機位置的變數 宣告妥必要的变量之后,请将 Game1 类别的建构函式编辑成以下的样子,负责定义 XNA 游戏的窗口大小: public Game1(){    graphics = new GraphicsDeviceManager(this);    Content.RootDirectory = "Content";    graphics.PreferredBackBufferHeight = 800;                //設定遊戲視窗的高度為 800    graphics.PreferredBackBufferWidth = 480;                //設定遊戲視窗的寬度為 480    TargetElapsedTime = TimeSpan.FromTicks(333333);} 设定好游戏窗口的高度和宽度之后,请编辑 Game1 类别的 LoadContent 方法,加入加载 3D 模型资源的程序代码: protected override void LoadContent(){spriteBatch = new SpriteBatch(GraphicsDevice);SnowMan=Content.Load<Model>("SnowMan");            //載入名稱為 SnowMan 的 3D 模型資源} [注意] 读者必须视需要修改上述程序代码所指定的 3D 模型资源名称,以加载正确的 3D 模型资源。 加载妥资源之后,我们只要编辑 Game1 类别的 Draw 方法,加入显示 3D 模型的程序代码,就可以成功地显示所制作的 3D 模型。请将 Game1 类别的 Draw 方法编辑成以下的样子,执行显示 3D 模型的工作: protected override void Draw(GameTime gameTime){GraphicsDevice.Clear(Color.CornflowerBlue);    Matrix[] transforms = new Matrix[SnowMan.Bones.Count];     //依據 3D 模型內含的骨骼數量建立 Matrix 陣列    SnowMan.CopyAbsoluteBoneTransformsTo(transforms);        //取出骨骼的轉置矩陣資料    foreach (ModelMesh mesh in SnowMan.Meshes)        //逐一取出 3D 模型中所有的網格資料    {        foreach (BasicEffect effect in mesh.Effects)    //逐一設定網格資料的所有 Effect        {            effect.EnableDefaultLighting();                        //啟用預設光源            effect.World = transforms[mesh.ParentBone.Index] *Matrix.CreateRotationY(modelRotation) * Matrix.CreateTranslation(modelPosition);      //設定 Effect 的 World Space            effect.View = Matrix.CreateLookAt(cameraPosition, Vector3.Zero, Vector3.Up);              //設定 Effect 的 View Space            effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 100000.0f);            //計算 3D 模型投影到 2D 空間的投影位置        }        mesh.Draw();                                            //繪製網格的內容    }base.Draw(gameTime);} [说明] 3D 模型在 3D 的世界中有几种不同的空间表示法,3D 模型中的坐标点未经任何的转置处理前称为物体空间 (object space),当 3D 模型经过旋转,放大等转置处理之后就会得到世界空间 (world space),经过转置处理过的世界空间在显示给用户检视之前,还需要加入相机的位置和角度的处理,形成检视空间 (view space),最后的检视空间在显示之前必须投射到屏幕的 2 维空间,称为屏幕空间 (screen space),而屏幕空间显示的内容就是用户最终看到的结果。 做好之后请执行项目,您将会看到所制作 3D 雪人模型显示在游戏窗口的正中央,如图22 所示: 图22:XNA 游戏显示所制作的 3D 模型的情形 [范例影片观赏] 范例影片 》观看影片 》HTML5 影音播放 (推荐使用 Internet Explorer 9) [范例档案下载] Display3DModel.zip

游戏画面管理与切换控制

clock 八月 1, 2011 10:10 by author alex
  摘要 以 XNA 为基础的游戏程序虽然可以很容易地显示图形和文字供用户检视,并附予各种特殊效果,丰富游戏的内容,以达到吸引游戏用户的最终目标。但是 XNA Framework 不像一般支持应用程序开发的 .NET Framework、.NET Compact Framework、或是 Silverlight Framework,对应用程序编程接口的开发提供完整的支持,XNA Framework 主要的支持在游戏功能的设计,在支持应用程序编程接口方面,例如窗口、对话框、菜单、工具栏、状态栏等和用户沟通的接口部分就需要多投入一点心力,才能建置具功能选单、场景切换、或是允许使用者设定游戏主角、游戏内容、和游戏难度的完整游戏。在这一篇文章中,我们将要介绍以 XNA 为基础的游戏程序的画面开关技巧,以及 XNA Framework 在与使用者沟通方面的支持。 游戏画面管理与开关 和一般的应用程序一样,以 XNA 为基础的游戏程序也需要功能选单供游戏用户选择喜好设定,包括游戏的背景图案、主角人物的图片、对战的对象、或是游戏的难度。每一种设定的设定画面都可能有更进一步的细部设定,造成游戏程序必须协助用户无障碍地在各种设定画面与游戏进行画面之间的切换,不会因为对游戏的使用者造成操作上的困扰。 游戏程序和一般的应用程序的定位不同,游戏程序通常不需要用户输入很多的数据,甚至允许用户不需要执行任何输入的动作就可以进入游戏程序设定的游戏场景,所以在支持画面设计与管理的功能方面较为薄弱,例如支持设计 Windows Phone 7 游戏程序开发的 XNA Framework 不像支持设计一般的 Windows Phone 7 应用程序的 Silverlight Framework 可以很容易地利用 XAML 文件与 PhoneApplicationPage 类别建立和供使用者操作的画面,和使用者进行沟通,或是读取用户设定或输入的数据,并利用 NavigationService 类别提供的 Navigate、GoForward、和 GoBack 等方法进行切换,开发以 XNA 为基础的游戏程序必须投入较多的心力在管理游戏程序的游戏画面、菜单画面、或是设定画面,以提供完整的游戏程序功能供用户使用。 认识 Game State Management 范例 要为以 XNA 为基础的游戏程序加上菜单选单,设定画面,或是场景切换功能,除了自行开发以外,也可以利用发表于 APP HUB 网站的 Game State Management 范例程序 (网址:http://create.msdn.com/en-US/education/catalog/sample/game_state_management),此范例程序提供三种游戏平台的范例,分别是 Windows Phone 7 平台的范例:GSMSample_4_0_PHONE.zip;Windows 平台和 XBOX 游戏机平台的范例:GSMSample_4_0_WIN_XBOX.zip,有兴趣的读者可以自行下载 Game State Management 范例程序提供的原始码,为上述三种平台的游戏程序加入游戏画面管理与切换的功能。如果读者有需要开发网络游戏程序,可以参考发表于 APP HUB 网站的 Network Game State Management 范例程序(网址:http://create.msdn.com/en-US/education/catalog/sample/network_game_state_management)。 [注意] 使用 Game State Management 范例程序提供的原始码必须遵守 Microsoft Permissive License 授权规范,详细的授权规定请参考 Microsoft Permissive License (Ms-PL) 文件的说明。 欲使用 Game State Management 范例程序提供的原始码,请先下载正确的范例版本,解压缩之后使用 Visual Studio 2010 Express 开启扩展名为 .sln 的方案档,您将可以在 [Solution Explorer] 窗口中看到名称为 GameStateManagementSample (Phone) 的游戏程序项目,以及名称为 Content 的 Content Pipeline 项目。 名称为 GameStateManagementSample (Phone) 的游戏程序项目中名称为 ScreenManager 的文件夹中有三个重要的原始程序档案,分别是负责管理游戏画面的 ScreenManager.cs,可以协助处理使用者输入的 InputState.cs,以及做为游戏程序画面基类的 GameScreen.cs。ScreenManager 文件夹底下的原始程序档案的用途可以参考表1的说明: 表1:ScreenManager 文件夹底下的原始程序档案的用途 原始程序文件名 定义的类别名称 用途 ScreenManager.cs ScreenManager 提供管理多个 GameScreen 类别的对象的功能,负责呼叫 GameScreen 类别的对象的 Update 方法更新画面的状态,呼叫 GameScreen 类别的对象的 Draw 方法更新画面显示的内容,以及将使用者的输入的动作转交给目前显示的 GameScreen 类别的对象。 InputState.cs InputState 负责处理用户透过键盘、游戏控制器、和触控面板的输入操作。 GameScreen.cs GameScreen 代表一个游戏画面,包括游戏的主菜单、设定菜单、和游戏的主画面,都是一个 GameScreen 类别的对象。 [说明] ScreenManager 类别继承自 XNA Framework 内建的 DrawableGameComponent 类别。XNA Framework 内建的 DrawableGameComponent 类别和 GameComponent 类别都是支持模块化游戏设计的基类,两者皆支持 Update 方法,以执行更新游戏状态的动作,DrawableGameComponent 另外支持 LoadContent、UnloadContent、以及 Draw 方法,做为加载游戏资源、释放资源、以及绘制游戏内容之用。以 XNA 为基础的游戏程序可以将游戏的人物和模块制作成继承自 DrawableGameComponent 类别或 GameComponent 类别的衍生类别,将更新游戏状态、加载/释放资源、与绘制游戏内容的工作封装在类别中,使游戏程序具备更优良的结构。 [提示] 每一个继承自 GameScreen 类别的衍生类别可以经由覆写 (override) LoadContent 方法加载画面需要用到的资源,覆写 UnloadContent 方法执行释放资源的动作,覆写 Update 方法以执行更新画面状态的动作,覆写 HandleInput 方法以处理使用者的输入操作,覆写 Draw 方法以显示画面的内容。除此之外,GameScreen 类别另外提供允许衍生类别覆写的 Serialize 方法和 Deserialize 方法,执行储存画面状态,或是读回之前储存的画面状态的动作。如果有需要控制画面转场的效果,可以设定 GameScreen 类别的 TransitionOnTime 属性和 TransitionOffTime 属性,将 TransitionOnTime 属性和 TransitionOffTime 属性设定成 0 代表画面切换不需要转场效果。如果画面要以突现的方式盖在目前的画面之上,可以将 GameScreen 类别的 IsPopup 属性设定成 true。 除了名称为 ScreenManager 的文件夹以外,名称为 GameStateManagementSample (Phone) 的游戏程序项目中还有一个名称为 Screens 的文件夹,位于此文件夹底下的是 Game State Management 范例程序提供的现成游戏程序画面,您可以直接使用 GameStateManagementSample (Phone) 的游戏程序项目提供的游戏画面,或是自行开发继承自 GameScreen.cs 定义的 GameScreen 类别的衍生类别,定义所开发的游戏程序需要的画面。 Screens 文件夹底下的原始程序档案的用途可以参考表2 的说明: 表2:Screens 文件夹底下的原始程序档案的用途 原始程序文件名 定义的类别名称 用途 BackgroundScreen.cs BackgroundScreen 做为游戏背景的画面。 GameplayScreen.cs GameplayScreen 游戏的执行画面。 LoadingScreen.cs LoadingScreen 显示加载中,请用户稍候的讯息的画面。 MainMenuScreen.cs MainMenuScreen 主菜单选单画面。 MenuEntry.cs MenuEntry 代表菜单中的功能选项的类别。 MenuScreen.cs MenuScreen 所有的菜单画面的基类。 MessageBoxScreen.cs MessageBoxScreen 显示讯息供用户检视的画面。 OptionsMenuScreen.cs OptionsMenuScreen 提供设定画面供使用者进行喜好设定的画面。 PlayerIndexEventArgs.cs PlayerIndexEventArgs 菜单被用户选中时引发的Selected事件伴随的事件参数。 [提示] 请注意 ScreenManager 文件夹和 Screens 文件夹中的某些类别需要使用到 Content Pipeline 项目中的游戏资源,也就是您必须在 Content Pipeline 项目中准备好必要的游戏资源,否则光有类别的原始码还是无法制作出游戏程序需要使用的游戏画面。 需要使用到资源的源代码档案包括负责管理游戏画面的 ScreenManager.c 会用到 Content Pipeline 项目中名称为 blank 的图片和 menufont 字型定义文件;负责显示讯息窗口和用户沟通的 MessageBoxScreen.cs 会用到 Content Pipeline 专案中名称为 gradient 的图片;负责扮演游戏主窗口的 GameplayScreen.cs 会用到 Content Pipeline 项目中名称为 gamefont 字型定义文件,而当做背景窗口的 BackgroundScreen.cs 则需要用到 Content Pipeline 项目中名称为 background 的图片。请注意如果游戏程序需要改变上述置于 Content Pipeline 项目的游戏资源的名称,必须一并修改使用到这些资源的源代码,否则所开发的游戏程序将无法执行。 使用 Game State Management 范例提供的原始码管理游戏程序的画面 了解 Game State Management 范例提供的原始程序档案的功能和用途之后,我们就可以利用范例制作好的类别来开发具备菜单与画面切换功能的游戏程序了。 首先请启动 Visual Studio 2010 Express for Windows Phone,建立一个 [Windows Phone Game(4.0)] 型态的项目,然后将 Game State Management 范例的游戏程序项目中的 ScreenManager 文件夹和 Screens 文件夹中的所有档案加入到所建立的游戏程序项目中,将 Game State Management 范例的 Content Pipeline 项目中的 background.png、blank.png、gradient.png、gamefont.spritefont、和menufont.spritefont 加入到所建立的游戏程序项目附属的 Content Pipeline 项目中。 做好之后请编辑 Game1 类别,于原始程序档案的最前面引入以下的名称空间: using GameStateManagement; 然后于 Game1 类别中宣告以下的变量,负责管理游戏程序需要使用的所有画面: ScreenManager screenManager;                //管理遊戲畫面的 ScreenManager 類別的物件 再把 Game1 类别的建构函式编辑成以下的样子,负责设定游戏窗口的宽度和高度,建立负责管理游戏画面的 ScreenManager 类别的对象: public Game1(){graphics = new GraphicsDeviceManager(this);    Content.RootDirectory = "Content";graphics.PreferredBackBufferWidth = 480;                //設定遊戲視窗的寬度為 480    graphics.PreferredBackBufferHeight = 800;                //設定遊戲視窗的高度為 800    screenManager = new ScreenManager(this);              //建立 ScreenManager 類別的物件Components.Add(screenManager);        //將 ScreenManager 類別的物件加入到 Components 集合if (!screenManager.DeserializeState())           //如果沒有前一次執行儲存的畫面狀態    {        screenManager.AddScreen(new BackgroundScreen(), null);                //建立遊戲背景畫面,並交給 ScreenManager 管理        screenManager.AddScreen(new MainMenuScreen(), null);                //建立主功能表畫面,並交給 ScreenManager 管理    }    TargetElapsedTime = TimeSpan.FromTicks(333333);} 请注意,游戏程序执行时需要用到的所有画面都必须交给 ScreenManager 管理。 [提示] 因为 ScreenManager 类别继承自 DrawableGameComponent 类别,当 DrawableGameComponent 类别的衍生类别的对象加入到 Game1 类别的 Components 集合之后,每次 Game1 类别的 LoadContent 方法、Update 方法、和 Draw 方法被呼叫的时候,DrawableGameComponent 类别的衍生类别的对象的 LoadContent 方法、Update 方法、和 Draw 方法也会被呼叫,让 DrawableGameComponent 类别的衍生类别的对象能够加载游戏资源,更新状态,并绘制对象的内容。 编辑妥 Game1 类别的建构函式之后,请覆写 OnExiting 方法,在游戏程序结束执行时将画面的状态储存下来,以便下一次执行游戏程序的时候还原到上一次结束执行时的画面。请为 Game1 类别加入以下的 OnExiting 方法: protected override void OnExiting(object sender, EventArgs args){    screenManager.SerializeState();    //呼叫 screenManager 的 SerializeState 方法儲存遊戲畫面    base.OnExiting(sender, args);} 最后我们必须编辑 Game1 类别的 Update 方法中,将判断使用者是否按下 Windows Phone 7 智能型手机左下方的 Back 硬件按键的程序代码批注掉,因为加入画面控制功能的游戏程序在用户按下智能型手机左下方的 Back 硬件按键时,如果有上一个游戏画面,则必须回到上一个游戏画面,而不是结束游戏程序的执行,编辑妥的 Update 方法如下: protected override void Update(GameTime gameTime){    //if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)    //    this.Exit();    base.Update(gameTime);} 做好之后请执行游戏程序项目,您将会看到游戏程序提供的主菜单,如图1 所示: 图1:游戏程序提供的主菜单 按下画面中的 [Options] 功能,您就会看到游戏程序的设定画面,点选设定画面中的菜单,可以看到仿真设定值改变的效果,如图2 所示: 图2:游戏程序的设定画面 按下 Windows Phone 7 智能型手机左下方的 Back 硬件按键回到图1的画面,再点选 [Play Game] 功能,您就会看到一个简单的游戏执行的情形,如图3 所示: 图3:简单的游戏执行的情形 按下 Windows Phone 7 智能型手机左下方的 Back 硬件按键可以回到主菜单,再按一次可以结束游戏程序的执行。 [Game State Management 范例使用要诀] 因为 Game State Management 范例提供的原始程序档案中,当做所有游戏画面基类的 GameScreen 类别内容储存/读回游戏画面状态的功能,所以在游戏画面管理和切换的功能尚未制作完善之前,先将制作好的游戏程序部署至 Windows Phone 7 Emulator (仿真器) 执行,如果功能不正确,则将仿真器关闭,丢弃储存的游戏画面状态,待程序修改妥后,再将修改后的程序重新部署到仿真器执行。 您可以视需要修改 MainMenuScreen.cs 的程序代码,提供不同的菜单供用户操作,不要忘了请一并编辑 Content Pipeline 项目中的 menufont.spritefont 字型定义文件,定义菜单需要显示的文字。如果需要为所开发的游戏程序提供中文菜单,请参考 [图形特效与文字显示] 一文的说明,于 Content Pipeline 项目中的 menufont.spritefont 字型定义文件定义好欲显示的中文字和字型规格。 欲修改游戏程序的主菜单,请编辑 MainMenuScreen.cs 源文件的 MainMenuScreen 类别的建构函式中建立 MenuEntry 类别的对象的语法,如下: public MainMenuScreen() : base("Main Menu"){    MenuEntry playGameMenuEntry = new MenuEntry("Play Game");    MenuEntry optionsMenuEntry = new MenuEntry("Options");    playGameMenuEntry.Selected += PlayGameMenuEntrySelected;    optionsMenuEntry.Selected += OptionsMenuEntrySelected;    MenuEntries.Add(playGameMenuEntry);    MenuEntries.Add(optionsMenuEntry);} 当然,修改建立 MenuEntry 类别的对象的语法必须一并修改 MenuEntry 类别的 Selected 事件的事件处理程序,如下: void PlayGameMenuEntrySelected(object sender, PlayerIndexEventArgs e){     LoadingScreen.Load(ScreenManager, true, e.PlayerIndex, new GameplayScreen());}void OptionsMenuEntrySelected(object sender, PlayerIndexEventArgs e){    ScreenManager.AddScreen(new OptionsMenuScreen(), e.PlayerIndex);} 请注意 [Options] 菜单的 Selected 事件的事件处理程序会直接显示 OptionsMenuScreen 类别定义的内容,而 [Play Game] 菜单的 Selected 事件的事件处理程序则会先显示 LoadingScreen 类别定义的内容,让使用者先看到 [Loading…] 的文字,然后才看到 GameplayScreen 类别定义的内容。 以 GameplayScreen 类别为例,其建构函式就有设定 TransitionOnTime 属性和 TransitionOffTime 属性,表示切换到 GameplayScreen 画面和切换离开 GameplayScreen 画面都要显示转场效果。GameplayScreen 类别的建构函式如下: public GameplayScreen(){    TransitionOnTime = TimeSpan.FromSeconds(1.5);    TransitionOffTime = TimeSpan.FromSeconds(0.5);} 最后我们只要将游戏的功能制作在扮演游戏主画面的 GameplayScreen.cs 档案中,或置换掉 GameplayScreen.cs 档案的内容,就可以完成提供主菜单和游戏设定画面的游戏程序了。 利用 Guide 类别与游戏使用者沟通 以 XNA 为基础的游戏程序除了可以利用主菜单和设定画面与用户沟通之外,也可以利用 Guide 类别提供的功能和用户沟通,包括显示讯息画面供用户检视和显示输入画面协助用户输入资料等常用的用户接口。 欲利用 Guide 类别提供协助用户输入数据的操作画面并显示讯息画面,请先于原始程序档案的最前面引入以下的名称空间: using Microsoft.Xna.Framework.GamerServices;using System;using System.Collections.Generic; 然后于类别中宣告以下的变量 string typedText;                                //存放使用者輸入的資料的變數 需要显示协助用户输入数据的操作画面的时候,只要透过下列的程序代码,就可以利用 Guide 类别的 BeginShowKeyboardInput 方法显示供用户输入数据的画面: Guide.BeginShowKeyboardInput(PlayerIndex.One,                        "Here's your Keyboard", "Type something...",                        ((typedText == null) ? "" : typedText),                        GetTypedChars, null);            //顯示供使用者輸入資料的畫面 当使用者完成输入并按下输入画面中的 [OK] 键时,就会执行到名称为 GetTypedChars 的方法,由 GetTypedChars 方法呼叫 Guide 类别的 EndShowKeyboardInput 方法取得使用者输入的内容,做法如下: protected void GetTypedChars(IAsyncResult r){    typedText = Guide.EndShowKeyboardInput(r);            //取得使用者輸入的內容} 做好之后请执行游戏程序,执行显示协助用户输入数据的功能,您就会看到如图4 所示的画面: 图4:协助用户输入数据的画面 如果游戏程序需要显示讯息和用户沟通,要求使用者做决策,就可以利用 Guide 类别的 BeginShowMessageBox 方法显示讯息画面供用户做决策。例如以下就是 Guide 类别的 BeginShowMessageBox 方法的使用技巧: string msg = "Quit Game?";                                //準備供使用者檢視的訊息List<string> MBButtons = new List<string>();            //準備供使用者選擇的按鍵的集合MBButtons.Add("Yes");                                //加入 Yes 鍵到MBButtons集合MBButtons.Add("No");                                    //加入 No 鍵到MBButtons集合Guide.BeginShowMessageBox("Game Over", msg, MBButtons, 0,                    MessageBoxIcon.Alert, GetMBResult, null);//顯示訊息畫面和使用者溝通 当用户按下讯息画面中的按键时会呼叫到名称为 GetMBResult 的方法,由 GetMBResult 方法判断用户的按键选择。以下的 GetMBResult 方法会呼叫 Guide 类别的 EndShowMessageBox 方法取得用户的按键状态: protected void GetMBResult(IAsyncResult r){    int? b = Guide.EndShowMessageBox(r);                    //取得使用者的按鍵狀態    if (b == 0)                                        //使用者按下第一個按鍵    {        //user click Yes    }    else                                            //使用者按下第二個按鍵    {        //user click No    }} 请注意呼叫 Guide 类别的 EndShowMessageBox 方法取得的结果是一个整数,其内容值为 0 代表用户按下第一个按键,其内容值为 1 代表用户按下第二个按键,其内容值为 2 代表用户按下第三个按键,以此类推。 做好之后请启动游戏程序,执行显示讯息和用户沟通的功能,您就会看到图5 的画面: 图5:利用 Guide 类别显示讯息和用户沟通的画面 [范例下载] XNAGameWithMenu.zip

图形特效与文字显示

clock 八月 1, 2011 10:05 by author alex
  摘要 使用 XNA Framework 设计游戏程序的时候可以利用 SpriteBatch 类别提供的功能显示游戏的内容供用户检视,SpriteBatch 类别提供了丰富的功能协助游戏程序绘制图画和文字,包括对所输出的图画和文字加入特殊的效果,例如放大/缩小、旋转、设定透明度、水平/垂直翻转、以及图层深度等游戏程序常常需要表现的效果。在这一篇文件中,我们将会为大家介绍利用 SpriteBatch 类别为游戏程序加上特殊效果的多种做法。 认识 SpriteBatch 类别 XNA Framework 支持的 SpriteBatch 类别可以协助游戏程序输出 2 维的图片和文字,并提供批次输出的功能,避免频繁地输出游戏的内容造成闪烁的现象。 [提示] 虽然以 XNA 为基础的 3D 游戏输出 3 维的图形内容时不需要依靠 SpriteBatch 类别提供的功能,但是当 3D 游戏需要提供文字的功能选单供用户选择的时候,还是需要使用到 SpriteBatch 类别提供的文字输出功能。 表 1 所示为 SpriteBatch 类别常用的属性 表 1 :SpriteBatch 类别常用的属性 属性名称 说明 GraphicsDevice 取得游戏程序所使用的 GraphicsDevic 类别的对象的属性。 表 2 所示为 SpriteBatch 类别常用的方法: 表 2 :SpriteBatch 类别常用的方法 方法名称 说明 Begin 宣告批次绘图的动作开始。 Draw 绘制 2 维图案。 DrawString 绘制文字。 End 宣告批次绘图的动作结束,将从呼叫 Begin 方法之后绘制的所有内容输出到游戏窗口,并将 GraphicsDevice 的状态还原到呼叫 Begin 方法之前的状态。 SpriteBatch 类别的功能看似简单,但是负责绘制 2 维图案的 Draw 方法有高达7个不同的多载 (Overload) 版本,负责绘制文字的 DrawString 方法也有多达 6 个不同的多载版本,让游戏程序可以经由传入不同数量的参数,控制方法执行的结果,如果再加计列举型态的参数的内容值选择,就可以组合出各种繁复的变化,满足游戏程序就变更游戏程序内容的需求。 SpriteBatch 类别与图形特效支持 SpriteBatch 类别支持绘制 2 维图案的 Draw 方法一共有以下 7 个不同的多载版本: SpriteBatch.Draw(texture, destinationRectangle, color) SpriteBatch.Draw(texture, destinationRectangle, sourceRectangle, color) SpriteBatch.Draw(texture, destinationRectangle, sourceRectangle, color, rotation, origin, effects, layerDepth) SpriteBatch.Draw(texture, position, color) SpriteBatch.Draw(texture, position, sourceRectangle, color) SpriteBatch.Draw(texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth) SpriteBatch.Draw(texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth) 其需要用到的参数请参考表3的详细说明: 表 3 :SpriteBatch 类别的 Draw 方法需要使用的参数 参数名称 说明 texture 型态为 Texture2D 类别的参数,负责管理欲绘制的 2 维图案。 destinationRectangle 负责描述欲绘制在游戏窗口上的物体的矩形。 color 负责控制欲绘制的图形的色调的参数,传入 Color.White 表示不改变欲显示的物体的色调。 sourceRectangle 指定欲用来取用来源对象的矩形。 scale 图案放大/缩小的倍数。 rotation 旋转角度。 origin 旋转图形时所依据的圆心。 effects 设定成 SpriteEffects.FlipHorizontally 表示要水平翻转图案,设定成 SpriteEffects.FlipVertically 表示要垂直翻转图案。 layerDepth 图层深度。0 代表前景层,1 代表背景层,可以搭配呼叫 SpriteBatch 类别的 Begin 方法传入的 SpriteSortMode 参数控制是否要对欲绘制的内容依 layerDepth 的内容值排序。 position 指定欲绘制的图案的左上角点坐标。 光是靠 SpriteBatch 类别的 Draw 方法的众多参数,就能够创造繁复的游戏效果,例如透过 color 参数设定图案的色调和透明度,透过 rotation 参数指定旋转图案的角度,利用 origin 参数设定图案旋转的圆心坐标,利用 effects 参数水平翻转或垂直翻转图案,利用 scale 参数指定放大/缩小图案的倍数,或是利用 layerDepth 参数设定图案要显示在那一个图层。 要利用 SpriteBatch 类别的 Draw 方法的各个参数创造繁复的游戏效果,首先请启动 Visual Studio 2010 Express for Windows Phone,并建立 [Windows Phone Game(4.0)] 型态的项目,然后将游戏程序欲显示的图案加入到 Content Pipeline 项目,以便让游戏程序加载显示。 要为欲显示的图案加入各种不同的特殊效果,请先于 Game1 类别中宣告以下的变量,负责管理欲显示的图案,以及图案的显示的位置: Texture2D Mario; //管理欲顯示的圖案的變數 Vector2 MarioPosition; //存放圖案顯示的位置的變數 然后将Game1类别的建构函式编辑成以下的样子,负责设定游戏窗口的高度和宽度: public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferHeight = 800; //設定遊戲視窗的高度為800 graphics.PreferredBackBufferWidth = 480; //設定遊戲視窗的寬度為480 TargetElapsedTime = TimeSpan.FromTicks(333333); } 设定妥游戏窗口的高度和宽度之后请编辑 Game1 类别的 LoadContent 方法,负责加载游戏程序欲使用的资源,编辑好的 LoadContent 方法如下: protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); Mario = Content.Load<Texture2D>("Mario"); //從Content Pipeline專案載入欲顯示的圖案 MarioPosition = new Vector2(0, 0); //設定圖案要顯示在遊戲視窗的最左上角 } 最后我们只要在 Game1 类别的 Draw 方法中呼叫 SpriteBatch 的 Draw 方法,传入适当的参数,就可以显示出各种特殊的效果,例如以下的 Draw 方法便会旋转、放大/缩小、水平/垂直翻转、改变图案色调、以及设定图案的透明度: protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); //宣告批次繪圖動作開始 spriteBatch.Draw(Mario, MarioPosition, Color.White); //顯示未加上特殊效果的圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X + Mario.Width*2, MarioPosition.Y + Mario.Height), null, Color.White, MathHelper.ToRadians(180), new Vector2(0, 0), 1, SpriteEffects.None, 0); //將圖案旋轉180度 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X + Mario.Width * 2, 0), null, Color.White, 0, new Vector2(0, 0), 1, SpriteEffects.FlipHorizontally, 0); //將圖案水平翻轉 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X , MarioPosition.Y + Mario.Height), Color.Red); //以紅色的色調顯示圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X + Mario.Width, MarioPosition.Y + Mario.Height), Color.Blue); //以藍色的色調顯示圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X + Mario.Width * 2, MarioPosition.Y + Mario.Height), Color.Green); //以綠色的色調顯示圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X, MarioPosition.Y + Mario.Height*2), null, Color.White, 0, new Vector2(0, 0), 1, SpriteEffects.None, 0); //以圖案原始大小顯示圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X + Mario.Width, MarioPosition.Y + Mario.Height * 2), null, Color.White, 0, new Vector2(0, 0), 0.5f, SpriteEffects.None, 0); //以原圖一半的大小顯示圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X + Mario.Width*1.5f, MarioPosition.Y + Mario.Height * 2), null, Color.White, 0, new Vector2(0, 0), 2, SpriteEffects.None, 0); //以原圖兩倍的大小顯示圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X, MarioPosition.Y + Mario.Height*4), null, new Color(255, 255, 255, 255), 0, new Vector2(0, 0), 1, SpriteEffects.None, 0); //以原始透明度顯示圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X + Mario.Width, MarioPosition.Y + Mario.Height * 4), null, new Color(255, 255, 255, 170), 0, new Vector2(0, 0), 1, SpriteEffects.None, 0); //以約2/3的透明度顯示圖案 spriteBatch.Draw(Mario, new Vector2(MarioPosition.X + Mario.Width*2, MarioPosition.Y + Mario.Height * 4), null, new Color(255, 255, 255, 85), 0, new Vector2(0, 0), 1, SpriteEffects.None, 0); //以約1/3的透明度顯示圖案 spriteBatch.End(); //宣告批次繪圖動作結束 base.Draw(gameTime); } 做好之后请执行项目,您就会看到如图1的画面,看到传入不同的参数内容给 SpriteBatch 类别的 Draw 方法显示的内容的结果: 图1:传入不同的参数内容控制SpriteBatch类别的Draw方法显示的内容的结果 注:本文所使用的人物图案来源来自:http://www.emutalk.net/showthread.php?t=43273。 [说明] 呼叫 SpriteBatch 类别的 Draw 方法执行旋转图案的时候而要传入的旋转角度的单位是弪度量,而不是惯用的度度量。以 XNA 为基础的游戏程序可以利用 MathHelper 类别的 ToRadians 方法指定的度度量角度转换成弪度量,再交给 SpriteBatch 类别的 Draw 方法执行旋转图案的动作。 MathHelper 类别是 XNA Framework 中提供高效能数学运算功能的类别,负责提供游戏执行时需要的数学运算。有关 MathHelper 类别常用的属性可以参考表 4 的说明: 表 4 :MathHelper 类别常用的属性 属性名称 说明 E 代表数学上的指数常数 e (亦称欧拉数 – Euler's Number )。 Log10E 代表以 10 为基底的对数 e 。 Log2E 代表以 2 为基底的对数 e 。 Pi 代表圆周率(pi)。 PiOver2 代表/2。 PiOver4 代表/4。 TwoPi 代表2。 MathHelper 类别常用的方法请参考表 5 的说明: 表 5:MathHelper 类别常用的方法 方法名称 说明 Clamp 限制数值必须介于指定的数值范围。 Distance 计算两个数值之间的距离。 Hermite 执行 Hermite Spline 内插法。 Lerp 于两点之间执行线性内插。 Max 取得两个数值中较大者的内容值。 Min 取得两个数值中较小者的内容值。 SmoothStep 利用 cubic 方程式计算两个数值间的内插值。 ToDegrees 将弪度量转换成度度量。 ToRadians 将度度量转换成弪度量。 WrapAngle 限制角度必须介于π和-π之间。 [提示] 您只要善用 Game1 类别的 Update 方法,动态更新传递给 SpriteBatch 类别的 Draw 方法的参数内容值,就可以很容易地令所开发的游戏程序动态旋转图案、动态放大/缩小图案、或是动态令图案逐渐变成透明/不透明。 SpriteBatch 类别与文字输出支持 SpriteBatch 类别除了能够执行显示图案的功能以下,还能够协助游戏程序显示文字,当游戏程序需要显示菜单供用户选择,或是需要显示游戏的状态(包括分数和游戏的关卡)时,就会需要使用到 SpriteBatch 类别支持显示文字的功能。 游戏程序可以利用 SpriteBatch 类别的 DrawString 方法执行显示文字的动作,和 Draw 方法支持多种多载版本一样,SpriteBatch 类别支持的 DrawString 方法一共有以下 6 个不同的多载版本: SpriteBatch.DrawString(spriteFont, text, position, color) SpriteBatch.DrawString(spriteFont, text, position, color, rotation, origin, scale, effects, layerDepth) SpriteBatch.DrawString(spriteFont, text, position, color, rotation, origin, scale, effects, layerDepth) SpriteBatch.DrawString(spriteFont, text, position, color) SpriteBatch.DrawString(spriteFont, text, position, color, rotation, origin, scale, effects, layerDepth) SpriteBatch.DrawString(spriteFont, text, position, color, rotation, origin, scale, effects, layerDepth) 其需要用到的参数请参考表6的详细说明: 表 4 :SpriteBatch 类别的 DrawString 方法需要使用的参数 参数名称 说明 spriteFont 型态为 SpriteFont 类别的参数,负责记录欲输出的文字的定义。 text 欲输出的文字,其型态可以是 String 类别或 StringBuilder 类别。 position 指定欲输出的文字的左上角点坐标。 color 负责控制欲输出的文字的色调的参数,传入 Color.White 表示不改变欲显示的文字的色调。 rotation 依据输出的文字的圆心旋转 2 维输出的文字。 origin 旋转文字时所依据的圆心。 scale 文字放大/缩小的倍数。 effects 设定成 SpriteEffects.FlipHorizontally 表示要水平翻转所显示的文字,设定成 SpriteEffects.FlipVertically 表示要垂直翻转所显示的文字。 layerDepth 图层深度。0 代表前景层,1 代表背景层,可以搭配呼叫 SpriteBatch 类别的 Begin 方法传入的 SpriteSortMode 参数控制是否要对欲绘制的内容依 layerDepth 的内容值排序。 [提示] 呼叫 SpriteBatch 类别的 DrawString 方法需要的参数和 Draw 方法需要的参数几乎完全相同,所代表的意义和使用方式也都一样。 了解 SpriteBatch 类别的 DrawString 方法的基本功能之后,接下来我们就要为游戏程序加入显示文字的功能。 要显示文字,请先使用鼠标的右键点中 [Solution Explorer] 窗口中的 Content Pipeline 项目名称,再从出现的菜单选择 [Add | New Item] 功能,屏幕上就会出现如图 2 的画面,要求您选择欲新增的资源项目: 图 2:要求选择欲新增的资源项目的画面 请于中间窗口选择 [Sprite Font] 项目,于 [Name] 字段输入字型定义档案的名称,做好之后按下 [Add] 键,执行新增 SpriteFont 资源到 Content Pipeline 项目的动作,Visual Studio 2010 Express for Windows Phone 就会为 Content Pipeline 项目加入字型定义档案,其内容如下: <?xml version="1.0" encoding="utf-8"?><!--This file contains an xml description of a font, and will be read by the XNAFramework Content Pipeline. Follow the comments to customize the appearanceof the font in your game, and to change the characters which are available to drawwith.--><XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">  <Asset Type="Graphics:FontDescription">    <!--    Modify this string to change the font that will be imported.    -->    <FontName>Segoe UI Mono</FontName>    <!--    Size is a float value, measured in points. Modify this value to change    the size of the font.    -->    <Size>14</Size>    <!--    Spacing is a float value, measured in pixels. Modify this value to change    the amount of spacing in between characters.    -->    <Spacing>0</Spacing>    <!--    UseKerning controls the layout of the font. If this value is true, kerning information    will be used when placing characters.    -->    <UseKerning>true</UseKerning>    <!--    Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",    and "Bold, Italic", and are case sensitive.    -->    <Style>Regular</Style>    <!--    If you uncomment this line, the default character will be substituted if you draw    or measure text that contains characters which were not included in the font.    -->    <!-- <DefaultCharacter>*</DefaultCharacter> -->    <!--    CharacterRegions control what letters are available in the font. Every    character from Start to End will be built and made available for drawing. The    default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin    character set. The characters are ordered according to the Unicode standard.    See the documentation for more information.    -->    <CharacterRegions>      <CharacterRegion>        <Start>&#32;</Start>        <End>&#126;</End>      </CharacterRegion>    </CharacterRegions>  </Asset></XnaContent> 您可以利用 <FontName> 卷标指定欲显示的文字所使用的字型,利用 <Size> 卷标指定欲显示的文字的字号,利用 <Style> 卷标设定字型的样式,包括 Bold (粗体)和 Italic (斜体),利用 <CharacterRegion> 卷标指定欲显示的文字范围,包括利用 <Start> 卷标定义欲显示的字符中第一个字符的 Unicode ,利用 <End> 卷标定义欲显示的字符中最后一个字符的 Unicode 。例如以下就是一个 SpriteFont 字型定义文件的范例,指定欲使用大小为 18 的 Courier New 粗体字型,而且开头字符为 a,终止符为 z: <?xml version="1.0" encoding="utf-8"?><XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">  <Asset Type="Graphics:FontDescription">    <FontName>Courier New</FontName>    <Size>18</Size>    <Spacing>0</Spacing>    <UseKerning>true</UseKerning>    <Style>Bold</Style>    <CharacterRegions>      <CharacterRegion>        <Start>a</Start>        <End>z</End>      </CharacterRegion>       </Asset></XnaContent> [注意] 请注意 SpriteFont 字型定义文件是一个内容大小写视为相异的 XML 文件,编辑字型定义的时候请特别注意大小写不要写错。除此之外,如果游戏程序需要显示汉字字符,因为汉字字符的 Unicode 内容值并不一定会连续,所以我们必须为每一个欲显示的中文字定义一个 <CharacterRegion> 段落,例如以下的字型定义,便会指定游戏程序可以显示 [中文]这两个中文字: <CharacterRegions>      <CharacterRegion>        <Start>中</Start>        <End>中</End>      </CharacterRegion>      <CharacterRegion>        <Start>文</Start>        <End>文</End>      </CharacterRegion></CharacterRegions> 定义好游戏程序欲显示的中文字型定义之后,就可以利用 SpriteFont 类别提供的功能管理游戏程序欲显示的字型。SpriteFont 类别常用的属性可以参考表 5 的说明: 表 5:SpriteFont 类别常用的属性 属性名称 说明 Characters 取得 SpriteFont 类别的对象管理的所有字符。 DefaultCharacter 指定默认字符。如果 DefaultCharacter 属性的内容值设定为异于 null 的内容值,则当游戏程序欲显示 SpriteFont 字型定义文件中未定义的字符时,就会自动显示 DefaultCharacter 属性指定的字符。如果 DefaultCharacter 属性的内容值设定为 null,则当游戏程序欲显示 SpriteFont 字型定义文件中未定义的字符时,就会引发例外。 表 6 所示为 SpriteFont 类别常用的方法: 表 6:SpriteFont 类别常用的方法 方法名称 说明 MeasureString 传回 SpriteFont 字型定义文件定义的文字的高度与宽度。当游戏程序欲将文字显示在某个矩形的正中央的时候就需要用到这个方法。 准备好必要的字型定义文件之后,接下来我们就要为游戏程序加入显示文字的功能。 首先请于 Game1 类别加入以下的变量宣告,负责管理欲显示的文字,文字显示的位置,文字旋转的角度,以及旋转的圆心坐标: SpriteFont GameFont; //管理欲顯示的文字的變數 Vector2 FontPosition; //管理文字顯示位置的變數 Vector2 RotateOrigin; //管理旋轉文字依據的圓心座標的變數 float FontAngle = 0; //管理文字旋轉角度的變數 然后于 Game1 类别的 LoadContent 方法加入以下的程序代码,负责加载欲显示的文字定义,计算欲显示的文字的大小,位置,以及旋转时的圆心坐标: Viewport ViewPort = GraphicsDevice.Viewport; //取得遊戲視窗的大小 GameFont = Content.Load<SpriteFont>("GameFont"); //載入欲顯示的文字資源 Vector2 FontSize = GameFont.MeasureString("中文"); //計算欲顯示的文字的大小 FontPosition = new Vector2((ViewPort.Width - FontSize.X) / 2, (ViewPort.Height - FontSize.Y) / 2); //設定文字要顯示在遊戲視窗的正中央 RotateOrigin = new Vector2(FontSize.X / 2, FontSize.Y / 2);//設定文字的中心為旋轉的圓心 [注意] 呼叫 Content 对象的 Load 方法加载 Content Pipeline 项目管理的字型资源时所传入的参数名称即扩展名为 .spritefont 的字型定义文件的主文件名,也就是图 2 所示加入字型资源的画面中于 [Name] 字段输入的文件名中的主档名。 加载妥游戏程序欲显示的文字资源之后,请将 Game1 类别的 Update 方法编辑成以下的样子,负责递增文字旋转的角度: protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); if (FontAngle < 360) //如果旋轉角度尚未到達360度 { FontAngle += 1; //遞增旋轉角度1度 } // TODO: Add your update logic here base.Update(gameTime); } 最后我们只要在 Game1 类别的 Draw 方法中呼叫 spriteBatch 对象的 End 方法之前加入以下的程序代码,负责显示字型定义文件中指定的中文字,并以文字中心点为圆心旋转所显示的文字 spriteBatch.DrawString(GameFont, "中文", FontPosition+RotateOrigin, Color.White, MathHelper.ToRadians(FontAngle), RotateOrigin, 1, SpriteEffects.None, 0); //呼叫SpriteBatch類別的DrawString方法顯示並依據指定的角度旋轉文字 做好之后请执行项目,您将会看到游戏程序显示的中文出现在游戏窗口的正中央,并以文字的中心点为圆心进行旋转的情形,如图 3 所示: 图 3:显示并旋转中文字的游戏程序执行的情形 [范例档案下载] SpriteAndText.zip

二维动画与碰撞侦测

clock 八月 1, 2011 10:03 by author alex
  摘要 以 XNA 为基础的游戏可以利 3D 模型为游戏程序加入动画效果,也可以利用简单的程序技巧将 2 维的图片显示成动画。2 维动画在制作上要比 3 维动画简单,在程控上也比较单纯,但是在效果上当然也比较逊色,不过因为游戏会吸引使用者使用的原因,游戏的声光与娱乐效果只占其中的一部分,游戏的内涵、趣味性、和挑战性也是吸引玩家的重要因素。在这一篇文章中我们将要为大家介绍以 XNA 为基础的游戏程序制作 2D 动画与物体碰撞侦测的技巧。 2 维动画技术 游戏程序一般有两种常见的 2 维动画效果,一种是动画物体,另外一种是会移动的背景。动画物体的效果可以利用一连串不同的小图案组成的大图当做动画的内容,由游戏程序一次取出一张小图案来显示。例如图 1 所示即为由 12 张小图组成的大图: 图 1:由 12 张小图组成的大图 (注:本图材自 http://www.emutalk.net/showthread.php?t=43273) 在快速取出不同的小图案来显示的状况下,游戏程序可以显示出生动的 2 维动画效果,以执行在 Windows Phone 7 智能型手机的游戏程序而言,在正常的状况下,每秒可以显示 30 张不同的图案,如果游戏程序是执行在 Windows 平台或是 Xbox/Xbox 360 游戏机,每秒可以显示高达 60 张不同的图案,因为人眼有视觉暂留功能,所以物体在快速转变时,之前显示的影像虽然消失,但是人的眼睛仍然能继续保留影像的内容 0.1 秒 ~ 0.4 秒之久,所以物体可以平顺地呈现不同的图案,形成动画的效果。 第二种常见的 2 维动画常常应用在游戏程序的卷动背景,可以用来营造物体往前移动或是往后倒退的效果,有关如何卷动游戏的背景可以参考 XNA Framework 常用的类别 一文的说明。 实作 2 维动画 欲利用一连串不同的小图案组成的大图当做动画的内容,由游戏程序一次取出一张小图案来显示,游戏程序必须知道组成大图的小图案的高度和宽度,假设组成大图的每一张小图案的高度和宽度皆为 64 ,则从 X 坐标为 0 ,Y 坐标为 0 的位置取出高度为 64 ,宽度为 64 的图案,就可以取出第一张小图案,而从 X 坐标为 64 ,Y 坐标为 0 的位置取出高度为 64 ,宽度为 64 的图案,就可以取出第二张小图,如果要取出第二排的第一张小图案,则可以从 X 坐标为 0 ,Y 坐标为 64 的位置取出高度为 64 ,宽度为 64 的图案,以此类推。其坐标的计算方式为 X 坐标往右增加,Y 坐标往下增加,而坐标的原点在图案的左上角,如图 2 所示: 图 2 :取用大图中的小图案的坐标计算方式 (注:本图材自 http://www.emutalk.net/showthread.php?t=43273) 游戏程序只要计算好欲取用的小图案的起始坐标,以及小图案的高度与宽度,再透过 SpriteBatch 类别的 Draw 方法的 SourceRectangle 参数指定来源图案的矩形(指定来源图案的矩形相当于指定来源图案的起始坐标和高度与宽度),就可以从来源图案取出指定区域的内容,再显示到游戏的窗口供用户检视。有关支持指定 SourceRectangle 参数的 SpriteBatch 类别的 Draw 方法原形如下: public void Draw ( Texture2D texture, Vector2 position, Nullable<Rectangle> sourceRectangle, Color color ) 游戏程序可以利用以下的公式取出组成大图的任何一张小图案: Rectangle SourceRectangle=new Rectangle((Column-1) *小图图宽, (Row -1) *小图图高,小图图宽,小图图高); 其中的 Row 代表列编号,Column 代表栏编号,所以当游戏程序欲取用第一列的第一张小图时,SourceRectangle 的计算方式如下: Rectangle SourceRectangle=new Rectangle(0, 0,小图图宽,小图图高); 当游戏欲取第一列的第二张小图时,SourceRectangle 的计算方式如下: Rectangle SourceRectangle=new Rectangle(小图图宽, 0, 小图图宽,小图图高); 当游戏欲取第二列的第一张小图时,SourceRectangle 的计算方式如下: Rectangle SourceRectangle=new Rectangle(0,小图图高, 小图图宽,小图图高); 要将图 2 所示的大图显示成 2 维动画,请先启动 Visual Studio 2010 Express for Windows Phone ,建立一个型态为 [Windows Phone Game(4.0)] 型态的项目,然后将图 2 所示的图片加入到 Content Pipeline 项目中。 [提示] 您可以视需要加入背景图案当做游戏的背景。 做好之后请于 Game1 类别加入以下的变量宣告: Texture2D Mario; //管理圖檔的變數 Texture2D Background; //管理背景圖案的變數 int RowCount = 2; //圖檔每一欄的小圖案數目 int ColumnCount = 6; //圖檔每一列的小圖案數目 int RowIndex = 1; //第一個小圖案的列編號 int ColumnIndex = 1; //第一個小圖案的欄編號 Vector2 BasePosition; //存放小圖案顯示的位置的變數 Rectangle SourceRectangle; //欲取用的小圖案位置 int MarioWidth = 114; //組成大圖的小圖案寬度 int MarioHeight = 120; //組成大圖的小圖案高度 宣告好变量之后,请于 Game1 类别的建构函式中设定游戏窗口的高度与宽度,编辑好的 Game1 类别建构函式如下: public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferHeight = 800; //設定遊戲視窗的高度為800 graphics.PreferredBackBufferWidth = 480; //設定遊戲視窗的寬度為480 TargetElapsedTime = TimeSpan.FromTicks(333333); } 做好之后请编辑 Game1 类别的 LoadContent 方法,从 Content Pipeline 项目加载欲显示的大图,并设定组成大图的小图案欲显示的位置,编辑好的 LoadContent 方法如下: protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); Mario = Content.Load<Texture2D>("MarioAnimate12"); //從Content Pipeline專案載入圖形 Background = Content.Load<Texture2D>("Background"); //從Content Pipeline專案載入背景 Viewport viewport = graphics.GraphicsDevice.Viewport; //取得遊戲視窗的大小 BasePosition = new Vector2(0, viewport.Height - 220); //設定小圖案顯示的位置 } 加载好欲显示的图形之后,请将 Game1 类别的 Update 方法编辑成以下的样子,负责从图 2 所示的大图依序取出每一张小图案来显示: protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); SourceRectangle = new Rectangle((ColumnIndex - 1) * MarioWidth, (RowIndex - 1) * MarioHeight, MarioWidth, MarioHeight); //計算欲取用的小圖案的位置 if (ColumnIndex <= ColumnCount) //如果第一列的小圖案尚未取完 { ColumnIndex++; //遞增欲取用的小圖案編號 } else //否則 { ColumnIndex = 1; //設定要讀取第一欄的小圖案 RowIndex++; //遞增欲讀取的圖形列編號 if (RowIndex > RowCount) //如果已經讀完所有的列 { RowIndex = 1; //設定要讀取第一列的小圖案 } } BasePosition.X++; //令小圖案以每次一個單位往右移動 base.Update(gameTime); } 最后请将 Game1 类别的 Draw 方法编辑成以下的样子,负责依据欲取用的小图案的位置取出小图案,并显示在由 BasePosition 变量指定的位置: protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); //宣告繪圖動作開始 spriteBatch.Draw(Background, Vector2.Zero, Color.White); //顯示背景 spriteBatch.Draw(Mario, BasePosition, SourceRectangle, Color.White); //顯示取出的小圖案 spriteBatch.End(); //宣告繪圖動作結束 base.Draw(gameTime); } 做好之后请执行项目,您就会看到图 2 所示的大图形中的每一张小图案会依序被游戏程序取出来显示,因为图形更换的速度很快,所以可以形成动画的效果,如图 3 所示: 图 3 :以 XNA 为基础的游戏程序显示 2 维的动画的情形 碰撞侦测 当游戏程序显示的物体和动作过程或是移动过程发生碰撞时,游戏程序必须能够侦测物体是否发生碰撞,并据以做出适当的反应,例如在飞弹击中入侵的异形时发出爆炸的声音,或是在棒搥打中怪兽时发出惨叫的声音,达到与使用者互动的效果,添加游戏的趣味性与临场感。 使用 XNA Framework 开发游戏程序的程序设计师可以利用 BoundingBox 结构、BoundingSphere 结构、或是 BoundingFrustum 类别辅助游戏程序判断物体是否发生碰撞,不需要自己辛苦地利用几何学的技巧进行判断。 使用 BoundingBox 结构判断物体是否发生碰撞主要的概念是利用立方体来描述物体,当两个代表物体的立方体产生交集的时候便是物体发生碰撞;使用 BoundingSphere 结构判断物体是否发生碰撞主要的概念是利用球体来描述物体,当两个代表物体的球体产生交集的时候便是物体发生碰撞;而使用 BoundingFrustum 类别判断物体是否发生碰撞主要的概念是利用断面锥体来描述物体,当两个代表物体的断面锥体产生交集的时候便是物体发生碰撞。 图 4 所示即为利用 BoundingBox 结构定义的立方体描述游戏物体的示意图,图 5 所示即为利用 BoundingSphere 结构定义的球体描述游戏物体的示意图,图 6 所示即为利用 BoundingFrustum 类别定义的断面锥体描述游戏物体的示意图。 图 4 :利用 BoundingBoxn 结构定义的立方体描述游戏物体的示意图 图 5 :利用 BoundingSphere 结构定义的球体描述游戏物体的示意图 图 6 :利用 BoundingFrustum 类别定义的断面锥体描述游戏物体的示意图 欲利用 BoundingBox 架构游戏的物体,程序必须定义描述物体的立方体的最小坐标点和最大坐标点,欲利用 BoundingSphere 架构游戏的物体,程序必须定义描述物体的球体的圆心和半径,而欲利用 BoundingSphere 类别描述游戏的物体,程序必须定义描述物体的断面锥体的各个顶点的坐标。 表 1 所示为 BoundingBox 结构常用的属性: 表 1:BoundingBox 结构常用的属性 属性名称 说明 Max 描述物体的立方体的顶点的坐标中内容值最大者。 Min 描述物体的立方体的顶点的坐标中内容值最小者。 表 2 所示为 BoundingBox 结构常用的方法: 表 2 :BoundingBox 结构常用的方法 方法名称 说明 Contains 判断BoundingBox架构的立方体是否包含指定的立方体、球体、坐标、或是断面锥体。 CreateFromPoints 依据一组坐标点建立立方体。 CreateFromSphere 依据 BoundingSphere 架构的球体立方体。 CreateMerged 依据两个 BoundingBox 架构的立方体组成立方体。 Intersects 判断 BoundingBox 架构的立方体是否与指定的立方体、球体、断面锥体、平面、或光束有交集。 BoundingSphere 结构常用的属性可以参考表 3 的说明: 表 3 :BoundingSphere 结构常用的属性 属性名称 说明 Center 描述球体圆心的坐标。 Radius 描述球体的半径。 BoundingSpheren 结构常用的方法可以参考表 4 的说明: BoundingSphere 结构常用的方法可以参考表 4 的说明: 方法名称 说明 Contains 判断 BoundingSphere 架构的球体是否包含指定的立方体、球体、坐标、或是断面锥体。 CreateFromBoundingBox 依据 BoundingBox 架构的立方体建立球体。 CreateFromFrustum 依据 BoundingFrustum 类别描述的断面锥体建立球体。 CreateFromPoints 依据一组坐标点建立球体。 CreateMerged 依据指定的两个球体建立组合的球体。 Intersects 判断 BoundingSphere 架构的球体是否与指定的立方体、球体、断面锥体、平面、或光束有交集。 Transform 平移或放大/缩小 BoundingSphere 架构的球体。 BoundingFrustum 类别常用的属性可以参考表 5 的说明: 表 5 :BoundingFrustum 类别常用的属性 属性名称 说明 Bottom 取得 BoundingFrustum 类别描述的断面锥体的底部平面。 Far 取得 BoundingFrustum 类别描述的断面锥体的远平面。 Left 取得 BoundingFrustum 类别描述的断面锥体的左方平面。 Matrix 取得 BoundingFrustum 类别描述的断面锥体的矩阵表示法。 Near 取得 BoundingFrustum 类别描述的断面锥体的近平面。 Right 取得 BoundingFrustum 类别描述的断面锥体的右方平面。 Top 取得 BoundingFrustum 类别描述的断面锥体的上方平面。 BoundingFrustum 类别常用的方法可以参考表 6 的说明: 表 6 :BoundingFrustum 类别常用的方法 方法名称 说明 Contains 判断 BoundingFrustum 类别描述的断面锥体是否包含指定的立方体、球体、坐标、或是断面锥体。 GetCorners 取得包含 BoundingFrustum 类别描述的断面锥体的所有顶点的数组。 Intersects 判断 BoundingFrustum 类别描述的断面锥体是否与指定的立方体、球体、断面锥体、平面、或光束有交集。 [提示] 不管是 BoundingBox 结构的 Min/Max 属性,BoundingSphere 结构的 Center 属性,还是 BoundingFrustum 类别的 GetCorners 方法传回的数组的元素型态,都是 Vector3 结构型态的数据,也就是 3 度空间的坐标,换句话说,BoundingBox 结构、 BoundingSphere 结构、以及 BoundingFrustum 类别都可以适用于侦测 3 维游戏的物体碰撞,如果要应用在2维游戏的物体碰撞侦测,则将代表Z轴的坐标的内容值设定成0即可,因为2维空间的物体没有Z轴的坐标。透过将Z轴的坐标的内容值设定成0,2维游戏程序一样能够利用 BoundingBox 结构、BoundingSphere 结构、或是 BoundingFrustum 类别来判断物体与物体是否发生碰撞。 了解使用 XNA Framework 的游戏程序如何利用 XNA 支持的结构和类别进行碰撞侦测判断之后,接下来我们就要修改上述的游戏程序,在物体移动到与背景右下角的圆柱体发生碰撞时,禁止物体继续往右移动。 首先请在 Game1 类别中加入以下的变量宣告 BoundingBox bbActor; //負責定義移動物體大小的立方體 BoundingBox bbCylinder; //負責定義遊戲背景右下角的圓柱體的立方體 做好之后请于 Game1 类别的 LoadContent 方法中加入以下的程序代码,负责设定游戏背景右下角的圆柱体的立方体的位置: bbCylinder.Min = new Vector3(396, 545, 0); //設定定義圓柱體的立方體的最小座標點 bbCylinder.Max = new Vector3(463, 698, 0); //設定定義圓柱體的立方體的最大座標點 然后请编辑 Game1 类别的 Update 方法,以下的程序代码: base.Update(gameTime); 的前面加入下列的程序代码,负责设定描述移动物体的立方体的大小,并在移动的物体触及游戏背景右下角的圆柱体时停止往右移动物体的动作: bbActor.Min = new Vector3(BasePosition.X, BasePosition.Y, 0); //設定定義移動物體的立方體的最小座標點 bbActor.Max = new Vector3(BasePosition.X + MarioWidth, BasePosition.Y + MarioHeight, 0);//設定定義移動物體的立方體的最大座標點 if (!bbActor.Intersects(bbCylinder)) //如果移動的物體尚未與遊戲背景右下角 //的圓柱體發生碰撞 { BasePosition.X++; //將物體往右移動一個單位 } 做好之后请执行项目,待游戏程序显示的物体一直往右移动至碰撞到游戏背景右下角的圆柱体时,就会停止继续往右移动的动作,如图7所示: 图 7 :游戏程序显示的物体碰撞到游戏背景右下角的圆柱体后停止继续往右移动的情形 范例下载:SimpleAnimation.zip

体感游戏设计

clock 七月 31, 2011 23:17 by author alex
  摘要 Windows Phone 7 智能型手机支持完整的传感器 (Sensor) 功能,包括:重力传感器 (G-Sensor)、数字罗盘、趋近传感器、以及环境光线传感器,这些传感器可以视为另外一种型态的输入,可以用来控制游戏程序的进行。在这一篇文章,我们将会利用 Windows Phone 7 智能型手机的重力传感器,让使用者能够利用传感器代替输入设备,达到控制游戏执行的目的。 认识传感器 传感器可以视为一种特殊的输入设备,使用者可以不需要特别执行任何输入的动作,程序就可以依据传感器输入的数据做出反应。例如程序可以利用数字电子罗盘得到方向相关的数据,协助定位相关的功能进行定位;利用光线传感器感应外界光线的强弱,自动调节智能型手机屏幕的亮度;利用接近传感器判断智能型手机是否贴近使用者的脸部欲进行通话的动作,而锁定屏幕,避免误触智能型手机的功能而影响通话的进行;利用重力传感器感应智能型手机运动的方向,并据以调整智能型手机的屏幕显示方向,或是改变程序显示的内容。例如类似弹珠台的游戏程序就可以善用重力传感器模拟使用者摇晃弹珠台,以改变弹珠的滚动方向的动作,达到逼真的游戏操作体验。除此之外,重力传感器在支持 GPS (全球定位系统) 功能方面,能够在智能型手机接收不到卫星讯号时利用智能型手机运动的方法推断用户的位置。 传感器在游戏程序的应用 传感器在游戏程序的应用很广泛,例如使用者挥动手臂的动作可以模拟使用球棒挥击棒球的动作,模拟掷出保龄球的动作,拍击网球、羽毛球、或乒乓球的动作,模拟丢掷骰子,甚至可以模拟游戏者身体移动的方向,跳跃的高度与距离,让使用者融入游戏的场景,达到与游戏真实互动的感觉,而不是像传统的游戏,游戏的使用者感觉较像局外人。 Microsoft、任天堂、与 Sony 等游戏大厂在体感游戏开发方面都有丰富的成果,而且也都得到游戏玩家正面的反应。Windows Phone 7 智能型手机支持了完整的传感器功能,让程序设计师能够很方便开发出利用传感器控制的俄罗斯方块,在不靠键盘输入的状况下利用倾斜智能型手机的方式控制方块掉落的位置。 Windows Phone 7 体感游戏设计支持 了解传感器的用途以及传感器在游戏程序上的应用之后,接下来我们就要利用 Windows Phone 7 智能型手机提供的重力传感器开发能够利用传感器控制游戏显示的内容的游戏。 Windows Phone 7 智能型手机提供的重力传感器可以利用量测重力的原理判断智能型手机移动的方向,允许使用者利用摇动或甩动智能型手机的方式控制游戏的执行,其原理和汽车的安全气囊相同,在侦测到汽车快速减速的时候立刻充气以保护驾驶人与乘客不会受伤。 要使用重力传感器当做游戏程序的输入,以 XNA 为基础的游戏程序可以利用 Accelerometer 类别提供的功能启用/停用重力加速器,取得重力加速器的状态,以及处理重力加速器引发的事件。有关 Accelerometer 类别常用的属性可以参考表1 的说明: 表1:Accelerometer 类别常用的属性 属性名称 说明 State 管理重力加速器状态的属性,其型态为 SensorState 列举型态。有关 SensorState 列举型态合法的内容值可以参考表4 的说明。 Accelerometer 类别常用的方法可以参考表2 的说明: 表2:Accelerometer 类别常用的方法 方法名称 说明 Start 开始从重力加速器读取数据。 Stop 结束从重力加速器读取数据。 Accelerometer 类别常用的事件可以参考表 3 的说明: 表3:Accelerometer 类别常用的事件 事件名称 说明 ReadingChanged 当重力加速器读取到数据时会引发的事件。 处理 ReadingChanged 事件的事件处理程序的第二个参数的型态为 AccelerometerReadingEventArgs 类别,其 X、Y、与 X 属性的内容值代表智能型手机在 X 轴、Y 轴、和 Z 轴的加速方向,而不是三度空间的坐标,其单位为重力单位,也就是 G 力 (1G = 9.81 m/s2)。除了 X、Y、与 Z 三个属性以外,还有一个名称为 Timestamp 的属性,负责记录重力加速器读取数据的时间点。有关 X 轴、Y 轴、和 Z 轴的加速方向可以参考图1 的说明: 图1:X 轴、Y 轴、和 Z 轴的加速方向 请注意当智能型手机放在平坦的桌面上,而且正面朝上的时候,AccelerometerReadingEventArgs 类别的 Z 字段的内容值会是 -1.0,表示 Z 轴承受 -1G 的重力,而当智能型手机放在平坦的桌面上,而且正面朝下的时候,AccelerometerReadingEventArgs 类别的 Z 字段的内容值就会是 +1.0,表示 Z 轴承受 1G 的重力。 [说明] 透过 Accelerometer 类别的 State 属性取得的重力加速器状态是 SensorState 列举型态的数据,其合法的内容值请参考表4 的说明: 表4:SensorState 列举型态合法的内容值 内容值名称 说明 NotSupported 未支持重力加速器。 Ready 重力加速器处于可以处理数据的状态。 Initializing 重力加速器正在初始化。 NoData 未支持重力加速器。 NoPermissions 呼叫者没有权限取用重力加速器接收到的数据。 Disabled 重力加速器处于禁用的状态。 要使用重力加速器判断智能型手机加速的方向,首先您必须使用鼠标的右键点中 [Solution Explorer] 窗口中的项目名称,从出现的菜单选择 [Add Reference] 功能,然后于出现的窗口中选择名称为 Microsoft.Devices.Sensors 的组件,如图2 所示: 图2:参考名称为 Microsoft.Devices.Sensors 的组件的画面 做好之后请按下 [OK] 键完成参考组件的动作。接下来请于 Game1 类别中加入以下的变量宣告,负责管理重力加速器装置: Accelerometer gSensor; //管理重力加速器的變數 然后于 Initialize 方法执行建立 Accelerometer 类别的对象的动作,为 Accelerometer 类别的对象的 ReadingChanged 事件制作事件处理程序,并呼叫 Accelerometer 类别的 Start 方法,开始接收从重力加速器输入的数据,如下: protected override void Initialize() { gSensor = new Accelerometer(); //建立 Accelerometer 類別的物件 gSensor.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>( gSensor_ReadingChanged); //處理 Accelerometer 類別的物件的 ReadingChanged 事件 gSensor.Start(); //開始接收重力加速器輸入的資料 base.Initialize(); } 应用程序只要于 Accelerometer 类别的对象的 ReadingChanged 事件的事件处理程序中利用型态为 AccelerometerReadingEventArgs 类别,名称为 e 的参数,就可以得知 Windows Phone 7 装置加速的方向,如下: void gSensor_ReadingChanged(object sender, AccelerometerReadingEventArgs e) { //取用 e.X, e.Y, e.Z } 利用传感器移动 XNA 游戏显示的物体 了解 Windows Phone 7 对体感游戏设计的支持之后,接下来我们就要设计一个能够支持体感控制的 XNA 游戏,让使用者能够以倾斜 Windows Phone 7 智能型手机的方式移动物体。 首先请启动 Visual Studio 2010 Express for Windows Phone,建立一个 [Windows Phone Game (4.0)] 型态的项目,然后加入游戏程序欲显示的图片到 Content Pipeline 项目中。 做好之后请为 Game1 类别加入以下的变量宣告,负责管理欲显示的图片和图片的显示位置,以及管理重力加速器和记载加速度方向的变量: Texture2D Logo; //管理欲顯示的圖片的變數 Vector2 LogoPosition; //管理圖片顯示位置的變數 Accelerometer gSensor; //管理重力加速器的變數 Vector2 LogoVelocity; //管理加速度方向的變數 宣告妥游戏程序欲使用的变量之后请编辑 Game1 类别的建构函式,执行设定游戏窗口的高度与宽度的工作,设定好游戏窗口高度与宽度的 Game1 类别建构函式如下: public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferWidth = 480; //設定遊戲視窗的寬度 graphics.PreferredBackBufferHeight = 800; //設定遊戲視窗的高度 TargetElapsedTime = TimeSpan.FromTicks(333333); } 因为我们想让游戏的使用者能够利用传感器控制游戏显示的对象,所以我们必须在 Game1 类别的 Initialize 方法中启用重力加速器,做好的 Initialize 方法如下: protected override void Initialize() { gSensor = new Accelerometer(); //建立 Accelerometer 類別的物件 gSensor.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>( gSensor_ReadingChanged); //為建立 Accelerometer 類別的物件指定 //ReadingChanged 事件的事件處理程序 gSensor.Start(); //啟動重力加速器 base.Initialize(); } 上述的程序代码需要用到名称为 gSensor_ReadingChanged 的事件处理程序,所以我们必须在 Game1 类别中加入以下的方法,负责处理 Accelerometer 类别的对象 (即重力加速器) 引发的 ReadingChanged 事件: void gSensor_ReadingChanged(object sender, AccelerometerReadingEventArgs e) { LogoVelocity.X += (float)e.X; //取得 X 軸的加速度 LogoVelocity.Y += -(float)e.Y; //取得 Y 軸的加速度 LogoPosition += LogoVelocity; //將加速度的內容值加入到圖片的位置 } 建立妥管理 Accelerometer 类别的对象,启动重力加速器之后,并制作好处理重力加速器引发的 ReadingChanged 事件的事件处理程序之后,请编辑 Game1 类别的 LoadContent 方法,加载游戏程序欲显示的图片,顺便设定图片默认的显示位置,做好的 LoadContent 方法如下: protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); Logo = Content.Load<Texture2D>("xna"); //載入遊戲程式欲顯示的圖片 Viewport viewport = graphics.GraphicsDevice.Viewport; //取得遊戲視窗的視界 LogoPosition = new Vector2( (viewport.Width - Logo.Width) / 2, (viewport.Height – Logo.Height) / 2); //設定圖片預設顯示的位置在遊戲視窗的正中央 } 因为我们在初始化的阶段启用了重力加速器传感器,所以我们需要在游戏程序结束之前关闭重力加速器。您可以编辑 Game1 类别的 UnloadContent 方法,于 UnloadContent 方法中呼叫 Accelerometer 类别的对象的 Stop 方法,将重力加速器关闭,编辑好的 UnloadContent 方法如下: protected override void UnloadContent() { gSensor.Stop(); } 因为我们开发的游戏程序允许用户以倾斜的方式让用户移动游戏程序显示的对象,所以我们必须在 Game1 类别的 Update 方法中改变物体显示的位置,当游戏程序显示的物体碰撞到游戏窗口的四个边界的时候,停止移动物体的动作。所以我们要将 Game1 类别的 Update 方法编辑成以下的样子,在物体碰撞到游戏窗口的四个边界时,设定对象显示的位置,并将重力加速度的内容值设定为 0: protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); Viewport viewport = graphics.GraphicsDevice.Viewport; //取得遊戲視窗的視界 if (LogoPosition.X < 0) //如果所顯示的圖片已經超出遊戲視窗的左邊界 { LogoPosition.X = 0; //將所顯示的圖片的位置靠齊在遊戲視窗的左邊界 LogoVelocity.X = 0; //設定 X 軸方向的加速度為0 } else if (LogoPosition.X > viewport.Width – Logo.Width) //如果所顯示的圖片已經超出遊戲視窗的右邊界 { LogoPosition.X = viewport.Width – Logo.Width; //將所顯示的圖片的位置靠齊在遊戲視窗的右邊界 LogoVelocity.X = 0; //設定 X 軸方向的加速度為0 } if (LogoPosition.Y < 0) //如果所顯示的圖片已經超出遊戲視窗的上邊界 { LogoPosition.Y = 0; //將所顯示的圖片的位置靠齊在遊戲視窗的上邊界 LogoVelocity.Y = 0; //設定 Y 軸方向的加速度為0 } else if (LogoPosition.Y > viewport.Height – Logo.Height) //如果所顯示的圖片已經超出遊戲視窗的下邊界 { LogoPosition.Y = viewport.Height – Logo.Height; //將所顯示的圖片的位置靠齊在遊戲視窗的下邊界 LogoVelocity.Y = 0; //設定 Y 軸方向的加速度為0 } base.Update(gameTime); } [提示] 上述的程序代码会在游戏程序显示的物体碰撞到游戏窗口的四个边界的时候将物体停放在窗口的边界,如果要让对象碰撞到游戏窗口的边界时产生反弹的效果,可以将对象加速度的方向设定为负值,再依据模拟的摩擦系数进行递减,详细的做法可以参考[设计支持手势操作的 XNA 游戏]一文的说明,让游戏程序显示的物体能够像实际世界的物体一样呈现自然的反弹效果。 最后我们要编辑 Game1 类别的 Draw 方法,将游戏程序显示的物体显示在游戏程序的窗口中,编辑好的 Draw 方法如下: protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); //宣告繪製的動作開始 spriteBatch.Draw(Logo, LogoPosition,Color.White); //繪製事先載入的圖片資源 spriteBatch.End(); //宣告繪製的動作結束 base.Draw(gameTime); } 做好之后请执行建置项目的动作,完成使用传感器的游戏程序的制作。 因为我们制作的游戏程序需要使用到 Windows Phone 7 智能型手机上的重力加速器,让游戏程序利用传感器输入的数据来控制游戏程序的进行,所以制作好的程序必须部署到实际的 Windows Phone 7 智能型手机上执行,无法部署到 Windows Phone 7 的仿真器上执行,因为 Windows Phone 7 仿真器并未提供传感器的功能,就算 Windows Phone 7 仿真器提供重力加速器传感器,用户也很难摇动或是倾斜计算机的屏幕以产生重力加速的效果,以试验游戏程序设计读取传感器输入的数据的功能是否正确。有关如何将开发好的游戏程序部署到实际的 Windows Phone 7 智能型手机的做法可以参考 [设计支持手势操作的 XNA 游戏] 一文的说明。 请将开发好的游戏程序部署到实际的 Windows Phone 7 智能型手机执行,您将会看到如图3 的执行画面: 图3:使用重力加速器功能的游戏程序执行的情形 您可以将智能型手机往任意的方向倾斜,游戏程序显示的物体就会往屏幕倾斜的方向移动,这是因为智能型手机往任意的方向倾斜之后产生的重力加速度会被用来当做移动游戏程序移动物体的速度的关系。 [提示] 有些游戏程序会在用户将智能型手机的屏幕转向之后,切换屏幕显示的内容,例如将原本垂直显示的窗口内容改变成水平显示的内容,或是将原本水平显示的窗口内容改变成垂直显示的内容,这种功能并不需要利用 Accelerometer 类别提供的功能才能够达成。要在使用者旋转智能型手机方向的时候自动旋转游戏程序窗口的显示方向,以 XNA 为基础的游戏程序只要设定 GraphicsDeviceManager 类别的对象的 SupportedOrientations 属性的内容值,就可以在使用者旋转智能型手机的方向的时候自动旋转游戏程序窗口的显示方向。 GraphicsDeviceManager 类别的对象的 SupportedOrientations 属性的型态为 DisplayOrientation 结构,其合法的内容值请参考表5 的说明: 表5:DisplayOrientation 结构合法的内容值 合法的内容值 说明 Default 默认的窗口显示方向。 LandscapeLeft 将窗口的显示方向逆时钟旋转 90 度,变成水平显示的方向,此时屏幕的宽度将会大于窗口的高度。 LandscapeRight 将窗口的显示方向顺时钟旋转 90 度,变成水平显示的方向,此时窗口的宽度将会大于窗口的高度。 Portrait 以垂直的方向显示窗口的内容,此时窗口的高度将会大于窗口的宽度。 您只要在 Game1 类别的建构函式中将 GraphicsDeviceManager 类别的对象的 SupportedOrientations 属性的内容值设定成适当的内容值,就可以在使用者旋转智能型手机的方向的时候自动旋转游戏程序窗口的显示方向,做法如下: public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.SupportedOrientations = DisplayOrientation.Portrait | DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight; //表示視窗能夠自動切換成垂直顯//示,逆時鐘旋轉 90 度顯示,以及順//時鐘旋轉 90 度顯示 graphics.PreferredBackBufferWidth = 480; graphics.PreferredBackBufferHeight = 800; TargetElapsedTime = TimeSpan.FromTicks(333333); } 做好之后请将应用程序部署到实际的 Windows Phone 7 智能型手机执行,然后旋转执行程序的 Windows Phone 7 智能型手机屏幕的方向,您就会发现应用程序也会自动调整窗口显示的方向,使其和屏幕的方向一致,如图4 所示: 图4:可以依据屏幕的方向自动调整窗口显示方向的游戏程序 范例下载:AccelerometerEnabledGame.zip audioEngine = new AudioEngine("Content\\MyAudio.xgs"); //建立 AudioEngine 類別的物件 waveBank = new WaveBank(audioEngine, "Content\\Wave Bank.xwb"); //建立 Wave Bank 類別的物件 soundBank = new SoundBank(audioEngine, "Content\\Sound Bank.xsb"); //建立 Sound Bank 類別的物件 请注意建立 AudioEngine 类别对象时指定的是 XACT3 的项目名称,其扩展名为 .xgs,建立 WaveBank 类别的对象时指定的是 Wave Bank 的名称,其扩展名为 .xwb,而建立 SoundBank 类别的对象时指定的是 Sound Bank 的名称,其扩展名为 .xsb。 建立好必要的对象之后,请于 Game1 类别的 Update 方法加入以下的程序代码,准备欲播放的音效内容,并呼叫 AudioEngine 类别对象的 PlayCue 方法执行播放音效的动作: audioEngine.Update(); soundBank.PlayCue("RingOut"); 请注意呼叫 AudioEngine 类别对象的 PlayCue 方法所传入的是 Cue Name,意即要播放 Cue Name 所代表的声音文件。做好之后只要执行程序就可以听到程序播放 XACT3 工具管理的音效。

进阶音效控制与管理

clock 七月 31, 2011 23:15 by author alex
  摘要 以 XNA 为基础的游戏程序可以利用 SoundEffect 类别的功能执行简单的音效播放,或是利用 SoundEffectInstance 类别进行进阶的音效播放控制,包括播放、暂停、恢复、结束、是否要播放 3D 的音效、控制声音从左边或右边的声道输出、控制音调高低、以及控制音量等等。在这一回的文章中,我们将介绍利用 SoundEffectInstance 类别进行进阶音效控制的做法,除此之外,我们也将会介绍支持组织和管理声音文件的 XACT(Cross-platform Audio Creation Tool) 工具。 有关 XNA Framework 提供的 SoundEffect 类别和 SoundEffectInstance 类别的基本功能和使用方法可以参考 [XNA 交互式游戏设计] 一文的说明。 进阶音效控制 Windows Phone 7 智能型手机并非只能播放一种音效,以 XNA 为基础的游戏程序可以同时播放 64 种声音,达成混合多种音效的效果。除此之外,XNA Framework 也支持动态播放音效,例如播放应用程序执行时期建立的音效,或是搭配麦克风播放由外界输入的音效。 [提示] Windows Phone 7 智能型手机支持同时播放多达 64 种的音效,但是 Xbox 360 游戏机支持播放多达 300 种的音效,可以播放更丰富的声音效果,而 Windows 操作系统则是未限制可以同时播放的音效数目。 当游戏程序想要仿真环绕音效的时候,可以利用 SoundEffectInstance 类别的 Pan 属性控制音效要从左方发声,还是要从右方发声。Pan 属性是一个介于 -1.0 与 1.0 之间的数值,其值为 -1.0 表示要从左方发声,其值为 1.0 表示要从右方发声,而其值为 0 则表示要从中央发声,其默认值为 0。 当游戏程序需要控制所播放的音效的音调高低时,就可以利用 SoundEffectInstance 类别的 Pitch 属性进行控制。Pitch 属性是一个介于 -1.0 与 1.0 之间的数值,其值为 -1.0 表示音调最低,其值为 1.0 表示音调最高,其默认值为 0。 当游戏程序需要控制所播放的音效的音量时,就可以利用 SoundEffectInstance 类别的 Volume 属性进行控制。Volume 属性是一个介于 .0 与 1.0 之间的数值,其值为 0 表示音量最小 (静音),其值为 1 表示音量最大,其默认值为 1。请注意透过 SoundEffectInstance 类别控制的音量取决于 SoundEffect 类别的 MasterVolume 属性的内容值,换句话说将 SoundEffectInstance 类别的 Volume 属性的内容值设定为 1,所呈现的最大音量就是 SoundEffect 类别的 MasterVolume 属性设定的音量。 例如游戏程序想呈现环绕音效的效果,可以先把欲播放的声音文件加入到 Content Pipeline 项目中,然后于 Game1 类别宣告以下的变量,负责管理音效资源、SoundEffectInstance 类别的对象、以及记录音效移动的方向: SoundEffect RingOut; //管理音效資源的變數 SoundEffectInstance RingOutEffect; //管理 SoundEffectInstance 類別的物件的變數 bool bPaneLeft = false; //記錄音效移動方向的變數 宣告妥变量之后请于 Game1 类别的 LoadContent 方法加入以下的程序代码,负责加载音效资源和建立 SoundEffectInstance 类别的对象: RingOut = Content.Load<SoundEffect>("RingOut"); //載入名稱為 RingOut 的音效資源 RingOutEffect = RingOut.CreateInstance(); //建立 SoundEffectInstance 類別的物件 RingOutEffect.IsLooped = true; //設定音效要不斷地重覆播放 加载音效资源并建立妥 SoundEffectInstance 类别的对象之后,我们只要在 Game1 类别的 Update 方法中变更 SoundEffectInstance 类别的对象的 Pan 属性的内容值,就可以变更音效发声的位置,例如以下的程序: if (bPaneLeft) //判斷音效發聲的位置是否為往左移動 { if (RingOutEffect.Pan - 0.1 <= -1) //如果已經無法往左移動 { RingOutEffect.Pan = -1f; //設定音效從左方發聲 bPaneLeft = true; //設定移動方向為往右移動 } else RingOutEffect.Pan -= 0.1f; //設定發聲位置往左移動 0,1 } else //音效發聲的位置為往右移動 { if (RingOutEffect.Pan + 0.1 >= 1) //如果已經無法往右移動 { RingOutEffect.Pan = 1f; //設定音效從右方發聲 bPaneLeft = false; //設定移動方向為往左移動 } else RingOutEffect.Pan += 0.1f; //設定發聲位置往右移動 0,1 } 做好之后请执行程序,游戏程序播放的音效就会从逐渐移至右方发声,移至最右方之后再逐渐移至左方进行发声,移至最左方之后再逐渐移至右方进行发声,周而复始,仿真音效环绕的效果。 [提示] 以 XNA 为基础的游戏程序可以使用类似的技巧控制所播放的音效的音调和音量,仿真音量由大变小,由小变大,或是音调由高变低,或是由低变高的效果。 动态音效支持 XNA Game Studio 4.0 在支持音效控制方面加入了一些新的控制功能,包括支持从 PCM 格式的音效数据流建立 SoundEffect 类别的对象,直接从 PCM 格式的音效数据流进行播放动态音效的功能,以及录制从麦克风输入的声音的功能,详细说明如下: XNA Game Studio 4.0 支持的 SoundEffect 类别的建构函式允许接受 PCM 格式的数据流型态的参数,建立能够从 PCM 格式的数据流播放音效的 SoundEffect 类别的对象。以下就是一个能够利用第一个参数指定 PCM 格式的音效数据流用来建立 SoundEffect 类别的对象的建构函式: public SoundEffect ( byte[] buffer, int sampleRate, AudioChannels channels ) 以下就是利用 TitleContainer 类别的 OpenStream 方法从扩展名为 .wav 格式的声音文件读入 PCM 格式的音效数据流,再据以建立 SoundEffect 类别的对象: SoundEffect soundFromFile = SoundEffect.FromStream(TitleContainer.OpenStream( @"Content\Sample.wav")); //利用 WAV 格式音效檔案的內容建立 SoundEffect 類別的物件 soundFromFile.Play(); //播放 SoundEffect 類別的物件管理的音效資料 使用上述的方式播放音效数据流,以 XNA 为基础的游戏程序仍然需要将欲播放的 WAV 格式的声音文件预先加入到 Content Pipeline 项目,但是必须将 WAV 格式的声音文件的 [Build Action] 属性的内容值从默认的 Compile 改成 Content,再将 [Copy to Output Directory] 属性的内容值改成:Copy always,表示不要将 WAV 格式的声音文件建置成 XNB 格式的二进制数据,而要随着游戏程序一起部署到 Windows Phone 7 智能型手机,供游戏程序执行时期加载使用,并直接从 WAV 格式的声音文件加载欲播放的音效。 [注意] 第一个参数限制必须为 PCM 格式、频率为 8KHz 或 48KHz、单声道或立体声的音效数据。 除了 SoundEffect 类别能够支持播放格式为音效数据流的音效之外,XNA Game Studio 4.0 提供的 DynamicSoundEffectInstance 类别也支持游戏程序于运行时间建立音效数据,供游戏程序执行的时候播放。DynamicSoundEffectInstance 类别常用的属性请参考表1 的说明: 表1:DynamicSoundEffectInstance 类别常用的属性 属性名称 说明 IsLooped 控制音效是否要不断地重复播放的属性。 Pan 控制音效发声位置的属性。 PendingBufferCount 查询放置播放音效数据的缓冲区数目。 Pitch 控制音调高低的属性。 State 表示音效播放状态的属性,可能状态为:playing (播放中)、paused (暂停)、stopped (停止)。 Volume 控制音量大小的属性。 DynamicSoundEffectInstance 类别常用的方法请参考表2 的说明: 表2:DynamicSoundEffectInstance 类别常用的方法 方法名称 说明 Apply3D 播放 3D 效果的音效。 GetSampleDuration 依据存放音效数据的缓冲区的数据量计算音效长度。 GetSampleSizeInBytes 依据存放音效数据的缓冲区的时间长度计算数据量。 Pause 暂停播放音效。 Play 执行播放音效的动作。 Resume 恢复播放音效的动作。 Stop 停止播放音效。 SubmitBuffer 指定存放音效数据的缓冲区为欲播放的音效。 表3:DynamicSoundEffectInstance 类别常用的事件 方法名称 说明 BufferNeeded 当存放音效数据以供播放的缓冲区数目小于或等于 2 时引发的事件。 欲使用 DynamicSoundEffectInstance 类别执行动态音效播放,您可以于 Game1 类别宣告以下的变量: DynamicSoundEffectInstance dynamicSound; //管理 DynamicSoundEffectInstance 類別 //的物件的變數 int position; //指定從第幾個位元組的音效資料開始播放 int count; //指定欲播放的音效的位元組數 byte[] byteArray; //存放音效資料的位元組陣列 然后于 Game1 类别的 LoadContent 方法加入以下的程序代码,从声音文件读入欲播放的音效数据: Stream waveFileStream = TitleContainer.OpenStream( @"Content\RingOut.wav"); //依據欲播放的音效檔案建立 Stream 類別的物件 BinaryReader reader = new BinaryReader(waveFileStream);//為 Stream 類別的物件建立 BinaryReader 類別的物件 int chunkID = reader.ReadInt32(); //讀取 Chunk ID int fileSize = reader.ReadInt32(); //讀取檔案大小 int riffType = reader.ReadInt32(); //讀取 riff 型態 int fmtID = reader.ReadInt32(); //讀取 fmt ID int fmtSize = reader.ReadInt32(); //讀取 fmt 大小 int fmtCode = reader.ReadInt16(); //讀取 fmt code int channels = reader.ReadInt16(); //讀取 channel 數目 int sampleRate = reader.ReadInt32(); //讀取 Sample Rate int fmtAvgBPS = reader.ReadInt32(); //讀取 fmt 的平均 BPS int fmtBlockAlign = reader.ReadInt16(); //讀取 fmt Block Align 數據 int bitDepth = reader.ReadInt16(); //讀取位元深度 if (fmtSize == 18) //如果 fmt 大小為18 { int fmtExtraSize = reader.ReadInt16(); //讀取 fmt 額外的大小 reader.ReadBytes(fmtExtraSize); //讀入等同於 fmt 額外大小的資料 } int dataID = reader.ReadInt32(); //讀取音效資料 ID int dataSize = reader.ReadInt32(); //讀取音效資料大小 byteArray = reader.ReadBytes(dataSize); //將音效資料大小的音效資料讀入到緩衝區 dynamicSound = new DynamicSoundEffectInstance(sampleRate, (AudioChannels) channels); //依據 Sample Rate 和 Channel 建立 DynamicSoundEffectInstance 類別的物件 count = dynamicSound.GetSampleSizeInBytes( TimeSpan.FromMilliseconds(100)); //依據依據存放音效資料時間長度計算資料量 dynamicSound.BufferNeeded += new EventHandler<EventArgs>( DynamicSound_BufferNeeded); //指定處理 BufferNeeded 事件的事件處理程序 dynamicSound.Play(); //播放 DynamicSoundEffectInstance 類別的物件管理的音效 接下来请为 Game1 类别加入以下的方法,负责处理 DynamicSoundEffectInstance 类别的对象的 BufferNeeded 事件: void DynamicSound_BufferNeeded(object sender, EventArgs e) { dynamicSound.SubmitBuffer(byteArray, position, count); //從 byteArray 陣列第 position 位元組開始,取出 count 個位 //元組供 DynamicSoundEffectInstance 類別的物件進行播放 position += count; //遞增 position 變數的內容值 if (position + count > byteArray.Length) //如果 byteArray 陣列的內容已全部播放完畢 { position = 0; //將 position 變數的內容值歸零 } } [注意] 使用上述的方式播放音效数据流,以 XNA 为基础的游戏程序仍然需要将欲播放的 WAV 格式的声音文件预先加入到 Content Pipeline 项目,但是必须将 WAV 格式的声音文件的 [Build Action] 属性的内容值从默认的 Compile 改成 Content,再将 [Copy to Output Directory] 属性的内容值改成:Copy always,表示不要将 WAV 格式的声音文件建置成 XNB 格式的二进制数据,而要随着游戏程序一起部署到 Windows Phone 7 智能型手机,供游戏程序执行时期加载使用,并直接从 WAV 格式的声音文件加载欲播放的音效。 做好之后请按下 Ctrl+F5 组合键执行游戏程序,就可以听到 DynamicSoundEffectInstance 类别的对象播放的动态音效。 XNA Game Studio (4.0) 提供的 Microphone 类别可以协助程序利用 Windows Phone 7 内建的麦克风录制外界输入的声音。有关 Microphone 类别常用的属性可以参考表4 的说明: 表4:Microphone 类别常用的属性 属性名称 说明 All 取得所有可用的麦克风。 BufferDuration 存放从麦克风输入的音频数据的时间长度。 Default 取得默认使用的麦克风。 IsHeadset 判断是否为耳机式麦克风。 SampleRate 取得使用麦克风执行录音时的 Sample Rate。 State 取得麦克风录音的状态。 Microphone 类别常用的方法可以参考表5 的说明: 表5:Microphone 类别常用的方法 方法名称 说明 GetData 从麦克风取得最新录制的声音资料。 GetSampleDuration 查询缓冲区中待播放的音效数据的时间长度。 GetSampleSizeInBytes 查询利用麦克风录制指定时间长度的声音需要的字节数。 Start 开始录制声音的动作。 Stop 结束录制声音的动作。 Microphone 类别常用的事件请参考表6的说明: 表6:Microphone 类别常用的事件 方法名称 说明 BufferReady 当存放录制声音数据的缓冲区准备好可供播放时引发的事件。 接下来我们就要利用 Microphone 类别提供的类别,录制透过 Windows Phone 7 内建的麦克风输入的声音,供程序进行播放。 首先请于 Game1 类别宣告以下的变量,负责管理 DynamicSoundEffectInstance 类别的对象: DynamicSoundEffectInstance sound; 然后于 Game1 类别的 Initialize 方法加入以下的程序代码,负责启动录音的动作: sound = new DynamicSoundEffectInstance(Microphone.Default.SampleRate, AudioChannels.Mono); //利用麥克風預設的 Sample Rate 錄製單聲道的聲音 //處理錄製妥透過麥克風輸入的聲音引發的 BufferReady 事件 Microphone.Default.BufferReady += (a, b) => { byte[] data = new byte[Microphone.Default.GetSampleSizeInBytes( Microphone.Default.BufferDuration)]; //建立存放聲音資料的位元組陣列 Microphone.Default.GetData(data); //取得錄製的聲音,並儲存到 data 位元組陣列中 sound.SubmitBuffer(data); //將位元組陣列的內容交給 DynamicSoundEffectInstance 類 //別的物件進行播放 }; Microphone.Default.Start(); //啟動麥克風執行錄製聲音的工作 最后我们只要在 Game1 类别的 Update 方法中加入以下的程序代码,在录制5秒钟的声音之后停止录制的动作,并将录制妥的声音交由 DynamicSoundEffectInstance 类别的对象进行播放: if (gameTime.TotalGameTime.TotalSeconds > 5) //如果錄製的時間是否超過 5 秒 { Microphone.Default.Stop(); //關閉麥克風錄音的功能 sound.Play(); //將錄製妥的聲音交由 DynamicSoundEffectInstance 類別的物件進行播放 } 做好之后请执行程序,然后对麦克风讲话,5 秒钟之后,程序就会播放刚刚录制的声音。 范例下载:AdvSoundEffect.zip 认识 XACT (Cross-platform Audio Creation Tool) 声音组识与管理工具 XNA Game Studio 除了提供类别库供游戏程序制作所需要的功能以外,也提供了一系列辅助游戏开发的工具,包括:Microsoft Cross-Platform Audio Creation Tool 3 (XACT3)、XACT Auditioning Utility、和 XNA Framework Remote Performance Monitor,其中的 Microsoft Cross-Platform Audio Creation Tool 3 是一个支持声音组识与管理的工具,可以将游戏程序需要使用的音效事先组织妥当,并设定好必要的属性,降低游戏程序制作因管理音效的播放衍生的复杂性。 [注意] Windows Phone 7 平台目前尚未支持使用 Microsoft Cross-Platform Audio Creation Tool 3 工具建立的声音文件,所以使用 Microsoft Cross-Platform Audio Creation Tool 3 工具建立的声音文件只能供在 Windows 平台和 Xbox 360 游戏平台上执行的游戏程序使用。 要使用 Microsoft Cross-Platform Audio Creation Tool 3 工具组识和管理游戏程序执行时期欲播放的音效,请执行 [所有程序 | Microsoft Game Studio 4.0 | Tools | Microsoft Cross-Platform Audio Creation Tool 3 (XACT3)] 功能,启动 XACT3 工具,然后执行 [File | New Project] 功能建立新项目。项目建立成功之后请使用鼠标的右键点中名称为 [Wave Banks] 的项目,从出现的菜单选择 [New Wave Bank] 功能,执行建立 Wave Bank 的工作,然后从档案总管将欲管理的 .WAV 格式的声音档案拖曳至新建立的 Wave Bank 中。做好之后请使用鼠标的右键点中名称为 [Sound Banks] 的项目,从出现的菜单选择 [New Sound Bank],执行建立 Sound Bank 的工作,建立好 Sound Bank 之后,请从 Wave Bank 窗口将欲管理的声音文件拖曳至新建立的 Sound Bank 窗口的下方窗口中,得到一个预设的 Cue Name,请注意 Cue Name 即为游戏程序播放 XACT3 管理的音效的名称。当我们从 Wave Bank 窗口将欲管理的声音文件拖曳至新建立的 Sound Bank 窗口的下方窗口时,Sound Bank 窗口的上方窗口也会自动加入一个新项目,您可以点选这个项目,再利用 XACT3 工具左下角的窗口设定音效的属性,例如您可以设定音效重复播放的次数 (设定 Looping 属性,勾选 Infinite 字段表示要不断地重复播放),如图1 所示: 图1:使用 XACT3 工具管理游戏程序欲使用的音效的画面 设定完成后请储存项目,然后将 XACT3 工具关闭,所储存的项目是一个扩展名为 .xap 的档案,我们只要将这个档案加入到游戏程序的 Content Pipeline 项目中,就可以供游戏程序执行的时候加载播放。 要使用 Content Pipeline 项目中利用 XACT3 工具建立的声音文件,游戏程序可以在 Game1 类别中宣告下列的变量: AudioEngine audioEngine; //管理音效播放工作的變數 WaveBank waveBank; //管理 Wave Bank 內容的變數 SoundBank soundBank; //管理 Sound Bank 內容的變數 然后在 Game1 类别的 Initialize 方法中加入以下的程序代码,建立管理音效播放工作的对象、管理 Wave Bank 内容的对象、以及管理 Sound Bank 内容的对象: audioEngine = new AudioEngine("Content\\MyAudio.xgs"); //建立 AudioEngine 類別的物件 waveBank = new WaveBank(audioEngine, "Content\\Wave Bank.xwb"); //建立 Wave Bank 類別的物件 soundBank = new SoundBank(audioEngine, "Content\\Sound Bank.xsb"); //建立 Sound Bank 類別的物件 请注意建立 AudioEngine 类别对象时指定的是 XACT3 的项目名称,其扩展名为 .xgs,建立 WaveBank 类别的对象时指定的是 Wave Bank 的名称,其扩展名为 .xwb,而建立 SoundBank 类别的对象时指定的是 Sound Bank 的名称,其扩展名为 .xsb。 建立好必要的对象之后,请于 Game1 类别的 Update 方法加入以下的程序代码,准备欲播放的音效内容,并呼叫 AudioEngine 类别对象的 PlayCue 方法执行播放音效的动作: audioEngine.Update(); soundBank.PlayCue("RingOut"); 请注意呼叫 AudioEngine 类别对象的 PlayCue 方法所传入的是 Cue Name,意即要播放 Cue Name 所代表的声音文件。做好之后只要执行程序就可以听到程序播放 XACT3 工具管理的音效。

设计支持手势操作的 XNA 游戏

clock 七月 31, 2011 23:13 by author alex
  摘要 上一回我们为大家介绍了更多的 XNA Framework 支持游戏开发的类别,包括支持输入控制,音效播放,以及背景音乐播放控制的类别等等,让读者能够为所制作的游戏程序加入更丰富的游戏效果。这一回我们将要为大家介绍进阶的输入控制技巧,让使用者可以利用 Windows Phone 7 智能型手机支持多点触控的触控屏幕控制游戏程序的执行。 认识手势操作 在上一回的介绍中,我们学会如何利用 TouchPanel 类别的 GetState 方法查询智能型手机的触摸屏的状态,并依据使用者触碰在触控屏幕的位置移动游戏程序显示的图形的位置。 呼叫 TouchPanel 类别的 GetState 方法查询触控屏幕的状态并判断用户触碰触控屏幕的位置只是最简单的触控屏幕控制技巧,除了支持取得触控屏幕的状态以外,触控屏幕还支持用户进行多种不同的控制,包括触碰、触碰不放、水平拖曳、垂直拖曳、自由拖曳、以及轻拂等操作。表1 所示即为触控屏幕的各种状态的说明: 表1:触控屏幕的各种状态的说明 操作动作 说明 Tap 触碰。触碰触控屏幕后放开,没有移动的动作。 DoubleTap 连续触碰。连续触碰同一个位置两次。 Hold 点住不放。触碰后不放达一段时间。 VerticalDrag 垂直拖曳。触碰屏幕后上下移动。 HorizontalDrag 水平拖曳。触碰屏幕后左右移动。 FreeDrag 自由拖曳。触碰屏幕后往任意方向移动 DragComplete 拖曳结束。 Flick 轻拂。触碰屏幕后往任意方向拂动后离开屏幕。 Pinch 同时便用两个手指头触碰触控屏幕后移动。 PinchComplete Pinch 操作结束。 以 XNA 为基础的游戏程序可以利用表1所列的各种触控屏幕状态判断用户执行的触控操作种类,以反应使用者的触控操作。 [注意] 以 XNA 为基础的游戏程序必须启用触控功能才能够让游戏的使用者进行触控操作,如果已启用 Pinch 操作功能,则当用户利用两个手指头同时触碰触控屏幕并进行移动时,就会产生 Pinch 操作,而不是两个不同的拖曳操作,如果未启用 Pinch 操作功能,则所产生的就不是 Pinch 操作,而是依据两个触碰位置的平均为准的单一拖曳操作。 启用手势操作支持 以 XNA 为基础的游戏程序必须设定 TouchPanel 类别的 EnabledGestures 属性,才能够启用手势操作功能,以支持用户以手势操作游戏程序。 程序设计师可以在 Game1 类别的 Initialize 方法执行设定 TouchPanel 类别的 EnabledGestures 属性的动作,以启用手势操作支持,做法如下: TouchPanel.EnabledGestures = GestureType.Hold | GestureType.Tap | GestureType.DoubleTap | GestureType.FreeDrag | GestureType.Flick | GestureType.Pinch; [说明] 请注意在上述的程序中,GestureType.FreeDrag 设定表示要支持使用者以自由拖曳的方式操作游戏程序,设定了 GestureType.FreeDrag 就已经涵盖 GestureType.VerticalDrag 设定和 GestureType.HorizontalDrag 设定。而 DragComplete 状态和 PinchComplete 状态代表触控动作结束的状态,不需要启用。 [注意] 以 XNA 为基础的游戏程序可以视需要启用需要使用的触控功能,例如只支持用户利用触控的方式选取菜单的游戏程序,就可以仅启用 Tap 和 VerticalDrag 两种触控功能,让用户以垂直拖曳的方式卷动游戏程序提供的菜单,再触碰欲选择的菜单,其他不需要用到的触控控制功能就不需要启用,避免启用多种触控操作功能,造成判断触控操作动作的逻辑复杂,进而影响到触控的精确度和游戏程序执行的效能。 处理使用者的手势操作 启用了手势操作功能之后,以 XNA 为基础的应用程序可以在 Game1 类别的 Update 方法中呼叫 TouchPanel 类别的 ReadGesture 方法取得用户的手势操作信息。请注意读取使用者的手势操作的做法和呼叫 TouchPanel 类别的 GetState 方法读取触控面板的状态的做法不同,因为用户对游戏程序的触控操作会产生多个手势信息,来不及被游戏处理的手势信息会被存放到队列中等待处理,让游戏程序利用循环取出并加以处理。 以下的 Update 方法便会利用 while 循环,搭配 TouchPanel 类别的 IsGestureAvailable 属性判断是否还有用户触控操作产生的手势信息尚未被处理,如果尚有使用者触控操作产生的手势信息尚未被处理,则呼叫 TouchPanel 类别的 ReadGesture 方法读取手势信息,并加以处理: protected override void Update(GameTime gameTime) { … while (TouchPanel.IsGestureAvailable) //判斷是否尚有手勢資訊尚未被處理 { GestureSample gesture = TouchPanel.ReadGesture(); //讀取尚未處理的手勢資訊 switch (gesture.GestureType) //判斷手勢操作的種類 { case GestureType.Tap: //如果手勢操作的種類是Tap //處理 Tap 操作 break; … } } } 因为 GestureType.FreeDrag 自由拖曳操作已经包括 GestureType.VerticalDrag 垂直拖曳操作和 GestureType.HorizontalDrag 水平拖曳操作,所以游戏程序在判断用户的触控操作的动作时,不需要既判断动作是否为 GestureType.FreeDrag,又判断动作是否为 GestureType.VerticalDrag 或 GestureType.HorizontalDrag,两者择一处理即可。 [特别注意] 使用 TouchPanel 类别进行触控控制的游戏程序可以呼叫 TouchPanel 类别的 GetState 方法取得使用者对触控面板的触控状态,或是呼叫 TouchPanel 类别的 ReadGesture 方法取得使用者的手势操作状态,不要两者混用,否则将会无法得到正确的结果。例如先利用 TouchPanel 类别的 ReadGesture 方法取得使用者的手势操作状态,再利用 TouchPanel 类别的 GetState 方法取得用户触碰屏幕的位置,当做手势操作触碰屏幕的位置来使用就是错误的做法。 请注意呼叫 TouchPanel 类别的 ReadGesture 方法读取手势信息时,读取到的手势信息会以 GestureSample 结构的型式传回给呼叫者。表2 所示为 GestureSample 结构常用的属性: 表2:GestureSample 结构常用的属性 属性名称 说明 Delta 存放与第一个碰触点的偏差量。 Delta2 存放与第二个碰触点的偏差量。 GestureType 存放触控操作的种类。 Position 存放第一个碰触点的位置。 Position2 存放第二个碰触点的位置。 Timestamp 存放触控操作发生的时间。 因为呼叫 TouchPanel 类别的 ReadGesture 方法传回的 GestureSample 结构只能存放第一个和第二触碰点的位置:Position 和 Postition2,以及存放两个触碰点的偏差量,所以只能支持到最多两个手指头的操作,事实上 Windows Phone 7 配备的触控屏幕最多可以支持到多达四个触碰点的触控操作,程序可以经由呼叫 TouchPanel 类别的 GetCapabilities 方法查询触控屏幕的基本功能,取得 TouchPanelCapabilities 结构型态的传回值之后,就可以利用 TouchPanelCapabilities 结构的 MaximumTouchCount 成员得知触控屏幕最大支持的触碰点数,做法如下: TouchPanelCapabilities tcs = TouchPanel.GetCapabilities(); //查詢觸控螢幕的基本功能 有关 TouchPanelCapabilities 结构常用的属性请参考表3 的说明: 表3:TouchPanelCapabilities 结构常用的属性 属性名称 说明 IsConnected 查询触控屏幕的可用状态。 MaximumTouchCount 取得支持使用者同时触碰触控屏幕的触碰点数量。 如果需要取得用户同时触碰屏幕的所有坐标点,则可以呼叫 TouchPanel 类别的 GetState 方法,取得所有触碰点的集合,再利用循环取出集合中的所有触碰点并加以处理,做法如下: TouchCollection tc = TouchPanel.GetState(); //取得觸控螢幕的狀態 foreach (TouchLocation tl in tc) //取得所有的觸碰點 { //利用 tl.Position 取得觸碰點座標 } [说明] 请注意 GestureSample 结构的属性的型态为 TimeSpan 结构而不是 GameTime 类别,用来代表手势操作与手势操作之间的时间间隔。 各种手势操作需要用到的 GestureSample 结构的属性可以参考表4 的详细说明: 表4:各种手势操作需要用到的 GestureSample 结构的属性 触控操作 说明 Tap Position 属性 DoubleTap Position 属性 Hold Position 属性 VerticalDrag Position 属性和 Delta 属性 HorizontalDrag Position 属性和 Delta 属性 FreeDrag Position 属性和 Delta 属性 DragComplete 无 Flick Delta 属性 Pinch Position、Position2、Delta、和 Delta 2 四个属性 PinchComplete 无 处理手势操作的要诀 在处理使用者的触控操作方面,Flick 触控操作可以利用 GestureSample 结构的 Delta 属性的内容值当做用户轻拂的快慢速度,以控制卷动游戏内容的速度。VerticalDrag 和 HorizontalDrag 触控操作可以利用 GestureSample 结构的 Delta 属性的内容值判断垂直和水平移动的距离,而且 VerticalDrag 触控操作的 Delta 属性的 X 成员的内容值必为 0,而 HorizontalDrag 触控操作的 Delta 属性的 Y 成员的内容值必为 0,不需要另外透过程序代码进行设定。Pinch 触控操作是两个手指头并用的触控操作,常常用来执行旋转对象、放大缩小对象、或是旋转相机镜头的动作。游戏程序可以利用 GestureSample 结构的 Position 属性和 Delta 属性,取得第一个手指头的触碰点和偏差量,利用 Position2 属性和 Delta2 属性取得第二个手指头的触碰点和偏差量,再据以执行变更游戏程序显示的内容的动作。 设计支持手势操作的 XNA 游戏 了解 XNA Framework 支持触控操作的基本功能之后,接下来我们就要设计一个能够允许用户利用触控屏幕操作的简单游戏。 首先请启动 Microsoft Visual Studio 2010 Express,建立型态为 [XNA Game Studio(4.0)] 型态的项目,然后于 Content Pipeline 项目中加入让使用者进行触控操作的图形的圆形档案,以及当圆形碰撞到游戏程序的窗口时欲发出的声响的声音文件。例如本范例准备了两个圆形的图案,名称为 Ball.png 的图形档案是一个圆形,名称为 HoldBall.png 的图形档案是一个中心有一个红点的圆形,用来表示被点选的图形,而 Hitwall.wav 则是当圆形的图案碰撞到游戏程序的窗口时欲发出的声音的声音文件。 将游戏程序需要使用的资源加入到 Content Pipeline 项目之后,请开启游戏项目中的 Game1.cs 档案,于类别中加入以下的变量和属性宣告: Texture2D Ball; //管理圓形圖案的變數 Texture2D HoldBall; //管理中心有紅點的圓形圖案的變數 Vector2 BallPosition=Vector2.Zero; //記錄圓形圖案位置的變數 bool isHold = false; //記錄使用者是否執行Hold式的觸控操作的變數 SoundEffect HitWall; //管理圖案碰撞遊戲程式視窗的音效的變數 SoundEffectInstance HitWallEffect; //管理欲播放的音效的變數 Vector2 Velocity = Vector2.Zero; //記錄Flick操作速度的變數 public const float Friction = 0.9f; //記錄摩擦力的變數 public const float BounceMagnitude = .5f; //記錄反彈速度的變數 public const float MinScale = .5f; //記載最小縮小比例的變數 public const float MaxScale = 2f; //記載最大放大比例的變數 private float scale = 1f; //記載目前比例的變數 public float Scale //記載目前比例的屬性 { get { return scale; } set { scale=MathHelper.Clamp(value, MinScale, MaxScale);//控制縮放的比例的最大/最小值 } } 准备好游戏程序需要的变量之后,请修改 Game1 类别的建构函式,在建立 GraphicsDeviceManager 类别的对象之后设定 GraphicsDeviceManager 类别的对象的 PreferredBackBufferHeight 属性和 graphics.PreferredBackBufferWidth 属性,将游戏程序的窗口设定成宽 480 x 高 800 的大小。编辑妥的 Game1 类别建构函式如下: public Game1() { graphics = new GraphicsDeviceManager(this);//建立GraphicsDeviceManager類別的物件 Content.RootDirectory = "Content"; //設定載入遊戲資源的根路徑 graphics.PreferredBackBufferHeight = 800; //設定遊戲程式視窗的高度 graphics.PreferredBackBufferWidth = 480; //設定遊戲程式視窗的寬度 // Frame rate is 30 fps by default for Windows Phone. TargetElapsedTime = TimeSpan.FromTicks(333333); //設定 Update 方法被呼叫的頻率 } 设定妥游戏程序窗口的大小之后请编辑 Game1 类别的 Initialize 方法,负责启用手势操作的功能,编辑妥的 Initialize 方法如下: protected override void Initialize() { // TODO: Add your initialization logic here TouchPanel.EnabledGestures = GestureType.Hold | GestureType.Tap | GestureType.DoubleTap | GestureType.FreeDrag | GestureType.HorizontalDrag | GestureType.Flick | GestureType.Pinch; //啟用手勢操作功能 base.Initialize(); } 启用手势操作功能之后请于 Game1 类别的 LoadContent 方法加入加载游戏资源的程序代码,编辑好的 LoadContent 方法如下: protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here Ball = Content.Load<Texture2D>("Ball"); //載入圓形圖案 HoldBall = Content.Load<Texture2D>("HoldBall"); //載入中心有紅點的圓形圖案 BallPosition = new Vector2((Window.ClientBounds.Width - Ball.Width) / 2, (Window.ClientBounds.Height-Ball.Height)/2);//設定圓形圖案要顯示在視窗的正中央 // TODO: use this.Content to load your game content here HitWall = Content.Load<SoundEffect>("HitWall");//載入圓形圖案撞擊視窗邊界要發出的音效檔案 HitWallEffect = HitWall.CreateInstance(); //利用 SoundEffect 類別的物 //件建立 SoundEffectInstance 類別的物件 HitWallEffect.Apply3D(new AudioListener(), new AudioEmitter()); //呼叫SoundEffectInstance類別的 Apply3D 方法套用 3D 音效 } 您可以视需要修改上述的程序代码加载的资源名称,以加载游戏程序执行时需要的资源。 加载妥游戏程序需要使用的资源之后,请修改名称为 Update 的方法,以反应使用者的手势操作动作。加入手势控制功能的 Update 方法如下: protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // TODO: Add your update logic here while (TouchPanel.IsGestureAvailable) //判斷使用者是否執行了手勢操作 { GestureSample gesture = TouchPanel.ReadGesture(); //讀取使用者的手勢操作 switch (gesture.GestureType) //判斷使用者的手勢操作型態 { case GestureType.Tap: //Tap 操作 case GestureType.DoubleTap: //DoubleTap 操作 BallPosition.X=gesture.Position.X-Ball.Width/2; //設定圖案顯示在使用 BallPosition.Y=gesture.Position.Y-Ball.Height/2; //者觸碰的座標點 break; case GestureType.Hold: //Hold 操作 Rectangle BallRect = new Rectangle(0, 0, (int)(Ball.Width * Scale), (int)(Ball.Height * Scale)); //計算圖形的矩形大小 if (BallRect.Contains((int)(gesture.Position.X - BallPosition.X), (int)(gesture.Position.Y - BallPosition.Y)))//判斷觸碰點是否在矩形中 { isHold = true; //將 IsHold 變數的內容值設定為 true } break; case GestureType.FreeDrag: //FreeDrag 操作 BallPosition += gesture.Delta; //循著使用者拖曳的軌跡移動圖形 break; case GestureType.Flick: //Flick 操作 Velocity = gesture.Delta; //設定移動圖形的速度 break; case GestureType.Pinch: //Pinch 操作 Vector2 a = gesture.Position; //取得第一個觸碰點 Vector2 aOld = gesture.Position - gesture.Delta;//取得第一個觸碰點的起始位置 Vector2 b = gesture.Position2; //取得第二個觸碰點 Vector2 bOld = gesture.Position2 - gesture.Delta2;//取得第二個觸碰點的起始位置 float d = Vector2.Distance(a, b); //計算兩個觸碰點之間的距離 float dOld = Vector2.Distance(aOld, bOld);//計算兩個原始座標之間的距離 float scaleChange = (d - dOld) * .01f; //計算距離的變化量 Scale += scaleChange; //將距離變化量的 1/10 當做縮放的比例 break; } } BallPosition += Velocity * (float)gameTime.ElapsedGameTime.TotalSeconds; //依據Flick操作的速度移動圖形 Velocity *= 1f - (Friction * (float)gameTime.ElapsedGameTime.TotalSeconds);//利用磨擦係數減緩圖形的移動速度 float HalfWidth = (Ball.Width * Scale) / 2f; //取得圖形寬度的 1/2 float HalfHeight = (Ball.Height * Scale) / 2f; //取得圖形高度的 1/2 if (BallPosition.X < Window.ClientBounds.Left) //判斷圖形是否觸及視窗的左邊界 { BallPosition.X=Window.ClientBounds.Left;//設定圖形左上角點的X座標等於視窗左邊界 Velocity.X *= -BounceMagnitude; //設定反彈的速度為目前速度的一半 HitWallEffect.Play(); //播放碰撞視窗邊界的音效 } if (BallPosition.X > Window.ClientBounds.Right – Ball.Width * Scale) //判斷圖形是否觸及視窗的右邊界 { BallPosition.X = Window.ClientBounds.Right – Ball.Width * Scale; //設定圖形右上角點的X座標等於視窗右邊界 Velocity.X *= -BounceMagnitude; //設定反彈的速度為目前速度的一半 HitWallEffect.Play(); //播放碰撞視窗邊界的音效 } if (BallPosition.Y < Window.ClientBounds.Top) //判斷圖形是否觸及視窗的上邊界 { BallPosition.Y = Window.ClientBounds.Top;//設定圖形左上角點的Y座標等於視窗上邊界 Velocity.Y *= -BounceMagnitude; //設定反彈的速度為目前速度的一半 HitWallEffect.Play(); //播放碰撞視窗邊界的音效 } if (BallPosition.Y > Window.ClientBounds.Bottom – Ball.Height * Scale) //判斷圖形是否觸及視窗的下邊界 { BallPosition.Y = Window.ClientBounds.Bottom – Ball.Height * Scale; //設定圖形右下角點的Y座標等於視窗下邊界 Velocity.Y *= -BounceMagnitude; //設定反彈的速度為目前速度的一半 HitWallEffect.Play(); //播放碰撞視窗邊界的音效 } base.Update(gameTime); } 最后我们还要修改名称为 Draw 的方法,将游戏程序显示的图形显示在反应用户手势操作的位置上,做法如下: protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(); //開始繪製遊戲內容 if (isHold) //如果圖形被使用點中 { spriteBatch.Draw(HoldBall, BallPosition, null, Color.White, 0, Vector2.Zero, Scale, SpriteEffects.None, 0); //在指定的位置顯示中心有紅點的圓形 } else { spriteBatch.Draw(Ball, BallPosition, null, Color.White, 0, Vector2.Zero, Scale, SpriteEffects.None, 0); //在指定的位置顯示圓形 } spriteBatch.End(); //結束繪製遊戲內容 base.Draw(gameTime); } 做好之后请执行 [建置] 项目的动作。 将设计妥的游戏程序部署到 Windows Phone 7 智能型手机 因为 Windows Phone 7 仿真器不支持用户触控操作,所以允许使用者利用手势进行游戏操作的游戏程序必须部署到真实的 Windows Phone 7 智能型手机才能测试和手势操作有关的功能是否正确,无法透过 Windows Phone 7 仿真器进行测试,因为在一般计算机上执行的 Windows Phone 7 仿真器无法接受用户透过手势动作进行操作。 [提示] 如果用户所使用的计算机配备的是触控屏幕,则在配备触控屏幕的计算机执行的 Windows Phone 7 仿真器仍然可以允许用户透过触控屏幕进行操作。 欲将开发妥的游戏程序部署到实际的 Windows Phone 7 智能型手机,而不是 Windows Phone 7 仿真器,程序设计师使用的计算机必须先安装 Zune 软件。安装完成后请重新启动计算机,并将 Windows Phone 7 智能型手机连上开发计算机的 USB 端口,所安装的 Zune 软件将会在 Windows Phone 7 智能型手机连上计算机时自动启动。图1 所示即为 Zune 软件激活后的执行画面: 图1:Zune 执行的画面 Zune 自动启动之后,请执行 [所有程序 | Windows Phone Developer Tools] 群组中的 [Application Deployment] 程序,并在 [Application Deployment] 程序启动之后于 [Target] 下拉式选项中选择:Windows Phone 7 Device,再按下 [Browse] 键浏览到建置项目成功得到的 XAP 档案,如图2 所示: 图2:使用 [Application Deployment] 程序部署 Windows Phone 7 游戏程序的画面 做好之后按下 [Deploy] 键将所选择的 Windows Phone 7 游戏程序部署到 Windows Phone 7 智能型手机。请注意 Windows Phone 7 智能型手机不可处于屏幕锁定的状态,否则将无法部署成功。 [提示] 除了可以利用 [Application Deployment] 程序部署 Windows Phone 7 游戏程序到 Windows Phone 7 智能型手机以外,程序设计师也可以利用 Visual Studio 2010 Express for Windows Phone 直接将所开发的程序部署到 Windows Phone 7 智能型手机,不过程序设计师所使用的计算机仍然必须事先安装好 Zune 软件。程序设计师可以利用 [XNA Game Studio Device Management] 工具栏提供的下拉选项 [ ] 选择到 Windows Phone 7 Device,再按下 CTRL+F5 组合键将开发好的程序部署到 Windows Phone 7 智能型手机。同样地,Windows Phone 7 智能型手机不可以处于屏幕锁定的状态,否则将无法部署成功。 请执行部署到 Windows Phone 7 智能型手机的游戏程序,并利用 XNA Framework 支持的各种手势操作技巧点选、自由移动、轻拂、或放大缩小游戏程序显示的饼图案,体验利用手势操作程序的高度方便性。图3所示即为允许用户利用手势操作的程序执行的情形: 图3:允许用户利用手势操作的程序执行的情形 请注意当饼图案被轻拂至碰撞到程序的窗口时会发出声响并产生反弹。 程序下载:GestureControl.zip

XNA 交互式游戏设计

clock 七月 31, 2011 23:11 by author alex
摘要 上一回我们为大家介绍了 XNA Framework 支持游戏程序制作的重要类别,做为开发游戏程序的基础知识。除此之外,我们也利用所介绍的类别实作一个简单的游戏程序,并令其具有能够不断卷动的背景图案。这一回我们将会介绍更多 XNA Framework 支持游戏开发的类别,包括支持输入控制,音效播放,以及背景音乐播放控制的类别等等,让读者能够为所制作的游戏程序加入更丰富的游戏效果。 输入控制 游戏程序难免需要和用户互动,让使用者可以藉由输入设备,例如键盘、触控面板、鼠标、或是游戏控制器进行游戏控制,与游戏程序达到互动的效果。要让用户控制游戏程序的进行,可以利用 Keyboard 类别搭配 KeyboardState 结构,利用 TouchPanel 类别搭配 TouchCollection 结构,利用 Mouse 类别搭配 MouseState 结构,或是利用 GamePad 类别搭配 GamePadState 结构,对所操作的游戏程序进行控制。 Keyboard 类别的功能很简单,只提供名称为 GetState 的方法以取得用户操作键盘的按键状态。游戏程序可以呼叫 Keyboard 类别的 GetState 方法取得键盘的状态 (型态为 KeyboardState 结构的传回值),以判断使用者按了那一个键。KeyboardState 结构的常用属性请参考表1 的说明: 表1:KeyboardState 结构的常用属性 属性名称 说明 Item 代表键盘的某个按键的按键状态 KeyboardState 结构的常用方法请参考表2 的说明: 表2:KeyboardState 结构的常用方法 方法名称 说明 GetPressedKeys 取得目前所有被按下的按键 IsKeyDown 判断指定的按键是否已被按下 IsKeyUp 判断指定的按键是否已被放开 要判断用户操作鼠标的做法和判断用户操作键盘的操作类别,游戏程序可以先呼叫 Mouse 类别的 GetState 方法取得鼠标的状态 (型态为 MouseState 结构的传回值),再透过鼠标的状态判断用户的操作动作。有关 Mouse 类别常用的方法可以参考表3 的说明: 表3:Mouse 类别常用的方法 方法名称 说明 GetState 取得包括鼠标坐标在内的鼠标按键状态 SetPosition 设定鼠标光标相对于游戏窗口左上角的位置 MouseState 结构常用的属性请参考表4 的说明: 表4:MouseState 结构常用的属性 方法名称 说明 LeftButton 鼠标左键的按键状态 MiddleButton 鼠标中间键的按键状态 RightButton 鼠标右键的按键状态 ScrollWheelValue 记录从游戏开始执行迄今,鼠标滚轮累计的卷动距离 X 鼠标光标的 X 坐标位置 Y 鼠标光标的 Y 坐标位置 虽然 Windows Phone 7 智能型手机并未配备鼠标,但是游戏程序还是仍然能够使用 Mouse 类别与 MouseState 结构判断用户操作鼠标的动作,其效果相当于使用者对智能型手机进行触控操作。GamePad 类别和 GamePadState 结构也一样,虽然 Windows Phone 7 智能型手机未配备游戏控制器,但是游戏程序仍然能够使用 GamePad 类别和 GamePadState 结构判断用户操作游戏控制器的动作,当使用者按下 Windows Phone 7 智能型手机下缘的硬件按键,其效果相当于透过游戏控制器和游戏程序沟通。例如在使用 Visual Studio 2010 Express for Windows Phone 建立的 Windows Phone Game (4.0) 的项目中,Game1 类别的 Update 方法便会利用以下的程序代码判断使用者是否按下智能型手机下缘回到上一个画面的按键,如果是的话,则结束游戏程序的执行: if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) //判斷使用者是否按下回到上一個畫面的按鍵 this.Exit(); //結束遊戲 GamePad 类别常用的方法可以参考表 5 的说明: 表5:GamePad 类别常用的方法 方法名称 说明 GetCapabilities 查询游戏控制器支持的功能的方法 GetState 取得游戏控制器的状态 SetVibration 设定游戏控制器震动马达的速度 GamePadState 结构常用的属性可以参考表6 的说明: 表6:GamePadState 结构常用的属性 属性名称 说明 Buttons 取得游戏控制器所有按键的状态 DPad 取得方向指引器指引的方向 IsConnected 判断游戏控制器是否已连接到游戏设备 ThumbSticks 取得游戏控制器的摇捍的位置 Triggers 取得游戏控制器的触控钮的位置 GamePadState 结构常用的方法可以参考表7 的说明: 表7:GamePadState 结构常用的方法 方法名称 说明 IsButtonDown 判断指定的游戏控制器按钮是否被按下 IsButtonUp 判断指定的游戏控制器按钮是否已放开 Windows Phone 7 是一只支持多点触控智能型手机,程序设计师可以利用 TouchPanel 类别搭配 TouchCollection 结构判断用户的触控操作。TouchPanel 类别常用的属性可以参考表8 的说明: 表8:TouchPanel 类别常用的属性 属性名称 说明 DisplayHeight 代表触控屏幕高度的属性 DisplayOrientation 代表触控屏幕方向的属性 DisplayWidth 代表触控屏幕宽度的属性 EnabledGestures 代表手势操作是否启用的属性 IsGestureAvailable 代表是否支持手势操作的属性 TouchPanel 类别常用的方法可以参考表9 的说明: 表9:TouchPanel 类别常用的方法 方法名称 说明 GetCapabilities 查询触控面板支持的功能的方法 GetState 取得触控面板目前的状态的方法 ReadGesture 读取用户手势操作的相关信息的方法 使用者对 Windows Phone 7 智能型手机进行触控及手势操作的触控信息会存放在 TouchCollection 结构型态的变量中,程序设计师可以利用 TouchCollection 结构提供的属性和方法取得用户的触控和手势操作的相关信息。TouchCollection 结构常的属性可以参考表10 的说明: 表10:TouchCollection结构常用的属性 属性名称 说明 Count 取得触控屏幕被用户触碰的位置的数目 IsConnected 判断触控屏幕是否作用中 IsReadOnly 判断存放触控屏幕被用户触碰的位置的集合是否只读 Item 取得触控屏幕被用户触碰的位置的相关信息 TouchCollection 结构常的方法可以参考表11 的说明: 表11:TouchCollection 结构常用的方法 方法名称 说明 Add 新增一个触碰点到存放触碰位置的集合中 Clear 清除存放触碰点的集合的所有内容 Contains 清除存放触碰点的集合的所有内容 FindById 依据 ID 搜寻触碰点 IndexOf 判断指定的的触碰点位于存放触碰点的集合的编号 Insert 插入触碰点到存放触碰点的集合的指定位置 Remove 自存放触碰点的集合移除指定的触碰点 RemoveAt 依据编号自存放触碰点的集合移除指定的触碰点 了解以 XNA 为基础的游戏的输入控制技巧之后,接下来我们就要制作一个能够允许使用者利用触控的方式移动图形的程序。 首先请启动 Visual Studio 2010 Express for Windows Phone,建立一个 [Window Phone Game (4.0) 型态的项目],并于 Content Pipeline 项目加入一个图形档案 (例如内容为圆形的图形档案),做为用户利用触控面板移动的主体。做好之后请于 Game1 类别中宣告以下的变量,负责管理 2 维图形,以及记录 2 维图形的位置: Texture2D Ball; //管理 2 維圖形的變數 Vector2 BallPosition; //記錄 2 維圖形的位置 宣告好变量之后请修改 Game1 类别的建构函式,加入设定游戏窗口大小的程序代码,编辑好的 Game1 类别建构函式如下: public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferHeight = 800; //設定遊戲視窗的高度為 800 graphics.PreferredBackBufferWidth = 480; //設定遊戲視窗的寛度為 480 // Frame rate is 30 fps by default for Windows Phone. TargetElapsedTime = TimeSpan.FromTicks(333333); } 设定好游戏窗口的大小之后请修改 Game1 类别的 LoadContent 方法,负责加载 Content Pipeline 项目中事先准备好的图形,并设定图形要显示在游戏窗口的正中央: protected override void LoadContent() { //Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here Ball = Content.Load<Texture2D>("Ball"); //載入名稱為 Ball 的圖形資源 BallPosition = new Vector2((Window.ClientBounds.Width - Ball.Width) (Window.ClientBounds.Height-Ball.Height)/2); //設定圖形顯示的位置在視窗正中央 } [提示] 上述的程序代码利用 Game1 类别的 Window 属性取得游戏窗口,再利用游戏窗口的 ClientBounds 属性的 Width 成员和 Height 成员取得游戏窗口的大小,做为计算图形显示位置的依据。 [注意] 读者必须依据准备在 Content Pipeline 项目中的图形资源名称进行加载图形资源的动作。 protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // TODO: Add your update logic here TouchCollection tc=TouchPanel.GetState(); //取得使用者操作觸控面板的狀態 if (tc.Count > 0) //如果使用者有觸控螢幕的動作 { BallPosition = tc[0].Position; //將使用者第一個觸碰點當做顯示圖形的左上角點 } base.Update(gameTime); } 接下来请编辑 Game1 类别的 Draw 方法,将游戏加载的图形资源显示在用户触碰的屏幕坐标点,做好的 Draw 方法如下: protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(); //開始繪製遊戲內容 spriteBatch.Draw(Ball, BallPosition, Color.White); //將載入的圖形資源顯示在 BallPosition 變數指定的座標位置 spriteBatch.End(); //結束繪製遊戲內容 base.Draw(gameTime); } 做好之后请按下 CTRL+F5 组合键执行游戏程序,当游戏程序开始执行的时候会将所加载的图形显示在游戏窗口的正中央,当您触碰 Windows Phone 7 智能型手机的触控屏幕的时候,游戏程序显示的图形资源就会改变显示位置到触控屏幕被触碰的坐标点,如图1 所示: 图1:依据用户触碰触控屏幕的坐标点显示图形资源的游戏程序执行的情形 音效与音乐 以 XNA 为基础的游戏程序可以利用 SoundEffect 类别或 SoundEffectInstance 类别播放音效,或是利用 Song 类别和 MediaPlayer 类别执行播放音乐的动作,让游戏程序可以播放悦耳的背景音乐。 在支持音效播放方面,SoundEffectInstance 类别提供更多的音效播放控制方法,而 SoundEffect 类别只能提供单纯的音效播放功能。表12 所列为 SoundEffect 类别常用的属性: 表12:SoundEffect 类别常用的属性 属性名称 说明 DistanceScale 距离比例。当音源远离使用者时,如果声音衰减太快,则须递增 DistanceScale 属性的内容值,反之则须递减 DistanceScale 属性的内容值 DopplerScale Doppler 比例。当音源接近用户时,如果频率升高太快,则须递减 DopplerScale 属性的内容值,反之则须递增 DopplerScale 属性的内容值 Duration 控制音效播放时间长短的属性 MasterVolume 代表音量大小的属性。内容值为 0.0f 代表不发出声音 (静音),内容值为 1.0f 代表最大音量 SpeedOfSound 代表音速的属性,内容值为每秒 343.5 公尺 表13 所列为 SoundEffect 类别常用的方法: 表13:SoundEffect 类别常用的方法 方法名称 说明 CreateInstance 能够利用 SoundEffect 类别的对象建立 SoundEffectInstance 类别的对象的方法 FromStream 能够利用数据流建立 SoundEffect 类别的对象 Play 负责播放音效的方法 SoundEffect 类别支持控制音效播放的功能较少,例如只支持播放音效的 Play 方法而未支持停止音效播放 Stop 方法,而且也未提供重复播放音效的功能,如果游戏程序需要对所播放的音效执行进阶控制,就可以利用 SoundEffect 类别提供的 CreateInstance 方法建立 SoundEffectInstance 类别的对象,以便对所播放的音效执行更进阶的控制。 表14 所示即为 SoundEffectInstance 类别常用的属性: 表14:SoundEffectInstance 类别常用的属性 属性名称 说明 IsLooped 控制音效是否需要重复播放的属性 Pan 控制音效平移的属性 Pitch 控制音效的音调高低的属性 State 代表音效播放状态的属性。其内容值为 Paused、Playing、Stopped 三种状态之一 Volume 控制音效播放音量的属性 有关 SoundEffectInstance 类别常用的方法可以参考表15 的说明: 表15:SoundEffectInstance 类别常用的方法 方法名称 说明 Apply3D 播放 3D 音效 Pause 暂停音效播放 Play 播放音效 Resume 恢复音效播放 Stop 停止音效播放 如果游戏程序欲播放的是音乐,例如扩展名为 mp3 的音乐或歌曲当做游戏的背景音乐,就必须用到 Song 类别和 MediaPlayer 类别。Song 类别负责管理游戏程序欲播放的音乐或歌曲,其常用的属性请参考表16 的说明: 表16:Song 类别常用的属性 属性名称 说明 Duration 代表歌曲长度的属性 IsProtected 代表音乐是否为数字版权管理 (DRM) 的内容 Song 类别常用的方法可以参考表17 的说明: 表17:Song 类别常用的方法 方法名称 说明 FromUri 利用 Uri 指向的音乐建立 Song 类别的对象 建立好负责管理音乐或歌曲的 Song 类别的对象之后,就可以利用 MediaPlayer 类别进行播放与控制。MediaPlayer 类别常用的属性可以参考表18 的说明: 表18:MediaPlayer 类别常用的属性 属性名称 说明 IsMuted 控制是否要调整成静音模式的属性 IsRepeating 控制是否要重复播放音乐的属性 PlayPosition 取得目前播放位置的属性 State 代表目前播放状态的属性。其内容值为 Paused、Playing、Stopped 三种状态之一 Volume 控制播放音量的属性 MediaPlayer 类别常用的方法可以参考表19 的说明: 表19:MediaPlayer 类别常用的方法 方法名称 说明 Pause 暂停音乐播放 Play 播放音乐 Resume 恢复音乐播放 Stop 停止音乐播放 为游戏程序加入音效和背景音乐 了解以 XNA 为基础的游戏程序支持播放音效与音乐的基本功能之后,接下来我们就要为所开发的游戏程序加入背景音乐。 首先请为 [Windows Phone Game (4.0)] 项目中的 Content Pipeline 项目加入当做背景音乐与音效的资源文件,例如您可以为项目加入扩展名为 .wma 的档案当做游戏程序的音效资源,为项目加入扩展名为 .mp3 或 .wma 的档案当做游戏的背景音乐。有关 Content Pipeline 项目支持的音效与音乐资源格式可以参考 [认识 XNA Game Studio 4.0] 一文的说明。 加入妥音效资源和背景音乐资源之后,请于 Game1 类别加入以下的变量宣告,负责管理音效资源,播放音效的 SoundEffectInstance 类别的对象,以及管理背景音乐的 Song 类别的对象: SoundEffect HitWall; //管理球體碰撞到遊戲視窗邊界的音效的變數 SoundEffectInstance HitWallEffect; //控制音效播放的變數 Song BackgroundSong; //管理遊戲背景音效的變數 接下来请编辑 Game1 类别的 LoadContent 方法,加入以下的程序代码,执行加载音效和音乐资源的动作: HitWall = Content.Load<SoundEffect>("HitWall"); //載入名稱為 HitWall 的音效資源 HitWallEffect = HitWall.CreateInstance(); //建立播放音效資源的 SoundEffectInstance 物件 HitWallEffect.Apply3D(new AudioListener(), new AudioEmitter()); //呼叫 SoundEffectInstance 物件的 Apply3D 方法,播放 3D 音效 BackgroundSong = Content.Load<Song>("SuperMario");//載入名稱為 SuperMario 的背景音樂資源 MediaPlayer.IsRepeating = true; //將MediaPlayer類別的IsRepeating屬性設定成true,表示要不中斷地重覆播放背景音樂 MediaPlayer.Play( BackgroundSong); //呼叫 MediaPlayer 類別的 Play 方法播放名稱為 //BackgroundSong的物件管理的背景音樂 最后请编辑 Game1 类别的 Update 方法,加入以下的程序代码,以便在使用者利用触碰触控屏幕移动游戏程序显示的图形时,判断所移动的图形是否碰撞到游戏窗口的边界,以决定是否要播放碰撞的音效: if (BallPosition.X == 0 || BallPosition.X > Window.ClientBounds.Width - Ball.Width || BallPosition.Y==0 || BallPosition.Y > Window.ClientBounds.Height-Ball.Height) //判斷圖形是否碰撞到遊戲視窗的四個邊界 { HitWallEffect.Play(); //播放碰撞的音效 } [说明] 控制播放音效与背景音乐的动作属于控制游戏程序状态的范畴,和显示游戏程序内容无关,所以不需要修改 Game1 类别的 Draw 方法。 做好之后请按下 CTRL+F5 组合键执行项目,待游戏程序启动之后,读者就可以听到不断重复播放的背景音乐。当您触碰游戏程序的窗口以移动戏程显示的图形时,只要图形碰触到游戏窗口的边界,就会听到游戏程序发出的碰撞音效。 范例下载:TouchMoveBall.zip [结语] 这一回我们为大家介绍了更多的 XNA Framework 支持游戏开发的类别,包括支持输入控制,音效播放,以及背景音乐播放控制的类别等等,让读者能够为所制作的游戏程序加入更丰富的游戏效果。下一回我们将要为大家介绍进阶的输入控制,例如手势触控输入,进阶音效控制技巧,碰撞侦测,以及辅助 XNA 游戏设计的好用类别。

XNA Framework 常用的类别

clock 七月 31, 2011 23:08 by author alex
XNA Framework 常用的类别 XNA Framework 常用的类别 摘要 上一回我们为大家介绍了如何使用 Visual Studio 2010 或 Visual Studio 2010 Express for Windows Phone 设计以 XNA 为基础的 Windows Phone 7 游戏程序,并了解以 XNA 为基础的游戏程序的基础架构与核心功能。这一回我们将要为大家介绍 XNA Framework 支持游戏程序制作的重要类别,做为开发游戏程序的基础知识。除此之外,我们也将利用本文所介绍的类别实作一个简单的游戏程序,并令其具有能够不断卷动的背景图案。 GraphicsDeviceManager 类别 第一个要介绍给大家认识的是负责管理绘图装置的 GraphicsDeviceManager 类别。以 XNA 为基础的游戏程序必须在初始化的时候建立妥 GraphicsDeviceManager 类别的对象,并设定包括游戏程序窗口高度与宽度在内的必要属性,做为显示游戏内容的基础。有关于游戏程序初始化阶段建立 GraphicsDeviceManager 类别的对象的详细做法可以参考 [设计以 XNA 为基础的 Windows Phone 7 游戏]一文的说明。表1 所示即为 GraphicsDeviceManager 类别常用的属性: 表1:GraphicsDeviceManager 类别常用的属性 属性名称 说明 IsFullScreen 控制游戏程序的窗口是否要以全屏幕的方式显示 PreferredBackBufferFormat 屏幕缓冲区的格式 PreferredBackBufferHeight 屏幕缓冲区的高度 PreferredBackBufferWidth 屏幕缓冲区的宽度 GraphicsDeviceManager 类别常用的方法请参考表2 的说明: 表2:GraphicsDeviceManager 类别常用的方法 方法名称 说明 ToggleFullScreen 在窗口模式和全屏幕模式中切换 游戏核心类别 以 XNA 为基础的游戏程序的主体是 Game 类别,也是做为游戏程序主体的 Game1 类别的基础类别。游戏程序可以利用 Game1 类别的 Update 方法更新游戏的状态,利用 Draw 方法显示游戏的内容。表3 所示即为 Game 类别的常用属性: 表3:Game 类别常用的属性 属性名称 说明 Components 管理所有 GameComponent 的集合 Content 取得 ContentManager 对象的属性 GraphicsDevice 取得图型装置对象的属性 IsActive 判断游戏程序的窗口目前是否在作用中 IsFixedTimeStep 控制游戏程序要使用固定更新模式或是可变更新模式 TargetElapsedTime 当 IsFixedTimeStep 属性的内容值为 true 时,控制 Update 方法被呼叫的频率的属性 Game 类别常用的方法可以参考表4 的说明: 表4:Game 类别常用的方法 方法名称 说明 Exit 结束游戏程序的执行 BeginDraw 宣告绘制图形的动作开始 EndDraw 宣告绘制图形的动作结束 Draw 执行绘制游戏内容的动作 LoadContent 执行加载游戏资源的方法 UnloadContent 执行释放游戏资源的方法 Update 负责更新游戏状态的方法 如果程序设计师要以模块化的方式设计游戏程序,可以将游戏的人物制作成 GameComponent 类别或是 DrawableGameComponent 类别,再将 GameComponent 类别的对象或是 DrawableGameComponent 类别的对象加入到 Game 类别的对象的 Conponents 集合中,由 Game 类别的对象统一管理,把复杂的游戏人物的初始化、状态更新、以及更新显示等动作封装在 GameComponent 类别或是 DrawableGameComponent 类别中,可以有效简化 Game 类别的控制逻辑。当游戏人物众多,角色复杂时,适当地利用 GameComponent 类别或是 DrawableGameComponent 类别可以提升游戏的可维护性,利于发展功能进阶,效果复杂的游戏。 XNA Framework 提供的 GameComponent 类别和 DrawableGameComponent 类别的差别在于 DrawableGameComponent 类别管理的是有用户接口的游戏组件,而 GameComponent 类别所管理的则是没有用户接口的游戏组件。换句话说,DrawableGameComponent 类别提供了 GameComponent 类别未支持的 LoadContent 方法和 Draw 方法,以便执行加载所管理的游戏资源和显示游戏的内容的工作。 当建立好 GameComponent 类别的对象或 DrawableGameComponent 类别的对象之后,只要将 GameComponent 类别的对象或 DrawableGameComponent 类别的对象加入到 Game 类别的对象的 Components 集合中,GameComponent 类别的对象或 DrawableGameComponent 类别的对象的 Update 方法就会被持续地呼叫,而 DrawableGameComponent 类别的对象的 Draw 方法也会在适当的时机被呼叫,并将游戏的内容显示到窗口中供用户操作。 欲为游戏程序项目加入 GameComponent 类别或 DrawableGameComponent 类别,可以使用鼠标的右键点中 [Solution Explorer] 窗口中的项目名称,从出现的菜单选择 [Add | New Item] 功能,屏幕上就会出现要求选择欲加入到项目的项目的窗口,如图1 所示: 图1:要求选择欲加入到项目的项目的窗口 请于中间的窗口选择 [Game Component] 项目,于 [Name] 字段输入文件名,然后按下 [Add] 键,就可以为项目加入一个继承自 GameComponent 类别的衍生类别。 [注意] 继承自 GameComponent 类别的衍生类别无法覆写基础类别的 LoadContent 方法和 Draw 方法,适合用来制作没有用户接口的游戏组件。如果需要制作提供用户接口的游戏组件,可以在为项目加入继承自 GameComponent 类别的衍生类别之后,自行将所继承的 GameComponent 基础类别修改成 DrawableGameComponent 类别,就可以覆写基础类别的 LoadContent 方法和 Draw 方法,分别负责执行加载所管理的游戏资源,和绘制游戏内容的工作。 以下就是在继承自 DrawableGameComponent 类别的衍生类别覆写基础类别的 LoadContent 方法和 Draw 方法的范例: public class GameComponent1 : Microsoft.Xna.Framework.DrawableGameComponent { … protected override void LoadContent() //覆寫 LoadContent 方法 { base.LoadContent(); } public override void Draw(GameTime gameTime) //覆寫 Draw 方法 { base.Draw(gameTime); } } GameComponent 类别常用的属性可以参考表5 的说明: 表5:GameComponent 类别常用的属性 属性名称 说明 Enabled 控制 GameComponent 类别的对象是否启用的属性。当对象启用时,类别的 Update 方法就会被定时地呼叫 UpdateOrder 控制 Update 方法被呼叫的顺序。内容值小的对象的 Update 方法会优先被呼叫 GameComponent 类别常用的方法详见表6 的说明: 表6:GameComponent 类别常用的方法 方法名称 说明 Initialize 负责执行 GameComponent 类别的对象初始化的方法 Update 负责更新 GameComponent 类别的对象的状态的方法 DrawableGameComponent 类别的功能和 GameComponent 类别的功能类似,差别在于 DrawableGameComponent 类别可以用来管理具用户接口的游戏组件。DrawableGameComponent 类别常用的属性请参考表7 的说明: 表7:DrawableGameComponent 类别常用的属性 属性名称 说明 Enabled 控制 DrawableGameComponent 类别的对象是否启用的属性。当对象启用时,类别的 Update 方法就会被定时地呼叫 Visible 控制 DrawableGameComponent 类别的对象是否隐藏的属性,对象隐藏时,类别的 Draw 方法将不会被呼叫 UpdateOrder 控制 Update 方法被呼叫的顺序。内容值小的对象的 Update 方法会优先被呼叫 DrawOrder 控制 Draw 方法被呼叫的顺序。内容值小的对象的 Draw 方法会优先被呼叫 Game 取得管理 DrawableGameComponent 类别的对象的Game类别对象的属性 GraphicsDevice 取得游戏程序使用的绘图装置的属性 DrawableGameComponent 类别常用的方法请参考表8 的说明: 表8:DrawableGameComponent 类别常用的方法 方法名称 说明 Initialize 负责执行 GameComponent 类别的对象初始化的方法 LoadContent 负责加载欲使用的游戏资源 UnloadContent 负责释放欲使用的游戏资源 Update 负责更新游戏的状态 Draw 负责显示游戏的内容 制作好 GameComponent 类别或是 DrawableGameComponent 类别之后,我们就可以在 Game1 类别的建构函式中建立 GameComponent 类别或是 DrawableGameComponent 类别的对象,再将建立好的对象加入到 Game1 类别的对象的 Components 集合中,游戏程序便会定时地呼叫 GameComponent 类别或是 DrawableGameComponent 类别的Update方法,以及 DrawableGameComponent 类别的 Draw 方法,达到更新游戏状态和显示游戏内容的效果。假设游戏项目中已经制作好继承自 GameComponent 类别的衍生类别,名称为 GameComponent1,以及继承自 DrawableGameComponent 类别的衍生类别,名称为 DrawableGameComponent1,则 Game1 类别的建构函式就可以写成以下的样子: public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferHeight = 480; graphics.PreferredBackBufferWidth = 800; Components.Add(new GameComponent1(this)); Components.Add(new DrawableGameComponent1 (this)); // Frame rate is 30 fps by default for Windows Phone. TargetElapsedTime = TimeSpan.FromTicks(333333); } [提示] XNA Framework 支持的 DrawableGameComponent 类别虽然可以用来管理游戏程序的人物,但是却没有记录游戏人物坐标位置的属性,程序设计师可以自行于继承自 DrawableGameComponent 类别的衍生类别加入负责记载游戏人物坐标的属性,以形成一个完整的游戏组件,利于游戏程序进行定位与移动。 显示 2D 与 3D 图形 以 XNA 为基础的游戏程序可以利用 Texture2D 类别管理 2D 图形资源,利用 Model 类别管理 3D 模型。用来管理 2D 图形资源的 Texture2D 类别常用的属性可以参考表9 的说明,用来管理 3D 模型的 Model 类别的常用属性可以参考表10 的说明: 表9:Texture2D 类别常用的属性 属性名称 说明 Bounds 代表图形资源的大小 Format 代表图形资源的格式 GraphicsDevice 取得游戏程序使用的绘图装置的属性 Height 图形资源的高度(单位:pixel) Width 图形资源的宽度(单位:pixel) 表10:Model 类别的常用属性 属性名称 说明 Bones 3D 模型中的骨骼数据的集合 Meshes 3D 模型中的网格数据的集合 Root 取得最根源的骨骼数据的属性 坐标 当以 XNA 为基础的游戏程序欲将所加载的 2D 或 3D 图形资源显示到游戏的画面时,我们就会需要用到和坐标有关的型态。XNA 支持的常用坐标类别包括定义 2 维空间坐标点的 Vector2 结构,定义 3 维空间坐标点的 Vector3 结构,以及定义齐次坐标系统 (Homogenous Coordinate System) 坐标点的 Vector4 结构。 Vector2 架构的是二维空间的坐标,坐标原点默认在窗口的左上角,如图2 所示: 图2:二维空间的坐标系统 Vector2 结构的数据成员请参考表11 的说明: 表11:Vector2 结构的数据成员 数据成员名称 说明 X 代表坐标点的X轴的位置 Y 代表坐标点的Y轴的位置 Vector2 结构常用的方法请参考表12 的说明: 表12:Vector2 结构常用的方法 方法名称 说明 Add 对坐标点执行加法运算 Clamp 限制坐标内容值必须落在指定的范围之间 Distance 计算两个坐标点之间的距离 DistanceSquared 计算两个坐标点之间的距离的平方 Divide 对坐标点执行除法运算 Equals 判断坐标点是否等于指定的坐标点 Lerp 计算两个坐标点之间的线性内插 Max 计算坐标点的最大值 Min 计算坐标点的最小值 Multiply 对坐标点执行乘法运算 Negate 对坐标点执行反运算 Subtract 对坐标点执行减法运算 Transform 对坐标点执行转置 (Transform) 运算 Vector3 架构的是三维空间的坐标,因为 XNA Framework 支持右手坐标系统 (Right-Handed System),其 Z 轴往使用者的方向递增,如图3 所示: 图3:XNA 支持的右手坐标系统 Vector3 结构的数据成员可以参考表13 的说明: 表13:Vector3 结构的数据成员 数据成员名称 说明 X 代表坐标点的X轴的位置 Y 代表坐标点的Y轴的位置 Z 代表坐标点的Z轴的位置 Vector3 结构常用的方法和 Vector2 结构常用的方法类似,但是有提供能够计算外积 (Cross Product) 的 Cross 方法。 Vector4 架构的是齐次坐标系统 (Homogeneous Coordinate System) 的坐标点,第四个坐标并非用来代表几何空间的位置,而是用来表示坐标轴的远近参数。 Vector4 结构的数据成员可以参考表14 的说明: 表14:Vector4 结构的数据成员 数据成员名称 说明 X 代表坐标点的 X 轴的位置 Y 代表坐标点的 Y 轴的位置 Z 代表坐标点的 Z 轴的位置 W W 元素。W 元素并不是用来代表几何空间的位置,而是为了定义转置矩阵的设计,利于执行平行、放大、与旋转的处理 Vector4 结构常用的方法和 Vector2 结构常用的方法类似,可以直接参考表12 的说明。 卷动游戏程序背景的做法 了解了 XNA Framework 支持游戏设计的常用类别之后,接下来我们就要使用这些类别为游戏程序制作一个可以自动卷动的背景,就像超级玛璃的游戏一样,在游戏进行时不断地卷动。 要制作游戏的卷动背景,可以准备一张以上,组合起来会变成完整的背景图的背景拼图,例如以下就是由三张连续的背景拼图组成的背景图。 图4:由三张连续的图片拼凑而成背景图 [说明] 游戏的背景通常是由多张图片拼凑而成,一张接着一张不断地轮播,周而复始。请注意除非背景图的内容永远不会重复,否则不要使用单一张完整的背景图片当做游戏的背景,就算要制作背景图内容永远不重复的游戏,也会因为单一张完整的图片体积过积而不切实际。 准备好当做游戏背景的背景拼图之后,请启动 Visual Studio 2010 Express for Windows Phone,建立一个 [Windows Phone Game(4.0)] 型态的项目,并把准备好的背景拼图加入到 [Solution Explorer] 窗口中的 Content Pipeline 项目中,以便被游戏程序加载使用。 建立好项目之后请开启 Game1.cs 原始程序档案进行编辑,于 Game1 类别中加入以下的变量宣告,分别用来管理当做游戏背景的拼图,以及记载背景拼图位置的变量: Texture2D Background1; //管理第一張背景圖片的變數 Texture2D Background2; //管理第二張背景圖片的變數 Texture2D Background3; //管理第三張背景圖片的變數 Vector2 Position1; //第一張背景圖片的顯示座標 Vector2 Position2; //第二張背景圖片的顯示座標 Vector2 Position3; //第三張背景圖片的顯示座標 请注意上述的变量宣告可以处理由三张图片拼凑而成的游戏背景,如果游戏背景是由更多图片组成,可以宣告适量的变量以管理组成背景的图片。 宣告好必要的变量之后,请修改 Game1 类别的建构函式,加入设定游戏窗口大小的程序代码,修改好的建构函式如下: public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferWidth = 480; //設定遊戲視窗的寬度為 480 graphics.PreferredBackBufferHeight = 800; //設定遊戲視窗的寬度為 400 // Frame rate is 30 fps by default for Windows Phone. TargetElapsedTime = TimeSpan.FromTicks(333333); } 设定好游戏窗口的大小之后,请编辑 Game1 类别的 LoadContent 方法,负责加载当做背景图片的图形,并设定妥背景图片的显示位置,编辑好的 LoadContent 方法如下: protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here Background1 = Content.Load<Texture2D>("Background01"); //載入第一張背景圖片 Position1 = new Vector2(0, 0); //設定第一張背景圖片的顯示位置 Background2 = Content.Load<Texture2D>("Background02"); //載入第二張背景圖片 Position2 = new Vector2(Position1.X + Background1.Width, 0); //設定第二張背景圖片要顯示在第一張背景圖片的右方 Background3 = Content.Load<Texture2D>("Background03"); //載入第三張背景圖片 Position3 = new Vector2(Position2.X + Background2.Width, 0); //設定第三張背景圖片要顯示在第二張背景圖片的右方 } 如果读者准备的背景图片的文件名和上述的范例程序代码所指定的文件名不同,请依据所准备的图形文件名修改上述指定加载的文件名。 完成加载游戏程序的背景图案之后,接下来我们必须修改 Game1 类别的 Update 方法,更新背景图片显示的位置,创造出背景卷动的效果。 如果要将游戏的背景由右往左卷动,则每一次 Update 方法被呼叫的时候必须递减所显示的背景图片的左上角点的 X 坐标的内容值,令背景图片产生往左移动的效果。反之,如果要将游戏的背景由左往右卷动,则每一次 Update 方法被呼叫的时候必须递增所显示的背景图片的左上角点的X坐标的内容值,就能够令背景图片产生往右移动的效果。请注意如果背景图是由右往左卷动,当最左边背景图卷动到游戏的窗口之外时,必须自动补到最右边的图的右边,背景图才能够周而复始地不断地卷动,反之亦然。图5 所示即为最左边的背景图卷动到游戏窗口的范围之外时,自动接续到最右方的背景图的右边继续显示的示意图: 图5:卷动背景图的做法 了解卷动背景图的做法之后,请将 Game1 类别的 Update 方法修改成以下的样,以由右往左的方式卷动背景图: protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // TODO: Add your update logic here if (Position1.X < -Background1.Width) //如果第一張背景圖離開遊戲的視窗 { Position1.X = Position3.X + Background3.Width; //令其顯示在第三張背景圖的右方的位置 } if (Position2.X < -Background2.Width) //如果第二張背景圖離開遊戲的視窗 { Position2.X = Position1.X + Background1.Width; //令其顯示在第一張背景圖的右方的位置 } if (Position3.X < -Background3.Width) //如果第三張背景圖離開遊戲的視窗 { Position3.X = Position2.X + Background2.Width; //令其顯示在第二張背景圖的右方的位置 } int aSpeed = 2; //設定每次捲動的單位距離 Position1.X -= aSpeed; //令第一張背景圖往左移動指定的距離 Position2.X -= aSpeed; //令第二張背景圖往左移動指定的距離 Position3.X -= aSpeed; //令第三張背景圖往左移動指定的距離 base.Update(gameTime); } [提示] 读者只要稍加修改上述的 Update 方法,就可以令背景图由左往右移动。如果要升高或降低背景的卷动速度,可以修改 aSpeed 变量的内容值。 最后我们只要将移动过的背景图显示到游戏的窗口,就可以令游戏程序的背景产生卷动的效果。请修改 Game1 类别的 Draw 方法,将每一张背景图显示在指定的位置,修改妥的 Draw 方法如下: protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(); spriteBatch.Draw(Background1, Position1, Color.White); //在 Position1 的位置顯示第一張背景圖 spriteBatch.Draw(Background2, Position2, Color.White); //在 Position2 的位置顯示第二張背景圖 spriteBatch.Draw(Background3, Position3, Color.White); //在 Position3 的位置顯示第三張背景圖 spriteBatch.End(); base.Draw(gameTime); } 做好之后请按下 CTRL+F5 组合键执行项目,所制作好的游戏便会部署到 Windows Phone 7 的仿真器中执行,您将可以看到背景图由右往左卷动的情形,其执行的画面如图6 所示: 图6:背景图由右往左卷动的游戏程序执行的情形 范例下载:ScrollBackgound.zip [结语] 这一次的文章中我们为大家介绍了 XNA Framework 支持游戏程序制作的重要类别,做为开发游戏程序的基础知识。除此之外,我们也利用本文所介绍的类别实作一个简单的游戏程序,并令其具有能够不断卷动的背景图案。下一回我们将会介绍更多的类别,让读者可以为所制作的游戏程序加入更丰富的游戏效果。

设计以 XNA 为基础的 Windows Phone 7 游戏

clock 七月 31, 2011 23:06 by author alex
设计以 XNA 为基础的 Windows Phone 7 游戏 设计以 XNA 为基础的 Windows Phone 7 游戏 摘要 在上一篇文章中我们为大家介绍了 XNA Game Studio 4.0 的基本功能与支持游戏设计的特性和优势,做为使用 XNA Game Studio 4.0 版设计游戏程序的基础。这一回我们将要为大家介绍如何使用 Visual Studio 2010 或 Visual Studio 2010 Express for Windows Phone 设计以 XNA 为基础的 Windows Phone 7 游戏程序,并了解以 XNA 为基础的游戏程序的基础架构与核心功能。 XNA 游戏程序结构 使用 Microsoft Visual Studio 2010 Express for Windows Phone 建立的 Windows Phone Game (4.0) 型态的项目中,名称为 Game1.cs 的程序档案是整个游戏最主要的核心,负责定义名称为 Game1 的类别,打造以 XNA 为基础的游戏的基本结构。有关使用 Microsoft Visual Studio 2010 Express for Windows Phone 建立 XNA 游戏项目的说明可以参考:[Windows Phone 7 智能型手机应用程序开发总览]一文的说明。 Game1 类别继承自 XNA Framework 提供的 Microsoft.Xna.Framework.Game 类别,预设会覆写基础类别的 Initialize 方法、LoadContent 方法、UnloadContent 方法、Update 方法、Draw 方法,加上 Game1 类别自己的建构函式,形成游戏最基本的结构。例如以下就是一个 XNA 游戏类别的最基本的类别及类别的结构: public class Game1 : Microsoft.Xna.Framework.Game { public Game1() {} protected override void Initialize() {} protected override void LoadContent() {} protected override void UnloadContent() {} protected override void Update(GameTime gameTime) {} protected override void Draw(GameTime gameTime) {} } Game 类别主要方法的功能 在 Game1 类别覆写基础类别的方法中,名称为 Initialize 的方法与 Game1 类别的建构函式负责执行游戏初始化的工作,名称为 LoadContent 的方法负责自 Content Pipeline 项目加载透过 Content Pipeline 技术预先处理成二进制数据的游戏资源,名称为 UnloadContent 的方法负责执行释放资源的动作,请注意以 XNA 为基础的应用程序只需要释放非 ContentManager 管理的资源即可,意即未透过 ContentManager 类别的Add方法加入到 ContentManager 进行管理的资源才有需要在 UnloadContent 方法执行释放资源的动作。 Game1 类别中名称为 Update 的方法负责更新游戏的状态,例如要将游戏的主角从 X 坐标等于 100 的位置移动到 X 坐标为 500 的位置,改变主角坐标的动作就可以在 Update 方法中执行。请注意 Update 方法只负责更新游戏的状态,不负责更新游戏的显示画面,换句话说,Update 方法只负责更新主角的坐标位置,但是并不负责将主角显示在新的坐标位置,负责更新游戏显示内容的是名称为 Draw 的方法,所有显示游戏最新状态的工作,皆由 Draw 方法负责执行。 XNA 游戏程序的基本结构 了解 Game1 类别各个方法的基本功能之后,接下来我们就要来探讨 Game1 类别各个方法必须执行的工作,以成功地建立一个能够执行的 Windows Phone 7 游戏程序。 图1 所示即为一个 Windows Phone 7 游戏程序从启动到结束执行的运作过程,以及游戏程序执行过程中每个阶段必须执行的工作: 图1:Windows Phone 7 游戏程序从启动到结束执行的各个阶段必须执行的工作 [注意] 以 XNA 为基础的 Windows Phone 7 游戏程序在游戏一开始会自动建立游戏核心类别 (即 Game1 类别) 的对象,程序设计师不需要制作建立 Game1 类别的对象的程序代码。 游戏程序开始执行并完成建立 Game1 类别的对象之后,第二个阶段就是执行游戏初始化。游戏初始化阶段主要的工作在建立 GraphicsDeviceManager 类别的对象。假设当做游戏核心的类别的名称为 Game1,则我们可以在 Game1 类别的建构函式中执行建立 GraphicsDeviceManager 类别的对象的动作,并设定 GraphicsDeviceManager 类别的对象的 PreferredBackBufferHeight 属性和 PreferredBackBufferWidth 属性,指定游戏窗口的高度和宽度。除了设定 GraphicsDeviceManager 类别的对象的属性以外,我们还可以设定 Game1 类别的对象的 TargetElapsedTime 属性,控制游戏程序更新状态及显示内容的频率,做法如下: public Game1() { graphics = new GraphicsDeviceManager(this);//建立 GraphicsDeviceManager 類別的物件 Content.RootDirectory = "Content"; //設定遊戲資源的根目錄 graphics.PreferredBackBufferHeight = 480; //設定遊戲視窗的高度 graphics.PreferredBackBufferWidth = 800; //設定遊戲視窗的寬度 // Frame rate is 30 fps by default for Windows Phone. TargetElapsedTime = TimeSpan.FromTicks(333333);//設定遊戲程式更新狀態及顯示內容的頻率為每秒 30 次 } [说明] Windows Phone 7 智能型手机目前只有屏幕高度为 800,宽度为 480 的版本,因此将游戏窗口设定成高度 480,宽度 800 的大小,表示游戏要以横式屏幕的方式执行,另外 Game1 类别的对象的 TargetElapsedTime 属性负责控制游戏程序更新状态及显示内容的频率,将 TargetElapsedTime 属性设定成 333333,表示更新频率为 0.0333333,也就是每秒要更新 30 次。请注意每秒更新 30 次适合在 Windows Phone 7 智能型手机平台执行的游戏,但是不一定适合在 PC 个人计算机平台或 Xbox 360 游机 游戏程序完成初始化之后,第三个阶段负责执行加载游戏资源的动作,也就是要加载游戏程序执行的时候需要用到所有资源,包括音效、音乐、图形、和字型等等。负责加载游戏程序需要使用的所有资源的动作可以交给 Game1 类别的 LoadContent 方法负责完成,例如以下就是负责加载游戏程序需要使用的图形资源的程序代码: Texture2D genie = null; //宣告Texture2D類別的物件 protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); //建立 SpriteBatch 類別的物件 // TODO: use this.Content to load your game content here genie = Content.Load<Texture2D>("genie"); } 请注意上述的程序代码会在游戏程序初始化的阶段建立 SpriteBatch 类别的对象,而建立 SpriteBatch 类别的对象的时候需要传入 GraphicsDeviceManager 类别的对象当做参数。SpriteBatch 类别的对象主要的用途在显示游戏程序欲显示的 2D 图形,包括游戏背景、游戏人物、游戏的状态、甚至是游戏提示供用户选取的菜单。 除了建立 SpriteBatch 类别的对象以外,游戏程序必须在初始化的阶段负责加载游戏程序执行时需要用到的所有资源,例如上述的程序代码中这一行程序代码: genie = Content.Load<Texture2D>("genie"); 负责从 Content Pipeline 项目的根目录加载名称为 genie 的资源,因为程序代码指定欲加载的是 Texture2D 格式的资源,也就是图形资源,所以在 Content Pipeline 项目中必须备妥名称为 genie 的图形档案,其扩展名可以是 .bmp、.dds、.dib、.hdr、.jpg、.pfm、.png、.ppm、和 .tga 等常见的图形格式档案。有关 XNA 游戏程序支持的图形文件格式可以参考 认识 XNA Game Studio 4.0 一文的说明。 [提示] 将上述程序代码中的 Texture2D 型态更换成 SoundEffect 型态,可以自 Content Pipeline 项目加载声音文件 (扩展名为 .wma 的档案),将上述程序代码中的 Texture2D 型态更换成 Song 型态,可以自 Content Pipeline 项目加载音乐档案 (扩展名为 .mp3 的档案),而将上述程序代码中的 Texture2D 型态更换成 SpriteFont 型态,可以自 Content Pipeline 项目加载字型定义档案 (扩展名为 .SpriteFont 的档案)。 [说明] 请注意上述的程序先在类别中宣告了一个型态为 Texture2D 类别,名称为 genie 的变量,负责管理游戏程序需要使用的图形。 加载妥游戏执行时需要用到的所有资源之后,游戏程序就可以进入运行时间,处理使用者的输入,更新游戏的状态,并显示游戏最新的内容。XNA 游戏程序一进入运行时间便会不断地呼叫 Update 方法和 Draw 方法,其详细的行为与呼叫的频率稍后会有详细的讨论。 假设游戏程序利用 Game1 类别定义的 pos 变量控制游戏人物显示的位置,则程序设计师可以于 Game1 类别的 Update 方法中更新 pos 变量的内容值,以便改变游戏人物显示的位置,完成更新游戏状态的工作,如下: int pos = 0; //存放遊戲人物位置的變數 protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) //如果使用者按下智慧型手機的 [Back] 鍵,回到上一個畫面 this.Exit(); //結束遊戲程式 if (pos <= 500) //如果 pos 變數的內容值不大於 500 { pos++; //遞增 pos 變數的內容值 } base.Update(gameTime); //呼叫基礎類別的 Update 方法 } 请注意 Game1 类别的 Update 方法只负责更新游戏的状态,不负责更新游戏显示的内容,更新游戏显示内容的工作是由 Game1 类别的 Draw 方法负责的。例如以下的 Draw 方法便会将游戏的人物移至 X 坐标等于 pos 变量的内容值的位置: protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); //清除遊戲程式目前顯示的內容 // TODO: Add your drawing code here spriteBatch.Begin(); //宣告顯示遊戲內容的動作開始 spriteBatch.Draw(genie, new Vector2(pos,100), Color.White);//將 genie 變數所管理的圖形的 x 座標顯示在 pos 變數的內容值指定的位置 spriteBatch.End(); //宣告顯示遊戲內容的動作結束 base.Draw(gameTime); //呼叫基礎類別的 Draw 方法 } [提示] 以 XNA 为基础的游戏程序可以在呼叫 spriteBatch 对象的 Begin 方法和呼叫 spriteBatch 对象的 End 方法之间执行多个绘制图形和文字的动作,由 XNA Framework 以批次的方式一次显示多个图形内容。另外还有一点需要提醒大家的是 Game1 类别的 Update 方法和 Draw 方法都会接一个型态为 GameTime 的参数,程序设计师可以从这个参数得知游戏程序开始迄今的时间,以及自上一次 Update 方法被呼到迄今的时间,做为控制更新游戏状态与更新游戏内容的参考。 当游戏程序结束执行的时候,游戏程序可以利用 Game1 类别的 UnloadContent 方法释放所有未透过呼叫 Content 对象的 Load 方法加载的资源。换句话说,所有透过呼叫 Content 对象的 Load 方法加载的资源可以不需要执行释放的动作,呼叫 Content 对象的 Load 方法加载的资源会由 ContentManager 负责执行释放的动作。 游戏程序执行的最后一个阶段是结束执行,结束游戏程序执行的动作很简单,游戏程序只要呼叫 Game1 类别的 Exit 方法就可以结束游戏程序的执行。 Update 方法与 Draw 方法的呼叫时机 前面有提到,XNA 游戏程序一进入运行时间便会不断地呼叫 Update 方法和 Draw 方法,其中的 Update 方法负责更新游戏程序的状态,而 Draw 方法则负责显示游戏的内容。请注意并不是每一次 Update 方法被呼叫之后,Draw 方法就会跟着被呼叫,Update 方法与 Draw 方法被呼叫的频率要视 Game1 类别的 IsFixedTimeStep 属性的内容值而定。 如果 Game1 类别的 IsFixedTimeStep 属性的内容值为 true (默认值),则游戏程序处于固定更新 (fixed-step) 模式。使用固定更新模式的游戏程序,其 Update 方法被呼叫的频率是由 Game1 类别的 TargetElapsedTime 属性的内容值定义的,其默认值视执行游戏程序的平台的不同而有不同。如果游戏程序在 PC 个人计算机或 Xbox 360 游戏机中执行,Update 方法呼叫的频率为每秒 60 次,如果游戏程序在 Windows Phone 7 智能型手机中执行,因为其处理器的速度较慢,Update 方法呼叫的频率为每秒 30 次。 请注意 Update 方法被呼叫的频率是一个游戏程序努力要达到的目标值,而不是一定可以达到的数据。如果游戏程序在 PC 个人计算机或 Xbox 360 游戏机上执行,但是却无法在 1/60 秒内完成 Update 方法和 Draw 方法执行的工作,则 Update 方法的 gameTime 参数 (型态为 GameTime 类别) 的 IsRunningSlowly 属性将会被设定为 true,表示游戏程序无法以正常的速度执行,此时游戏程序会在呼叫 Update 方法之后,将不会伴随着呼叫负责显示游戏内容的 Draw 方法,而是继续呼叫 Update 方法,以跟上 TargetElapsedTime 属性的内容值定义的更新频率,如果此举还是无法令游戏程序跟上欲达到的更新频率,则放弃。其结果将会造成游戏程序在呼叫负责更新游戏状态的 Update 方法几次之后才呼叫一次负责显示内容的 Draw 方法,因此游戏显示的人物的动作将会呈现动作不顺畅的现象。 当游戏程序出现动作更新显示不顺畅的现象时,我们可以降低 TargetElapsedTime 属性内容值,降低应用程序的更新频率,或是将 Game1 类别的 IsFixedTimeStep 属性的内容值为 false,令游戏程序以可变更新 (variable-step) 模式来执行。请注意以可变更新模式执行的游戏程序会不断地呼叫 Update 方法和 Draw 方法,周而复始而且不间断,不管 Game1 类别的 TargetElapsedTime 属性的设定值为何。 [特别注意] 不管以 XNA 为基础的游戏程序采用固定更新 (fixed-step) 模式执行,或是采用可变更新 (variable-step) 模式执行,Game1 类别的 Update 方法和 Draw 方法都会持续地被呼叫,直到游戏结束为止,也就是说游戏程序本身都已经身处循环之中,除非有特殊的需求,否则在 Update 方法和 Draw 方法中不需要再使用循环的语法进行控制。例如我们要将游戏中的动画人物从X坐标为 0 的位置移动到 X 坐标为 500 的位置,不可以在 Game1 类别的 Update 方法加入以下的程序代码,以更新动画人物的显示位置 (假设 pos 是负责控制动画人物 X 坐标位置的变量): for (int i = 0; i <= 500; i++) { pos = i; } 因为上述的循环在执行的时候会造成 Update 方法执行较久,等 Update 方法执行完毕才会呼叫 Draw 方法,所以游戏程序执行的时候我们将会看到动画人物原本在X坐标为 0 的位置,等循环跑完,动画人物会直接在X坐标为 500 的位置出现,用户将不会看到动画人物从X坐标为 0 的位置移至 X 坐标为 500 的位置的移动过程,造成动画人物移动的过程很突兀,没有渐进的效果。 要令动画人物移动位置的时候有渐进的效果,我们可以在 Update 方法中利用以下的程序代码改变负责控制动画人物 X 坐标位置的 pos 变量的内容值: if (pos <= 500) { pos++; } 因为 Update 方法会被持续不断地呼叫,每被呼叫一次,动画人物的 X 坐标的内容值就会改变,当 Draw 方法被呼叫的时候,使用者就可以看到动画人物渐进移动至目标位置的效果。 [结语] 这一回我们为大家介绍了如何使用 Visual Studio 2010 或 Visual Studio 2010 Express for Windows Phone 设计以 XNA 为基础的 Windows Phone 7 游戏程序,并了解以 XNA 为基础的游戏程序的基础架构与核心功能。下一回我们将要为大家介绍 XNA Framework 支持游戏程序制作的重要类别,做为开发游戏程序的基础知识。

认识 XNA Game Studio 4.0

clock 七月 31, 2011 23:04 by author alex
认识 XNA Game Studio 4.0 认识 XNA Game Studio 4.0 摘要 早在 Microsoft Pocket PC 和 Microsoft Smartphone 的年代,Microsoft 就已经支持程序设计师在智能型装置上设计游戏程序,只不过那个年代设计游戏程序依靠的是功能很基本的 GAPI (Game API)。到了 Windows Mobile 5.0 的时代,程序设计师已经可以利用 Microsoft 提供的 DirectX 技术设计高效能的 2D 和 3D 的游戏,不过对使用 C# 程序语言开发应用程序的程序设计师,利用 DirectX 技术开发游戏还是有一点隔阂。Microsoft 后来虽然推出了能够支持 C# 程序设计师设计游戏的 XNA,协助程序设计师开发 Windows 平台与 Xbox 游戏机上的游戏,但是尚不能协助程序设计师设计能够在智能型手机上执行的游戏,直到 XNA Game Studio 4.0 版问世,支持智能型手机游戏程序设计方面才又往前迈进一大步。在这一篇文章中我们将要为大家介绍 XNA Game Studio 4.0 的基本功能与支持游戏设计的特性和优势,做为使用 XNA Game Studio 4.0 版设计游戏程序的基础。 认识 XNA XNA Game Studio 是一个整合式的游戏开发环境,能够支持程序设计师使用 C# 程序语言,搭配其提供的工具和类别库设计游戏程序,其第一版发表于 2006 年 12 月,这个版本只能支持程序设计师开发能够在 Windows 平台和 Xbox 游戏机上执行的游戏程序。和第一版隔了大约只有一年,Microsoft 就于 2007 年 12 月推出了 XNA Game Studio 2.0 版,这个版本能够与包括 Visual Studio 2005 Express 版在内的任何 Visual Studio 2005 版本整合,让程序设计师可以使用熟悉的 Visual Studio 2005 进行设计游戏程序的工作,而且可以允许在 Windows 平台或 Xbox 360 游戏机上执行的游戏透过网络联机功能使用 Xbox Live 服务。Microsoft 于 2008 年 10 月推出的 XNA Game Studio 3.0 版不但能够与包括 Visual Studio 2008 Express 版在内的任何 Visual Studio 2008 版本整合,以进行设计游戏的工作,同时也加入了对 Microsoft Zune 数字播放器的支持,让程序设计师设计好的游戏程序可以部署到 Zune 装置上执行。除此之外,XNA Game Studio 3.0 版是第一个提供 Xbox Live 社群支持的版本,也是第一个支持 C# 3.0 和 LINQ 语法的版本,同时也是第一个提供试用模式的版本,允许程序设计师利用 XNA Game Studio 3.0 为所开发的游戏程序加入试用版的功能,以及利用 Xbox LIVE 服务开发多人在线游戏。Microsoft 于 2009 年 11 月推出的 XNA Game Studio 3.1,首开先河的推出影片播放功能,也改进了音效与音乐播放功能,其支持 Xbox LIVE 服务提供的 Party 功能允许最多 8 个人进行聊天并分享图片。除此之外,XNA Game Studio 3.1 也支持 Xbox 360 游戏的使用者使用其支持的化身 (Avatar) 功能。 最新版的 XNA Game Studio 4.0 版发表于 2010 年 9 月。这个版本支持除了可以支持程序设计师开发能够在 Windows 平台和 Xbox 360 游戏机上执行的游戏程序以外、也能够协助程序设计师开发最新的 Windows Phone 7 智能型手机平台的游戏,同时也支持 3D 硬件加速,于游戏开发阶段设定欲套用的 Effect,易于回复至原始状态的内建状态对象,图形放大、缩小、与旋转支持,多点触控输入,麦克风输入,具缓冲功能的音效播放功能,并且允许程序设计师利用 Microsoft Visual Studio 2010 程序开发工具开发 Windows Phone 7 智能型手机的游戏程序。 XNA Game Studio 游戏开发支持 了解了 XNA Game Studio 各个版本的功能和特性之后,接下来我们要为大家介绍 XNA Game Studio 支持游戏开发的工具、Framework、以及游戏支持处理功能。 从前述的介绍,我们已经了解 XNA Game Studio 是一个整合游戏开发平台、开发工具、与类别库的游戏开发工具包,其中除了内含可以用来设计游戏程序的 Visual Studio 2010 程序开发工具以外,另外还包含了以下的工具: Microsoft Cross-Platform Audio Creation Tool 3 (XACT3):XACT 是一个由 Microsoft 开发的跨平台的音效建立工具,可以协助您建立游戏程序需要使用的音效资源。 XNA Framework Remote Performance Monitor:XNA Framework Remote Performance Monitor 是一个可以在游戏运行时间分析游戏效能的工具,协助程序设计师分析游戏程序的执行效能。 [说明] 如果开发者的计算机未安装 Microsoft Visual Studio 2010,则开发者的计算机在安装 Windows Phone Developer Tools 套件的时候,Windows Phone Developer Tools 套件会自动在开发者的计算机中安装 Visual Studio 2010 Express for Windows Phone 程序开发工具,以便让开发者进行设计游戏程序的动作,如果开发者的计算机已经安装有 Microsoft Visual Studio 2010,则开发者的计算机在安装 Windows Phone Developer Tools 套件的时候会为 Visual Studio 2010 安装新的项目模板,协助程序开发者使用已安装的 Visual Studio 2010,搭配新的项目模板开发可以在 Windows 平台、XBOX 360 游戏机、以及 Windows Phone 7 智能型手机执行的游戏程序。 图1 所示即为已安装 Visual Studio 2010 的计算机在加装 Windows Phone Developer Tools 套件之后,在 [Visual C#] 项目底下多出 [XNA Game Studio 4.0] 项目型态选项的画面: 图1:选择 Windows Phone Developer Tools 套件提供的 [XNA Game Studio 4.0] 项目型态选项的画面 除了 Microsoft Cross-Platform Audio Creation Tool 和 XNA Framework Remote Performance Monitor 工具以外,XNA Game Studio 4.0 提供的 XNA Framework 是游戏程序执行时需要用到的主要功能。除此之外,XNA Game Studio 4.0 提供的 Content Pipeline 功能负责处理游戏程序执行时期需要使用的资源,包括 2D 图形、3D 模型、音效/音乐、以及字型等等。 Content Pipeline 功能能够利用内建的 Importer 汇入常见的游戏资源,而且也允许程序设计师制作自定义的 Importer,以处理游戏程序执行时需要用到的特定格式资源。有关 Content Pipeline 处理游戏资源的详细流程可以参考 [Windows Phone 7 智能型手机应用程序开发总览] 一文的说明。 [说明] Content Pipeline 能够在应用程序建置阶段将游戏程序执行时需要使用的资源处理成二进制格式的资源,以便让游戏程序执行的时候直接加载并使用,有效提升游戏程序加载资源的效率,连带地提升 XNA Game Studio 4.0 制作的游戏程序的执行效率。 Content Pipeline 默认能够处理的资源型态包括以下所列的常见资源: Autodesk FBX 格式的资源 (扩展名为 .fbx 的档案) DirectX Effect 格式的资源 (扩展名为 .fx 的档案) DirectX X 格式的资源 (扩展名为 .x 的档案) 描述字型的资源 (扩展名为 .spritefont 的档案) 纹理 (Texture) 档案,包括扩展名为 .bmp、.dds、.dib、.hdr、.jpg、.pfm、.png、.ppm、和 .tga 等常见的图形格式档案 Microsoft Cross-Platform Audio Creation Tool (XACT) 建立的声音档案 (扩展名为 .xap 的档案) 扩展名为 .MP3、.WMV、和 .WMA 的音乐档案和影片档案 描述游戏数据的 XML 文件 要使用 XNA Game Studio 4.0 设计游戏程序,您可以启动 Visual Studio 2010 Express for Windows Phone 或 Visual Studio 2010 应用程序开发工具,执行 [档案 | 新增 | 项目] 功能,屏幕上就会出现选择欲建立的项目型态的画面,请于左方窗口选择 [Visual C#] 项目底下的 [XNA Game Studio 4.0] 项目,于中间的窗口中选择 [Windows Phone Game (4.0)],表示要建立能够在 Windows Phone 7 智能型手机上执行的游戏程序。选妥项目类型之后请于 [名称] 字段输入项目名称,于 [位置] 字段选择存放项目文件的文件夹,做好之后按下 [确定] 键,执行建立项目的动作,等项目建立成功之后,您就可以在 [方案总管] 窗口中看到两个项目,如图2 所示: 图2:Windows Phone Game (4.0) 项目的内容 其中项目名称为 [项目名称 Content(Content)] 的项目就是负责准备 XNA 游戏程序资源的 Content Pipeline 项目。您只要将 Content Pipeline 预设能够处理的资源文件加入到 Content Pipeline 项目,该资源就会在项目建置的阶段被建置成二进制的资源格式,供游戏程序执行时加载使用。 使用 XNA Game Studio 4.0 设计游戏的优势 使用 XNA Game Studio 4.0 设计游戏程序具有众多的优点,除了可以支持使用高生产力的 C# 程序语言设计游戏程序以外,使用 XNA Game Studio 4.0 设计游戏还具有可以跨越平台和效能优良的优点,分别说明如下: 跨越 PC 个人计算机、Xbox 360 游戏机、以及 Windows Phone 7 智能型手机等平台的游戏开发解决方案。 XNA Game Studio 4.0 支持程序设计师使用相同的程序写法设计能够在 PC 个人计算机、Xbox 360 游戏机、以及 Windows Phone 7 智能型手机等平台上执行的游戏,不需要为不同的平台学习不同的游戏设计方法,使用 XNA Game Studio 4.0 设计上述三种平台的游戏程序时,程序设计师只要注意游戏执行所在的平台的屏幕大小及方向的差异即可,应用程序功能的设计方式几乎完全相同。 欲使用 XNA Game Studio 4.0 开发不同平台的游戏程序,程序设计师可以先使用 Visual Studio 2010 或 Visual Studio 2010 Express for Windows Phone 建立游戏项目,并设计妥游戏的功能,然后使用鼠标的右键点选 [方案总管] 窗口中的项目名称,再从出现的菜单选择 [Create Copy of Project for XXX] 功能 (其中的 XXX 代表:Windows、Xbox 360、或 Windows Phone),就可以依据目前项目的内容建立可以在指定平台上执行的游戏程序项目,Visual Studio 2010 或 Visual Studio 2010 Express for Windows Phone 就会为我们建立新的游戏项目,其做法如图3 所示: 图3:依据现有的项目内容建立新的游戏项目的做法 [提示] 利用同一份源代码开发能够在 Windows 平台、XBOX 360 游戏机、以及 Windows Phone 7 智能型手机执行的游戏时可以善用条件编译语法来处理不同平台的程序写法差异,例如以下的范例就会使用条件编译语法区隔 WINDOWS 和 XBOX 的程序写法和 Windows Phone 7 程序写法的差异,减轻程序设计师维护多份程序项目的负担: #if WINDOWS || XBOX//遊戲在 Windows 平台和 XBOX 遊戲機執行時會執行的程式碼#else      //遊戲在 Windows Phone 7 執行時會執行的程式碼#endif [特别注意] 执行 [Create Copy of Project for XXX] 功能 (其中的 XXX 代表:Windows、Xbox 360、或Windows Phone)虽然可以依据现有的项目内容建立新的项目,但是Visual Studio 2010或Visual Studio 2010 Express并未将现有的项目内容复制一份到新的项目中,而是让新的专案和现有的项目共享同一份源代码以及Content Pipeline项目,换句话说,当您修改现有项目的原始码时,相当于修改新建立的项目的原始码,免去维护多份源代码造成的负担。 使用 XNA Game Studio 4.0 设计游戏程序除了可以利用Pipeline Content功能在项目建置阶段处理游戏程序欲使用的资源,以提升游戏的执行效能以外,XNA Game Studio 4.0 经过长时间 (超过 3 年) 的效能调整,其功能在执行时具有良好的效能,例如 XNA Game Studio 4.0 提供的 Math 函数库中的各项数学功能皆经过效能优化的处理,而且在内存使用方面也可以有效降低游戏执行时产生的 Garbage,避免系统回收游戏程序产生的 Garbage 而影响到执行效能。 [结语] 在这一篇文章中我们为大家介绍了 XNA Game Studio 4.0 的基本功能与支持游戏设计的特性和优势,做为使用 XNA Game Studio 4.0 版设计游戏程序的基础。下一回我们将要为大家介绍如何使用 Visual Studio 2010 或 Visual Studio 2010 Express for Windows Phone 设计以 XNA 为基础的 Windows Phone 7 游戏程序。

网络——从Lobby移动到实际游戏

clock 三月 8, 2011 09:38 by author alex
问题 创建了会话后,你想给所有玩家时间用来聚集、聊天、让他们可以示意已经准备好可以进行游戏了。 解决方案 XNA在会话状态和玩家的IsReady属性中带有基本的lobby功能。 会话开始于Lobby状态。只有主机可以调用NetworkSession. StartGame方法,这个方法将会话移动到Playing状态。主机可以基于所有玩家的IsReady状态做出决定。 工作原理 当会话创建后,会话的SessionState属性会拥有Lobby值。 当在这个状态时,你需要让所有在这个会话中的玩家可以示意他们已经做好了准备,这可以通过将他们的IsReady设为true做到。这个值可以被会话中的所有玩家读取: case GameState.InSession: ...{ switch (networkSession.SessionState) ... { case NetworkSessionState.Lobby: ...{ if (keybState != lastKeybState) ... { if (keybState.IsKeyDown(Keys.R)) { LocalNetworkGamer localGamer = networkSession.LocalGamers[0]; localGamer.IsReady = !localGamer.IsReady; } } } break; case NetworkSessionState.Playing: ... { } break; } networkSession.Update(); } 主机需要检测是否所有玩家都将IsReady设为了true。最简单的方法是检查NetworkSess ion.IsEveryoneReady的值,如果所有人都准备好了,主机会调用NetworkSession.StartGame方法,这个方法将状态从Lobby移动到Playing: case NetworkSessionState.Lobby: ...{ if (keybState != lastKeybState) ... { if (keybState.IsKeyDown(Keys.R)) ... { LocalNetworkGamer localGamer = networkSession.LocalGamers[0]; localGamer.IsReady = !localGamer.IsReady; } } if (networkSession.IsHost) ...{ if (networkSession.AllGamers.Count > 1) ...{ if (networkSession.IsEveryoneReady) ...{ networkSession.StartGame(); log.Add("All players ready -- start the game!"); } } } } break; 当状态改变至Playing时,会话会引发GameStarted事件,这个事件所有玩家都会进行监听。这样可以让他们绘制游戏屏幕并开始发送和接收游戏数据。 当主机决定结束游戏时,需要调用NetworkSession.EndGame方法,这个方法将所有玩家的IsReady重置为false并返回Lobby状态。 case NetworkSessionState.Playing: ...{ if (networkSession.IsHost) ... { if (keybState != lastKeybState) ... { if (keybState.IsKeyDown(Keys.E)) ...{ networkSession.EndGame(); } } } } break; 这会引发会话的GameEnded事件,所有玩家都可以监听这个事件,这样他们就可以,例如,绘制lobby图像。 代码 InSession状态包含大多数基本代码让多个玩家表示他们的IsReady,让主机从Lobby状态移动到Playing状态或返回: case GameState.InSession: ...{ switch (networkSession.SessionState) ...{ case NetworkSessionState.Lobby: ...{ if (keybState != lastKeybState) ... { if (keybState.IsKeyDown(Keys.R)) ... { LocalNetworkGamer localGamer = networkSession.LocalGamers[0]; localGamer.IsReady = !localGamer.IsReady; } } if (networkSession.IsHost) ... { if (networkSession.AllGamers.Count > 1) ...{ if (networkSession.IsEveryoneReady) ...{ networkSession.StartGame(); log.Add("All players ready -- start the game!"); } } } } break; case NetworkSessionState.Playing: ... { if (networkSession.IsHost) ... { if (keybState != lastKeybState) ... { if (keybState.IsKeyDown(Keys.E)) ... { networkSession.EndGame(); } } } } break; } networkSession.Update(); } break;

友情链接赞助