【VC++开源代码栏目提醒】:网学会员在VC++开源代码频道为大家收集整理了“XNA教程 - 编程语言“提供大家参考,希望对大家有所帮助!
XNA教程 虽然我们将会由浅入深逐步
学习XNA但在开始之前你最好具备以下知识熟悉C 有基本的程序设计经验一定的图形学知识包括3D、2D坐标变换纹理光照等等当然如果你看过我原来翻译的managed dirctx kick start或者本来就熟悉MDX那么学起来将会更加得心应手。
此外你还需要以下工具 1 Visual Studio Express 2005注意当前的XNA GSE还无法集成到VS pro中 2 XNA Game Studio Express 3 最新版本的DirectX SDK 4 当然最好还有Mech Commander2的源
代码Mech Commander2是MS发行的一个游戏现在
开源了大概800m这个连接不是太稳定多试几次。
一切准备就绪了但我们还需要弄清楚一些概念 XNA / XNA Framework他是一个托管的专门针对游戏开发不仅仅是绘图的函数库基于Direct3D 9和.net framework 2.0可以同时在Windows和Xbox360上运行。
Managed DirectX 2.0MDX 1.0的升级版用于在.net framework 2.0下使用directx但ms后来决定提供一个完全用于游戏开发的API而不仅仅只是一套绘图函数库因此 把MDX 2.0整合到了XNA中单独的MDX 2.0也成为了一个被取消的计划。
假设你从来没有使用过
VC Express首先打开程序: 选择 Windows GameXNA摸版输入一个你喜欢的名字然后单击OK。
恭喜你已经使用XNA创建了你的第一个windows游戏_ 选择 Build- Build Solution然后按下F5或者 Debug - Start Debugging现在可以看到你的程序在运行了一个淡蓝色的窗口。
好吧我承认它并不是很有吸引力但作为nxa的“hello world”程序它其实传达了许多很重要的概念。
来看看模板生成的
代码 打开Program.cs文件 using System namespace WindowsGame1 static class Program /// /// The main entry point for the application. /// static void Mainstring args using Game1 game new Game1 game.Run 很简单所有程序都需要一个入口点这里就是程序开始运行的地方。
在main方法中我们创建了Game1类的一个实例并且调用了它的run方法。
来看看类Game1大部分工作都在这里完成。
打开Game1.cs文件 using System using System.Collections.Generic using Microsoft.Xna.Framework using Microsoft.Xna.Framework.Audio using Microsoft.Xna.Framework.Components using Microsoft.Xna.Framework.Graphics using Microsoft.Xna.Framework.Input using Microsoft.Xna.Framework.Storage namespace WindowsGame1 /// /// This is the main type for your game /// partial class Game1 : Microsoft.Xna.Framework.Game //Game1继承于Microsoft.Xna.Famework.Game //Game类是一个游戏的基本骨架这个对象把你从创建窗口 // 设置图形卡等其他琐碎设置的操作中解放出来 public Game1 InitializeComponent protected override void Update // The time since Update was called last float elapsed floatElapsedTime.TotalSeconds // TODO: Add your game logic here // Let the GameComponents update UpdateComponents protected override void Draw // Make sure we have a valid device if graphics.EnsureDevice return graphics.GraphicsDevice.ClearColor.CornflowerBlue graphics.GraphicsDevice.BeginScene // TODO: Add your drawing code here // Let the GameComponents draw DrawComponents graphics.GraphicsDevice.EndScene graphics.GraphicsDevice.Present 如何改变窗口的大小呢如何全屏显示呢 在Solution Explorer中右击Game1选择
设计视图。
选择graphics组件。
如前所属我们在程序运行时就创建了一个一旦
程序开始运行之后就会不停调用Update方法。
可以把这里认为是你的“game loop”。
这里将有大量的事件将要发生包括AI计算玩家的位置移动输入等等所有更新操作都在这里处理。
对这个程序来说我们只是简单的更新程序运行至今的时间。
之后调用了UpdateComponents方法它将自动调用程序中其他子组件的Update方法。
确保调用这个方法很重要组件component在xna中扮演了很重要的角色为了保证这些组件的行为正确这是必须的。
完成了实在在屏幕上绘图的操作。
当程序创建时将会创建一个名为graphics的对象它是GraphicsComponent类的一个实例。
GraphicsComponent是XNA中一个很重要也是很基本的类提供了原来MDX中Presentationparameters和Deivce的功能。
特别是GraphicsComponent的GraphicsDevice成员其方法和属性和MDX中的Device都是类似的。
首先确保创建了可用的设备否则跳出这个方法 把屏幕清理为背景颜色Cornflower Blur当然这里可以把背景设置为任何颜色但通常都是黑色。
告诉显卡你将要开始在场景中绘制物体 告诉每个子组件绘制其本身 告诉图形卡绘图操作已经完成 最后把绘制好的图形呈现到屏幕上 GraphicsComponent对象。
看一下属性窗口 这些都是GraphicsComponent所暴露的默认公共属性。
如果需要全屏显示只需要把IsFullScreen的值该为true就可以了。
如果你查看
代码可以看到在Game1的InitializeComponent方法中自动生成了this.graphics.IsFullScreen true。
如何还需要自动改变窗口大小则需要改变Game1的AllowUserResizing属性在构造函数中添加this.AllowUserResizing true此外你还可以改变Game1的IsMouseVisible属性选择是否显示鼠标。
再次强调组件在XNA中的重要性。
你的游戏是一个组件同样他也包含了许多其他组件你可以把组件理解为搭建游戏的积木。
你已经看到了在update和draw方法中我们依次调用每一个组件的update和draw方法。
这个效果将会传播到每个组件的子组件。
将来我们将会看到更多的组件比如目录组件地形组件FPS计数器之类的助手组件等等。
后面的章节我们将逐渐讨论这些组件。
ok第一讲到此结束。
File- Save All…. 打开上一章创建的项目当然你也可以创建一个新XNA项目。
把解决方案改名为“Chapter2”把工程和Game1.cs都重命名为“Sprite”当弹出确认更改文件名的对话框时点击确认。
接下来我们将在屏幕上绘制一张2D图片。
但在这之前需要介绍一点关于Sprite的概念。
什么是Sprite Sprite也称为精灵是一个直接绘制到屏幕上的2D图形。
在传统的2D游戏中你所看到的一切几乎都是sprite。
但在3D游戏中比如Halosprite逐渐演变为了用于增加3D图形视觉效果的纹理。
在讨论3D图形时我们会详细讲解它。
现在简单的把sprite认为是2D图形就可以了。
继续我们将把一些外部资源添加到工程中为了方便管理统一把资源放到一个单独的文件夹中。
在Solution Explorer邮件点击Add-New Floder命名为Graphics。
接下来邮件点击新创建的文件夹 Add-Existion Item….在弹出窗口中
导航到安装MC2源
代码的目录下在SourceDataArt中选择mcl_splashscreen_planet_2.tga.文件。
当然可以选择一张任何你喜欢的图片。
接下来编写
代码 namespace Chapter2 _sb是一个SpriteBatch对象。
SpriteBatch对象代表了一批sprite并且将在同样的状态设置下绘制他们。
大多数情况下几乎所有的sprite都在同一个批次中。
_sprite实际上是一张2D的纹理。
它代表了一张将要绘制到屏幕上的图片。
我们稍后将讨论不同类型的纹理现在只需知道2D纹理储存了在X和Y方向上每个像素的颜色信息。
也可以就把Texture2D认为是一张图片。
XNA直接支持jpgtgaddsbmppng格式的文件作为纹理。
使用GraphicsDevice对象作为参数实例化SpriteBatch。
这里参数的含义表示以后将用哪一个一个程序中可以有多个GraphicsDeviceGraphicsDevice对象绘制sb。
这行
代码把图片加载到内存中实例化Texture2D对象。
同样把当前的graphics device和图片的路径作为参数。
如果在给定路径没有找到所要的图片那么这个方法将抛出一个异常。
partial class Sprites : Microsoft.Xna.Framework.Game private Microsoft.Xna.Framework.Graphics.SpriteBatch _sb private Microsoft.Xna.Framework.Graphics.Texture2D _sprite public Sprites InitializeComponent _sb new SpriteBatchthis.graphics.GraphicsDevice _sprite Texture2D.FromFilethis.graphics.GraphicsDevice ../../Graphics/mcl_splashscreen_planet_2.tga protected override void Update float elapsed floatElapsedTime.TotalSeconds UpdateComponents protected override void Draw if graphics.EnsureDevice return graphics.GraphicsDevice.ClearColor.Black graphics.GraphicsDevice.BeginScene _sb.Begin _sb.Draw_sprite new Vector20.0f 0.0f Color.Red _sb.End DrawComponents graphics.GraphicsDevice.EndScene graphics.GraphicsDevice.Present 加粗部分为我们添加的
代码 这些
代码是什么意思呢 graphics.GraphicsDevice.ClearColor.Black 把屏幕清理为黑色。
上一节已经介绍过如何使用这个方法。
现在我会告诉你为什么需要调用这个方法。
如果把渲染比作绘图那么显存就是我们的画板通常把用于绘图的显存称为帧缓冲如何不清理帧缓冲那么上次在帧缓冲中绘制的图形仍然会保留在其中并且这些数据处于一种不确定的状态。
假设我们下次只在屏幕的左上角绘制图形那么显示时除了进行绘制的区域其他部分可能会显示一些随机数据相当于我们在一块绘制了大量图形的旧画板上绘图。
因此需要用Clear方法对帧缓冲进行初始化填充为某个我们希望的背景颜色。
注意看绘制sprite的
代码最后一个参数表示了绘制sprite时的色调。
如果我们把它改为Color.White那么将获得和原图一样的效果。
你看使用XNA轻易就能实现一些特效。
再绘制几个sprite 现在我们有4个相同大小的sprite了。
你已经掌握了2D绘图的基础足够完成一个2D游戏的背景渲染。
再次提醒所有的2D绘图操作都因该在Begin和End方法之间而SpriteBatch方法调用又必须在BeginScene和EndScene方法之间。
简单的说应该按照以下顺序 -- 开始渲染3D图形 l 渲染3D场景 l 开始渲染2D图形 n 渲染2D图片 l 结束2D渲染 -- 结束3D渲染 需要记住2D图形的渲染和绘制他们的顺序有关系。
在叠加区域先渲染的图形总是会被后渲染的图形挡住和在普通画布上绘图的原理一样。
这也带领我们进入下一个话题透明。
Transparent Blits 首先如果你要问我Blits是什么含意那么我要告诉你实际上你不必知道它是什么意思.。
Blit的含义来自于BLT表示Block Transfer意思是把一个平面的一部分复制到另一个平面。
好了关键的
问题就在于我们如何把图片中不透明的部分复制到已有图片上。
为了渲染一个带透明效果的sprite先来做一些辅助
工作。
首先打开windows中的绘图板任意绘制一个图形 接下来再创建一张同样大小的图片。
上一张图片是我们希望显示的部分而现在这张图片则作为它的透明遮罩 一般情况下遮罩里白色部分是不透明的而黑色部分则表示透明区域。
把两张图片分别保存为uglyStar.jpg和uglyStarMask.jpg。
为了方便使用把他们都添加到Grahics文件夹中。
现在打开DirectX SDK中的DxTex.exe程序它位于sdk安装路径的UtilitiesBinx86件夹下。
选择File-New Texture…在弹出的窗口中把纹理尺寸设置为32x32把Surface/Volume格式设置为Unsigned 32-bit: A8R8G8B8如图所示 选择“Open onto this surface”打开我们之前创建的红色星星uglyStar.jpg 选择“Open onto Alpha Channel of this Surface”打开uglyStarMask.jpg 最后把文件保存为star.dds并添加到Graphics文件夹。
好了现在有了一张带透明通道的图片如何使用他呢。
把它绘制到之前的天空上吧。
添加如下
代码 …………………. private Microsoft.Xna.Framework.Graphics.Texture2D _spriteStar …………………. public Sprites ……………………… _spriteStar Texture2D.FromFilegraphics.GraphicsDevice ../../Graphics/star.dds protected override void Draw …………….. graphics.GraphicsDevice.BeginScene _sb.BeginSpriteBlendMode.AlphaBlend _sb.Draw_sprite new Vector20.0f 0.0f Color.White _sb.Draw_spritenew Vector2_sprite.Width 0.0f Color.White _sb.Draw_sprite new Vector2_sprite.Width _sprite.Height Color.White _sb.Draw_sprite new Vector20.0f _sprite.Height Color.White _sb.Draw_spriteStar new Vector250.0f 50.0f Color.Yellow _sb.End …………………………… 运行程序你因该可以看到下图所示的结果: 注意到我们只显示了红色的部分没有这都是透明遮罩的功劳。
简要来说你使用DirectX纹理工具告诉了图片哪些部分需要渲染哪些部分是透明的注意这里使用了dds格式的文件而不是bmp格式。
你因该对这行
代码比较感兴趣 _sb.BeginSpriteBlendMode.AlphaBlend 使用AlphaBlend作为SpriteBlendMode参数会告诉XNA将要渲染一些带透明效果的图片。
还记得我先前说过“大多数情况下几乎所有的sprite都在同一个批次中”吗好吧我撒谎了-_-b。
Alpha混合通常需要进行额外的计算因此应该把需要进行Alpha混合的sprite单独作为一个批次。
一个批次用来绘制静态背景图片另一个用来绘制带透明效果的前景物品。
作为一条规则你应该总是把需要alpha混合的sprite作为一个批次 using System using System.Collections.Generic using Microsoft.Xna.Framework using Microsoft.Xna.Framework.Audio using Microsoft.Xna.Framework.Components using Microsoft.Xna.Framework.Graphics using Microsoft.Xna.Framework.Input using Microsoft.Xna.Framework.Storage namespace Chapter2 partial class Sprites : Microsoft.Xna.Framework.Game private Microsoft.Xna.Framework.Graphics.SpriteBatch _sbBackground private Microsoft.Xna.Framework.Graphics.SpriteBatch _sbForeground private Microsoft.Xna.Framework.Graphics.Texture2D _sprite private Microsoft.Xna.Framework.Graphics.Texture2D _spriteStar public Sprites this.AllowUserResizing true this.IsMouseVisible true InitializeComponent _sbBackground new SpriteBatchthis.graphics.GraphicsDevice _sbForeground new SpriteBatchthis.graphics.GraphicsDevice _sprite Texture2D.FromFilethis.graphics.GraphicsDevice ../../Graphics/mcl_splashscreen_planet_2.tga _spriteStar Texture2D.FromFilegraphics.GraphicsDevice ../../Graphics/star.dds protected override void Update float elapsed floatElapsedTime.TotalSeconds UpdateComponents protected override void Draw if graphics.EnsureDevice return graphics.GraphicsDevice.ClearColor.Black graphics.GraphicsDevice.BeginScene _sbBackground.Begin _sbBackground.Draw_sprite new Vector20.0f 0.0f Color.White _sbBackground.Draw_sprite new Vector2_sprite.Width 0.0f Color.White _sbBackground.Draw_sprite new Vector2_sprite.Width _sprite.Height Color.White _sbBackground.Draw_sprite new Vector20.0f _sprite.Height Color.White _sbBackground.End _sbForeground.BeginSpriteBlendMode.AlphaBlend _sbForeground.Draw_spriteStar new Vector250.0f 50.0f Color.White _sbForeground.End DrawComponents graphics.GraphicsDevice.EndScene graphics.GraphicsDevice.Present 好了第二部分到次结束 SpriteBatch.begin通过接受几个参数来控制如何渲染sprite。
参数BlendMode表示进行哪种模式的混合。
Xna只提供了三种也是最常见的混合模式AlphaBlend、Additive以及InverseColor。
这里我们主要讨论AlphaBlend。
AlphaBlend根据当前sprite alpha通道中的值对sprite颜色和帧缓冲中已存在的颜色进行混合。
计算的公式为finalColor spriteColor alpha backgroundColor alpha。
由于要
查询缓冲中的当前颜色因此alpha混合是一个相对较昂贵的操作。
单有SpriteBlendMode并不能完全达到我们所希望的效果当绘制多个图形时还需按照一定的顺序绘制sprite才行。
上面这张图片掩饰了5个半透明的环按照不同顺序绘制时所得到的结果其中后者是我们所期望的效果。
左图中我们最先绘制了中间的圆形因此它将和蓝色的背景混合导致最终的效果有一条蓝边。
而右图中我们最后才绘制中央的圆形。
有两种方法来控制sprite绘制顺序。
第一种使用SpriteBatch.Draw带depth参数的重载让
计算机在绘制图形时根据depth值为我们排序第二种则是在我们自己编写
代码来控制。
当场景中有大量sprite时第一种方法是不错的解决
方案。
为每个sprite都指定一个depth当物体移动时简单的改变它的深度就能获得正确的效果比如2D游戏中让玩家从树后移动到树前。
使用SpriteBatch.begin的sortMode参数来指定从后到前还是从前到后排序通常情况下我们选择前者。
另一种方法侧需要我们自己安排绘图顺序比如先绘制背景然后游戏对象接下来再绘制前方的遮挡物UI等等。
当大量物体相互遮挡时决定绘图顺序的逻辑可能会很复杂。
但是当场景不太复杂时这是一种很高效的方法。
到这里就需要讨论一下几种混合模式下的效率 Y轴表示帧速率X轴表示sprite数量 图中红色是SpriteBlendMode.None绿色是SpriteBlendMode.AlphaBlend蓝色是AlphaBlend以及SpriteSortOptions.BackToFront灰色是SpriteSortOptions.FrontToBack。
可以看到如果自己排序那么在有大量500个sprite的时候能获得相当不错的收益当然这里并未考虑我们手动排序的代价。
而如果使用SpriteBatch来进行排序那么sprite数量最好不要超过400个。
当然如果不使用alpha混合效率又会提高很多。
当然也许你还注意到了SpriteSortOptions.FrontToBack状态下性能异常的好但不幸的是这种方法通常不能创建正确的效果。