在大多数情况中,你想在多个图像的上面绘制另一张图像。主要问题是,如果所有的图像都是完全覆盖屏幕的,那么它们就会覆盖前面的一张图像。你还要确保首先绘制背景图像。
使用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。
图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的右图中。
图3-4 包含多个图像的一张图像(左图)和在一个草地表面上将多张图像混合在一起(右图)
因为草地的层值为1而峭壁图像的层值为0.5,XNA知道应该首先绘制草地,然后才是峭壁。
确保在SpriteBatch . Begin 方法中将SpriteSortMode设置为BackToFront,这样XNA才能知道在绘制它们前需要根据层进行排序:
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.None);
像素的透明值不是非要“全部或没有”。你也可以设置透明值,例如,设置为70%。例如当一辆车驶过草地时,你可以将灰色的车窗设置为70%透明。通过这种方式,当绘制汽车时,车窗的最终颜色将会是30%灰色,70%草地的额颜色。在教程2-13中可以获取更多知识,这叫做alphablending。
默认情况下,使用SpriteBatch类进行绘制时alpha混合是开启的。但是,你可以关闭alpha混合(尝试一些看看有什么不同!)或将模式设置为Additive blending。
在后一种情况中,你在像素中绘制的每个颜色都会添加到像素已有的颜色中。所以使用这种模式时,当你在一个红色像素上绘制蓝色像素时,结果是一个紫色像素。如果在这个紫色像素上绘制一个绿色矩形,结果是一个包含白色像素的绿色矩形。这个模式用在诸如火焰或爆炸的效果中,这些效果都是由许多sprites组成的并附加混合在一起,如教程3-12所示。
XNA可以将设置SpriteBatch类的混合模式作为一个SpriteBatch . Begin方法中的参数,你可以指定SpriteBlendMode中的一个:
Draw 方法中的所有代码前面已经写过了。