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 动起来~~。
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值