本文主要是介绍XNA教程(二)—— Into the 2D World,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
作者:Mike Fleishauer & clayman
本文版权归原作者所有,仅供个人学习使用,请勿转载,勿用于任何商业用途。
由于本人水平有限,难免出错,不清楚的地方请大家以原著为准。欢迎大家和我多多交流。
Blog:http://blog.csdn.net/soilwork
clayman_joe@yahoo.com.cn
special thanks to Mike Fleishauer ^_^
在第一章里,我们介绍了关于 XNA 的一些基础知识。但坦白的说,至今为止,我们还没有编写任何代码,而且只创建了一个单调的蓝色屏幕。
这一章,我们将尝试在屏幕上绘制一些东西,简单的 2D 图形。虽然 2D 游戏的时代已经渐渐远去,但即使你编写一个全 3D 的游戏,也不得不处理处理一些 2D 图形,比如简单的选项菜单、 HUD ( head up displays )等等。好了让我们开始把。
打开上一章创建的项目,当然,你也可以创建一个新 XNA 项目。把解决方案改名为“ Chapter2”,把工程和Game1.cs 都重命名为“ Sprite ”,当弹出确认更改文件名的对话框时,点击确认。
接下来,我们将在屏幕上绘制一张 2D 图片。但在这之前,需要介绍一点关于 Sprite 的概念。
什么是 Sprite ?
Sprite ,也称为精灵,是一个直接绘制到屏幕上的 2D 图形。在传统的 2D 游戏中,你所看到的一切几乎都是 sprite 。但在 3D 游戏中,比如 Halo , sprite 逐渐演变为了用于增加 3D 图形视觉效果的纹理。在讨论 3D 图形时我们会详细讲解它。现在,简单的把 sprite 认为是 2D 图形就可以了。
继续,我们将把一些外部资源添加到工程中,为了方便管理,统一把资源放到一个单独的文件夹中。在 Solution Explorer 邮件点击 Add->New Floder ,命名为 Graphics 。接下来,邮件点击新创建的文件夹 Add->Existion Item…. 在弹出窗口中,导航到安装 MC2 源代码的目录下,在 /Source/Data/Art 中,选择 mcl_splashscreen_planet_2.tga. 文件。(当然,可以选择一张任何你喜欢的图片)。
接下来,编写代码:
namespace Chapter2
{
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 SpriteBatch(this.graphics.GraphicsDevice);
_sprite = Texture2D.FromFile(this.graphics.GraphicsDevice, "../../Graphics/mcl_splashscreen_planet_2.tga");
}
protected override void Update()
{
float elapsed = (float)ElapsedTime.TotalSeconds;
UpdateComponents();
}
protected override void Draw()
{
if (!graphics.EnsureDevice())
return;
graphics.GraphicsDevice.Clear(Color.Black);
graphics.GraphicsDevice.BeginScene();
_sb.Begin();
_sb.Draw(_sprite, new Vector2(0.0f, 0.0f), Color.Red);
_sb.End();
DrawComponents();
graphics.GraphicsDevice.EndScene();
graphics.GraphicsDevice.Present();
}
}
}
(加粗部分为我们添加的代码)
这些代码是什么意思呢?
首先,我们为 Sprite 类添加了两个全新的成员:
private Microsoft.Xna.Framework.Graphics.SpriteBatch _sb;
private Microsoft.Xna.Framework.Graphics.Texture2D _sprite;
_sb 是一个SpriteBatch对象。SpriteBatch对象代表了一批sprite,并且将在同样的状态设置下,绘制他们。大多数情况下,几乎所有的sprite都在同一个批次中。
_sprite 实际上是一张2D的纹理。它代表了一张将要绘制到屏幕上的图片。我们稍后将讨论不同类型的纹理,现在,只需知道2D纹理储存了在X和Y方向上,每个像素的颜色信息。也可以就把Texture2D认为是一张图片。XNA直接支持jpg,tga,dds,bmp,png格式的文件作为纹理。
_sb = new SpriteBatch(this.graphics.GraphicsDevice);
使用GraphicsDevice对象作为参数,实例化SpriteBatch。这里,参数的含义表示以后将用哪一个(一个程序中可以有多个GraphicsDevice)GraphicsDevice对象绘制_sb。
_sprite = Texture2D.FromFile(this.graphics.GraphicsDevice, "../../Graphics/mcl_splashscreen_planet_2.tga");
这行代码把图片加载到内存中,实例化 Texture2D 对象。同样把当前的 graphics device 和图片的路径作为参数。如果在给定路径没有找到所要的图片,那么这个方法将抛出一个异常。
graphics.GraphicsDevice.Clear(Color.Black);
把屏幕清理为黑色。上一节已经介绍过如何使用这个方法。现在我会告诉你为什么需要调用这个方法。如果把渲染比作绘图,那么显存就是我们的画板,通常把用于绘图的显存称为帧缓冲,如何不清理帧缓冲,那么上次在帧缓冲中绘制的图形仍然会保留在其中,并且这些数据处于一种不确定的状态。假设我们下次只在屏幕的左上角绘制图形,那么显示时,除了进行绘制的区域,其他部分可能会显示一些随机数据,相当于我们在一块绘制了大量图形的旧画板上绘图。因此,需要用 Clear 方法对帧缓冲进行初始化,填充为某个我们希望的背景颜色。
_sb.Begin();
_sb.Draw(_sprite, new Vector2(0.0f, 0.0f), Color.Red);
_sb.End();
和之前提到的 BeginScenne 和 EndScene 一样, _sb.Begin 和 _sb.End 方法告诉图形设备我们将要绘制 sprite ,所有绘制 sprite 的代码都必须在这两个方法之间。 Draw 方法是真正绘制图形的地方。这里的参数告诉显卡从坐标位置为( 0 , 0 )的地方开始绘制 sprite 。注意,绘制 sprite 的,所使用的是屏幕坐标系,这意味着屏幕中的每个像素对应一个( x , y ) , 屏幕左上角的坐标总是( 0 , 0 ),而右下的坐标则取决于屏幕分辨率,如果分辨率为 1024 x 768 那么右下的坐标就是( 1024 , 768 )。绘制 sprite 的位置应该在这两个坐标之间。
运行程序看看吧:
虽然依旧很单调,但总是有了进步。
这里你可能会有一些问题:为什么原来蓝色的天空,现在“燃烧”了起来。
注意看绘制 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 安装路径的 Utilities/Bin/x86 件夹下)。选择 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.FromFile(graphics.GraphicsDevice, "../../Graphics/star.dds");
}
protected override void Draw()
{
…………… ..
graphics.GraphicsDevice.BeginScene();
_sb.Begin(SpriteBlendMode.AlphaBlend);
_sb.Draw(_sprite, new Vector2(0.0f, 0.0f), Color.White);
_sb.Draw(_sprite, new Vector2(_sprite.Width, 0.0f), Color.White);
_sb.Draw(_sprite, new Vector2(_sprite.Width, _sprite.Height), Color.White);
_sb.Draw(_sprite, new Vector2(0.0f, _sprite.Height), Color.White);
_sb.Draw(_spriteStar, new Vector2(50.0f, 50.0f), Color.Yellow);
_sb.End();
……………………………
}
运行程序,你因该可以看到下图所示的结果 :
注意到我们只显示了红色的部分没有?这都是透明遮罩的功劳。简要来说,你使用 DirectX 纹理工具,告诉了图片哪些部分需要渲染,哪些部分是透明的,注意这里使用了 dds 格式的文件,而不是 bmp 格式。
你因该对这行代码比较感兴趣:
_sb.Begin(SpriteBlendMode.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 SpriteBatch(this.graphics.GraphicsDevice);
_sbForeground = new SpriteBatch(this.graphics.GraphicsDevice);
_sprite = Texture2D.FromFile(this.graphics.GraphicsDevice, "../../Graphics/mcl_splashscreen_planet_2.tga");
_spriteStar = Texture2D.FromFile(graphics.GraphicsDevice, "../../Graphics/star.dds");
}
protected override void Update()
{
float elapsed = (float)ElapsedTime.TotalSeconds;
UpdateComponents();
}
protected override void Draw()
{
if (!graphics.EnsureDevice())
return;
graphics.GraphicsDevice.Clear(Color.Black);
graphics.GraphicsDevice.BeginScene();
_sbBackground.Begin();
_sbBackground.Draw(_sprite, new Vector2(0.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 Vector2(0.0f, _sprite.Height), Color.White);
_sbBackground.End();
_sbForeground.Begin(SpriteBlendMode.AlphaBlend);
_sbForeground.Draw(_spriteStar, new Vector2(50.0f, 50.0f), Color.White);
_sbForeground.End();
DrawComponents();
graphics.GraphicsDevice.EndScene();
graphics.GraphicsDevice.Present();
}
}
}
好了,第二部分到次结束,下一章,我们将让 sprite 动起来~~。
这篇关于XNA教程(二)—— Into the 2D World的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!