PS:自己翻译的,转载请著明出处
可变形的水平面
sniperED007
(译者:形变精灵是例子中的圆形)
首先我将会创建一个新的XNA2.0项目,接着我准备使用Paint.Net创建一个水平面。
我将会创建新的图象,用一个大小是800(w)*600(h)作为这个XNA2.0项目使用的默认大小。现在我们准备好去开始绘制水平面,选择这个Paintbrush工具和改变Antialiased(平滑)按钮去Antialiasing Disabled(反锯齿禁用).然后绘制你的水平面的大致轮廓(最好是黑色)。
你现在应该有类似的做法:
从这里选择上部部分(黑线以上)用魔术棒工具,压下delete键。则上部部分现在改变成灰色和灰白格子盒子,这代表alpha层。
选择你的想要的层颜色,然后选择Paint Bucket(油漆桶)工具并且点击在你的水平层底部点击。
现在你可以做下面的事情了:
在这里你就可以保存了,点击Save As和选择这个"Save as type:"到"PNG(*.png)"并且命名它"level"
所以,我们的水平面完成了,现在让我们创建我们的变形图象,我们用一个非常熟悉的方式作为这个水平面来实现这个,所以我习惯把它一步一步完成。
我准备使变形的图象是128(w)*128(h),基本上它是一个圆形(所以我们绘制它使用Ellipse(椭圆)工具,使用相同的Brush和Anti-Aliasing操作作为我用在这个水平面上。)
因为我们将会使用这个图象作为我们变形水平面的图象,我们需要指定水平面的里层是白色(我们将用alpha象素代替这段代码),椭圆的外面将会是透明的/alpha象这样的。然后保存这个为一个PNG文件,以及称它为"deform".或者你可以下面我的副本使用。
最后让我们创建一个非常棒的天空背景,创建一个新的图象800*600,然后选择一个非常棒的蓝色为你的主要颜色并且留下你的白色的合成颜色。然后简单的从Effects菜单"Effects->Render->Clouds"选择并且点击OK按钮。保存这个图象为sky.jpg(注意!我们不需要去保存这个为一个PNG文件作为不包含alpha层的天空图象,所以通过使用一个JPG格式保存到磁盘空间里)
好,现在我们准备开始编写代码了。
拖放上面两副png文件和这个sky.jpg,你创建它到你的内容文件夹在你的工程中。
现在打开Game1.cs文件并且让我们添加下面的的代码行:
1 SpriteBatch spriteBatch;
我们需要声明这个天空,水平面,和变形精灵:
1 private Texture2D textureSky;
2 private Texture2D textureLevel;
3 private Texture2D textureDeform;
然后在LoadContent类,让我们通过加在我们的内容来取代//TODO:
2 private Texture2D textureLevel;
3 private Texture2D textureDeform;
1 textureSky = Content.Load<Texture2D>("sky");
2 textureLevel = Content.Load<Texture2D>("level");
3 textureDeform = Content.Load<Texture2D>("deform");
我们准备去开始绘制类,所以让我们用下面的代码取代这个在Draw功能中的//TODO注释:
2 textureLevel = Content.Load<Texture2D>("level");
3 textureDeform = Content.Load<Texture2D>("deform");
1 spriteBatch.Begin();
2 spriteBatch.Draw(textureSky, new Vector2(0, 0), Color.White);
3 spriteBatch.Draw(textureLevel, new Vector2(0, 0), Color.White);
4 spriteBatch.Draw(textureDeform, new Vector2(100, 100), Color.White);
5 spriteBatch.End();
2 spriteBatch.Draw(textureSky, new Vector2(0, 0), Color.White);
3 spriteBatch.Draw(textureLevel, new Vector2(0, 0), Color.White);
4 spriteBatch.Draw(textureDeform, new Vector2(100, 100), Color.White);
5 spriteBatch.End();
好,我们已经准备去创建这个游戏和看下我们的水平面,它应该看起来象这个样子:
不坏,只有11行新代码!
Moving the deform sprite around(左右移动变形精灵)
为了移动这个变形精灵,我们将会使用鼠标来实现。所以如果你现在运行这个游戏,你将会注意到这个鼠标指针在窗口内被隐藏。让我们改变它,让它可见。
在这个初始的功能中用下面的代码代替//TODO注释:
1 this.IsMouseVisible = true;
接下来我们声明Vector2变量去保存鼠标的位置(添加它在我们声明的Texture2D声明的下面)同样声明当前鼠标的状态:
1 private Vector2 mousePosition;
2 private MouseState currentMouseState;
现在我们需要更新mousePosition,这样让我们为它创建一个新的功能:
2 private MouseState currentMouseState;
1 protected void UpdateMouse()
2 {
3 currentMouseState = Mouse.GetState();
4 // This gets the mouse co-ordinates
5 // relative to the upper left of the game window
6 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
7 }
然后,我们用一个我们刚才创建的UpdateMouse函数调用来代替//TODO:注释行在Update函数中。
2 {
3 currentMouseState = Mouse.GetState();
4 // This gets the mouse co-ordinates
5 // relative to the upper left of the game window
6 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
7 }
1 UpdateMouse();
最后为这个textureDeform精灵去修改这个draw调用,使用鼠标的位置:
1 spriteBatch.Draw(textureDeform, mousePosition, Color.White);
按下F5试一试。
Deforming the Level(使水平面变形)
为了从视觉上形变这个水平面,我们需要获取水平面的纹理数据,并且把它放入数组中,然后保存在单位值为每一个纹理的co-ordinate,然后我们修改水平面的部分的这个数组值,用户"pastes"(粘下)这个形变图象,通过设置水平面数组去等于形变精灵的数组。最后我们更新水平面纹理用新的数组值。
所以,让我们首先加载形变象素数组,这样在它的下面,你声明这个currentMouseState添加下面的代码行:
1 private uint[] pixelDeformData;
然后在这个LoadContent函数添加下面代码在你加载的textureDeform后面:
1 // Declare an array to hold the pixel data
2 pixelDeformData = new uint[textureDeform.Width * textureDeform.Height];
3 // Populate the array
4 textureDeform.GetData(pixelDeformData, 0, textureDeform.Width * textureDeform.Height);
请记住,因为我们可以形变这个水平面无限次,我们需要加载象素数组每一次,你试图形变这个水平面,所以让我们做这个在一个单独的功能中,当鼠标被单击我们可以调用它。
2 pixelDeformData = new uint[textureDeform.Width * textureDeform.Height];
3 // Populate the array
4 textureDeform.GetData(pixelDeformData, 0, textureDeform.Width * textureDeform.Height);
1 protected void DeformLevel()
2 {
3 // Declare an array to hold the pixel data
4 uint[] pixelLevelData = new uint[textureLevel.Width * textureLevel.Height];
5 // Populate the array
6 textureLevel.GetData(pixelLevelData, 0, textureLevel.Width * textureLevel.Height);
7 for (int x = 0; x < textureDeform.Width; x++)
8 {
9 for (int y = 0; y < textureDeform.Height; y++)
10 {
11 pixelLevelData[((int)mousePosition.X + x) + ((int)mousePosition.Y + y)* textureLevel.Width] = pixelDeformData[x + y * textureDeform.Width];
12 }
13 }
14 // Update the texture with the changes made above
15 textureLevel.SetData(pixelLevelData);
16 }
在我们测试它之前,我们需要实际上调用这个功能通过鼠标的左点击。2 {
3 // Declare an array to hold the pixel data
4 uint[] pixelLevelData = new uint[textureLevel.Width * textureLevel.Height];
5 // Populate the array
6 textureLevel.GetData(pixelLevelData, 0, textureLevel.Width * textureLevel.Height);
7 for (int x = 0; x < textureDeform.Width; x++)
8 {
9 for (int y = 0; y < textureDeform.Height; y++)
10 {
11 pixelLevelData[((int)mousePosition.X + x) + ((int)mousePosition.Y + y)* textureLevel.Width] = pixelDeformData[x + y * textureDeform.Width];
12 }
13 }
14 // Update the texture with the changes made above
15 textureLevel.SetData(pixelLevelData);
16 }
所以添加鼠标的点击支持,我们需要调用currentMouseState.LefButto==ButtonState.Pressed,这里有一个问题,然后每一祯调用UpdateMouse功能,同时鼠标按钮被压下。我们只想要去调用它每次点击一次。
为了实现这个我们需要更新这个"UpdateMouse"功能去保存一个之前的历史鼠标状态,然后检查这个之前的鼠标状态被设置成压下并且当前的鼠标状态被设置成释放(它表示一个点击)。
这里是调用DeformLevel()更新的功能:
1 protected void UpdateMouse()
2 {
3 MouseState previousMouseState = currentMouseState;
4 currentMouseState = Mouse.GetState();
5 // This gets the mouse co-ordinates
6 // relative to the upper left of the game window
7 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
8 // Here we make sure that we only call the deform level function
9 // when the left mouse button is released
10 if (previousMouseState.LeftButton == ButtonState.Pressed &¤tMouseState.LeftButton == ButtonState.Released)
11 {
12 DeformLevel();
13 }
14 }
现在你可以开始这个游戏并且试一试..(注意!我们不能做任何的错误检查当执行这地形时,所以确保你只能点击有效果的区域在水平面上,不能离边界太近,否则你也许到数组的边界外面去了。)2 {
3 MouseState previousMouseState = currentMouseState;
4 currentMouseState = Mouse.GetState();
5 // This gets the mouse co-ordinates
6 // relative to the upper left of the game window
7 mousePosition = new Vector2(currentMouseState.X, currentMouseState.Y);
8 // Here we make sure that we only call the deform level function
9 // when the left mouse button is released
10 if (previousMouseState.LeftButton == ButtonState.Pressed &¤tMouseState.LeftButton == ButtonState.Released)
11 {
12 DeformLevel();
13 }
14 }
你可能会以这样的事情告终当你点击水平面的附近时:
所以,我们现在需要做的是更新水平面的纹理用一个变形水平面的副本,现在让我们测试象素的颜色在这个形变数组中去看看它是否是alpha象素,如果它是,然后我们不需要去更新这个水平面象素颜色,这将防止square alpha层显示在周围形变精灵上面。我们可以做同样的象素测试在水平面数组上。
放入pixelLevelData在它的里面,if语句应该象这个样子:
1 // Here we check that the current co-ordinate of the deform texture is not an alpha value
2 // And that the current level texture co-ordinate is not an alpha value
3 if (pixelDeformData[x + y * textureDeform.Width] != 16777215&& pixelLevelData[((int)mousePosition.X + x) +((int)mousePosition.Y + y) * textureLevel.Width] != 16777215)
4 {
5
6 pixelLevelData[((int)mousePosition.X + x) + ((int)mousePosition.Y + y)* textureLevel.Width] = pixelDeformData[x + y * textureDeform.Width];
7 }
现在,如果你运行这个游戏,它将看起来象这样,当你在周围单击,这是更接近预期的效果:2 // And that the current level texture co-ordinate is not an alpha value
3 if (pixelDeformData[x + y * textureDeform.Width] != 16777215&& pixelLevelData[((int)mousePosition.X + x) +((int)mousePosition.Y + y) * textureLevel.Width] != 16777215)
4 {
5
6 pixelLevelData[((int)mousePosition.X + x) + ((int)mousePosition.Y + y)* textureLevel.Width] = pixelDeformData[x + y * textureDeform.Width];
7 }
最后一步是使任何的象素,在变形数组里到alpha是白色。并且使它更robust,我们需要去做一些错误检查去避免任何的问题,当形变这个水平面离边界太近。
这里是最终的功能:
1 /// <summary>
2 /// 16777215 = Alpha
3 /// 4294967295 = White
4 /// <summary>
5 protected void DeformLevel()
6 {
7 // Declare an array to hold the pixel data
8 uint[] pixelLevelData = new uint[textureLevel.Width * textureLevel.Height];
9 // Populate the array
10 textureLevel.GetData(pixelLevelData, 0, textureLevel.Width * textureLevel.Height);
11 for (int x = 0; x < textureDeform.Width; x++)
12 {
13 for (int y = 0; y < textureDeform.Height; y++)
14 {
15 // Do some error checking so we dont draw out of bounds of the array etc..
16 if (((mousePosition.X + x) < (textureLevel.Width)) &&((mousePosition.Y + y) < (textureLevel.Height)))
17 {
18 if ((mousePosition.X + x) >= 0 && (mousePosition.Y + y) >= 0)
19 {
20 // Here we check that the current co-ordinate
21 // of the deform texture is not an alpha value
22 // And that the current level texture co-ordinate
23 // is not an alpha value
24 if (pixelDeformData[x + y * textureDeform.Width] != 16777215&& pixelLevelData[((int)mousePosition.X + x) +((int)mousePosition.Y + y) * textureLevel.Width] != 16777215)
25 {
26 // We then check to see if the deform
27 // texture's current pixel is white (4294967295)
28 if (pixelDeformData[x + y * textureDeform.Width] == 4294967295)
29 {
30 // It's white so we replace it with an Alpha pixel
31 pixelLevelData[((int)mousePosition.X + x) + ((int)mousePosition.Y + y)* textureLevel.Width] = 16777215;
32 }
33 else
34 {
35 // Its not white so just set the level texture pixel
36 // to the deform texture pixel
37 pixelLevelData[((int)mousePosition.X + x) + ((int)mousePosition.Y + y) * textureLevel.Width] =pixelDeformData[x + y * textureDeform.Width];
38 }
39 }
40 }
41 }
42 }
43 }
44 // Update the texture with the changes made above
45 textureLevel.SetData(pixelLevelData);
46 }
现在看起来象这样(注意发生变形地方仍然有一个很好的黑色边框):2 /// 16777215 = Alpha
3 /// 4294967295 = White
4 /// <summary>
5 protected void DeformLevel()
6 {
7 // Declare an array to hold the pixel data
8 uint[] pixelLevelData = new uint[textureLevel.Width * textureLevel.Height];
9 // Populate the array
10 textureLevel.GetData(pixelLevelData, 0, textureLevel.Width * textureLevel.Height);
11 for (int x = 0; x < textureDeform.Width; x++)
12 {
13 for (int y = 0; y < textureDeform.Height; y++)
14 {
15 // Do some error checking so we dont draw out of bounds of the array etc..
16 if (((mousePosition.X + x) < (textureLevel.Width)) &&((mousePosition.Y + y) < (textureLevel.Height)))
17 {
18 if ((mousePosition.X + x) >= 0 && (mousePosition.Y + y) >= 0)
19 {
20 // Here we check that the current co-ordinate
21 // of the deform texture is not an alpha value
22 // And that the current level texture co-ordinate
23 // is not an alpha value
24 if (pixelDeformData[x + y * textureDeform.Width] != 16777215&& pixelLevelData[((int)mousePosition.X + x) +((int)mousePosition.Y + y) * textureLevel.Width] != 16777215)
25 {
26 // We then check to see if the deform
27 // texture's current pixel is white (4294967295)
28 if (pixelDeformData[x + y * textureDeform.Width] == 4294967295)
29 {
30 // It's white so we replace it with an Alpha pixel
31 pixelLevelData[((int)mousePosition.X + x) + ((int)mousePosition.Y + y)* textureLevel.Width] = 16777215;
32 }
33 else
34 {
35 // Its not white so just set the level texture pixel
36 // to the deform texture pixel
37 pixelLevelData[((int)mousePosition.X + x) + ((int)mousePosition.Y + y) * textureLevel.Width] =pixelDeformData[x + y * textureDeform.Width];
38 }
39 }
40 }
41 }
42 }
43 }
44 // Update the texture with the changes made above
45 textureLevel.SetData(pixelLevelData);
46 }
你可以应用相同的原理去添加到水平面,例如你可以粘贴玩家的尸体到地形或墓碑,玩家死在那了。
源代码:http://www.ziggyware.com/readarticle.php?article_id=154
(完)