处理2D图像和纹理——使用层绘制透明图像

问题

在大多数情况中,你想在多个图像的上面绘制另一张图像。主要问题是,如果所有的图像都是完全覆盖屏幕的,那么它们就会覆盖前面的一张图像。你还要确保首先绘制背景图像。

解决方案

使用XNA,你可以指定层(layer),或者说图像的绘制深度。位于最大深度的图像会被首先绘制。

大多数图像格式支持透明。这意味着除了包含红、绿、蓝的颜色信息,它还包含alpha信息。当开启透明时,图像的透明区域会显示图像下面的东西。

在Xna中,你还可以指定一个颜色变为透明。这对那些没有包含透明信息的图像是很有用的。

工作原理
简单的透明

SpriteBatch类对象可以处理包含透明信息的图像。这在大多数情况中是必须的,例如,当绘制一片草地时,你想在草地上面再绘制一块石头。石头图像是一张简单的白色图像,中间是一块石头。

如果你将这张图像绘制在草地上,那么还可以看到石头图像的白色背景。所以,你需要将图像背景变成透明。通过这种方式,你将石头绘制在草地上,而石头图像的透明部分显示的则是底下草地的颜色。

如果你想让XNA处理图像的透明,需要使用SpriteBatch . Begin () 方法开启alpha混合:


spriteBatch.Begin(SpriteBlendMode.AlphaBlend); 

spriteBatch.Draw(myTexture, Vector2.Zero, Color.White); 

spriteBatch.End();
使用颜色键透明

对那些没有透明信息的图像,你可以指定一个颜色作为透明色。这可以通过设置图像属性中的color key实现。在解决方案浏览器中选择图像,让它的属性显示在窗口的右下方。如图3-3所示。找到Color Key Color,将它改变为你想要的颜色。图像中的这个颜色就会当做透明处理。请确保将Color Key Enabled设置为True。

1

图3-3 设置纹理的Color Key Color属性

使用多个层

如果你想混合多张图像,需要指定哪张图像在最上面。在XNA中,对每张要绘制的图像,你可以以一个介于0到1之间的数字指定层。层1表示这个层首先绘制,层0表示最后绘制,在所有其它层之上。你可以将这个值作为SpriteBatch . Draw方法的最后一个参数。

你需要通过正确设置SpriteBatch.Begin方法的SpriteSortMode开启层排序。

例如,下面的代码首先绘制覆盖整个屏幕的草地。因为这个底层的纹理,你可以安全地将这些图像的层值设置为1。然后绘制一个峭壁,它位于草地之上,所以它们的层值小于1:


Rectangle grassRec = new Rectangle(240, 121, 40, 40); 

Rectangle leftRec = new Rectangle(40, 121, 80, 40); 

Rectangle topleftRec = new Rectangle(40, 0, 80, 80); 

Rectangle topRec = new Rectangle(240, 0, 40, 80); 

Rectangle toprightRec = new Rectangle(320, 0, 80, 80); 

Rectangle rightRec = new Rectangle(320, 121, 80, 40); 

Rectangle bottomrightRec = new Rectangle(320, 281, 80, 120); 

Rectangle bottomRec = new Rectangle(240, 281, 40, 120); 

Rectangle bottomleftRec = new Rectangle(40, 281, 80, 120); 

Rectangle centerRec = new Rectangle(240, 201, 80, 40); 



spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.None); 

for (int x = 0; x < 10; x++) 

    for (int y = 0; y < 10; y++) 

        spriteBatch.Draw(myTexture, new Vector2(x * 40, y * 40), grassRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 1); 



spriteBatch.Draw(myTexture, new Vector2(40, 120), leftRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f); 

spriteBatch.Draw(myTexture, new Vector2(40, 40), topleftRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f); 

spriteBatch.Draw(myTexture, new Vector2(120, 40), topRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f); 

spriteBatch.Draw(myTexture, new Vector2(160, 40), toprightRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f); 

spriteBatch.Draw(myTexture, new Vector2(160, 120), rightRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f); 

spriteBatch.Draw(myTexture, new Vector2(160, 160), bottomrightRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f);

spriteBatch.Draw(myTexture, new Vector2(120, 160), bottomRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f);

spriteBatch.Draw(myTexture, new Vector2(40, 160), bottomleftRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f); 

spriteBatch.Draw(myTexture, new Vector2(120, 120), centerRec, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0.5f); 



spriteBatch.End();

使用两个循环绘制了100个草地块。九张图像构成了峭壁,这九张图像和草地图像都存储在一张图像文件之中,如图3-4左图所示。所以,代码首先声明子图像在图像中所处的矩形。通过将这个矩形作为第三个参数,XNA可以从整张图像中切割出正确的子图像,最终的结果显示在图3-4的右图中。

2

图3-4 包含多个图像的一张图像(左图)和在一个草地表面上将多张图像混合在一起(右图)

因为草地的层值为1而峭壁图像的层值为0.5,XNA知道应该首先绘制草地,然后才是峭壁。

确保在SpriteBatch . Begin 方法中将SpriteSortMode设置为BackToFront,这样XNA才能知道在绘制它们前需要根据层进行排序:


spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.None);
Alpha混合

像素的透明值不是非要“全部或没有”。你也可以设置透明值,例如,设置为70%。例如当一辆车驶过草地时,你可以将灰色的车窗设置为70%透明。通过这种方式,当绘制汽车时,车窗的最终颜色将会是30%灰色,70%草地的额颜色。在教程2-13中可以获取更多知识,这叫做alphablending。

SpriteBlendModes

默认情况下,使用SpriteBatch类进行绘制时alpha混合是开启的。但是,你可以关闭alpha混合(尝试一些看看有什么不同!)或将模式设置为Additive blending。

在后一种情况中,你在像素中绘制的每个颜色都会添加到像素已有的颜色中。所以使用这种模式时,当你在一个红色像素上绘制蓝色像素时,结果是一个紫色像素。如果在这个紫色像素上绘制一个绿色矩形,结果是一个包含白色像素的绿色矩形。这个模式用在诸如火焰或爆炸的效果中,这些效果都是由许多sprites组成的并附加混合在一起,如教程3-12所示。

XNA可以将设置SpriteBatch类的混合模式作为一个SpriteBatch . Begin方法中的参数,你可以指定SpriteBlendMode中的一个:

  • SpriteBlendMode. None关闭混合,忽略任何透明信息将图像绘制到屏幕上,这样会导致覆盖任何屏幕上层深度更大的图像。
  • SpriteBlendMode.AlphaBlend使用部分已存在的颜色和部分新图像中包含的颜色,对应指定在图像中的alpha值。这对应刚才所举的车窗的例子。
  • SpriteBlendMode.Additive将新图像的颜色添加到帧缓冲中已经存在的颜色之中。
代码

Draw 方法中的所有代码前面已经写过了。

3

你可能感兴趣的:(使用)