前面的代码写得太乱了,所以不得不再花一点时间整理一下,这次除了把镰刀手也写成类外,还有其它效果代码的改进。如旋转代码。所以一般旧的代码就整理成类,新的代码再次乱七八糟地放在主场上。经过一周的学习,对类掌握得更熟练了。这周还学会了逐帧动画,像素级的碰撞,还新增了不少“精灵”,如背景和血等。依照惯例,我每次都会把自己的作品的“美照”摆出来让大家观赏。
具体“收获”:
1、Xna绘制“精灵”
这里不是指简单的把精灵画在屏幕上,而是通过Draw里的参数来控制“精灵”的行动。Draw里的参数有Texture2D,Vector2,Color,这三个是最基本的绘图参数,其中Color还可以用以改变精灵颜色以及精灵的透明度(这个在渐显渐隐效果里要用到),只要把Color写成new Color(int x, int y, int z, int a)型就行了,其中最后一个就是透明度的参数。本小结中的背景也有运用到透明度,把精灵变为半透明的视觉效果似乎更好。
除上面三个Draw参数外,还有rotation, origin, Rectangle,scale,effects, layerDepth。其中rotation是用来控制旋转的,origin用来设定精灵的中心,Xna里一般默认精灵中心为左上角。以前写的代码里就是因为不会用origin所以才把旋转效果的代码写得复杂化了。如果把origin设为图像的中心点的话,旋转与“呼吸”效果都不用考虑精灵中心的位移了。(source)Rectangle是用来指明你要画出精灵图像的哪部分。制作逐帧动画时就要用到这个参数。当然也可以把逐帧动画分成很多张图片,而不是一张集成的图,但是这样的话制作图像时它的边界大小就较难控制了,而且制作步骤变多,代码也显得更烦琐了。scale是控制所画图像的大小的参数,effects能够提供翻转效果(水平翻转与垂直翻转),而最后的layerDepth是控制绘制精灵时的深度的。
2、Flash制作精灵
我使用的大多数精灵(在本游戏则是全部精灵)都是自己用Flash画的。因为对Flash的操作比较熟悉以及正好Flash能导出Xna所支持的Png透明图片,所以我首选Flash为作图工具。这里就介绍制作子弹爆炸集成图的方法。首先当然还是用我上次笔记里所价绍的圆切法来画每一帧的爆炸图的轮廓(而它的轮廓而是我在纸上先画好了然后再用Flash画)。画好之后的上色也是一个问题,很容易越过边界的(当然这里稍微越过也没什么大问题,不过在一些精确度要求较高的地方就要注意)。要克服这个问题就要用到遮罩,使用轮廓图来做遮罩,然后可以在它的下一层胡乱“涂鸦”了。
再说说集成图制作的另一难题(也许只是对一周之前的我来说是难题,呵呵),就是图的集成了。因为爆炸图里各图大小不一,所以不能单凭它们的座标就能轻松把它们排列好。最后我想到的方案是:先画好格子,再把各图手动填入格子内,格子大小一般以最大的那个爆炸图为准。我认为除此之外最好还要把格子画成正方形,以后有旋转的需要的画就好办多了。排好图后就面临最后一个问题,因为集成图里是不能有格子的,但是把格子去掉的话导出的图片的大小会与想得到的有出入。因为格子去掉时导出图片的话,Flash会把没内容的像素去掉。解决这个问题的方法是把格子的Alpha值调为透明就行了。
3、像数碰撞函数。
这个是在网上学到的,因为我对Xna的函数知识面并不广。具体思路是,当两个精灵(没旋转)的区域(矩形)有重叠时,然后再检测重叠部分两精灵的所有像数是不是Alpha都不为0,如果是的话则两精灵有碰撞。具体代码如下:
private int collision(Texture2D a, Texture2D b, int ax, int ay, int bx, int by)
{
if (Math.Abs(ax + a.Width / 2 - bx - b.Width / 2) < (a.Width / 2 + b.Width / 2) &&
Math.Abs(ay + a.Height / 2 - by - b.Height / 2) < (a.Height / 2 + b.Height / 2))
{
Color[] adata = new Color[a.Width * a.Height];
a.GetData(adata);
Color[] bdata = new Color[b.Width * b.Height];
b.GetData(bdata);
int top = Math.Max(ay, by);
int bottom = Math.Min(ay + a.Height, by + b.Height);
int left = Math.Max(ax, bx);
int right = Math.Min(ax + a.Width, bx + b.Width);
for (int i = top; i < bottom; i++)
{
for (int j = left; j < right; j++)
{
Color colora = adata[(j - ax) + (i - ay) * a.Width];
Color colorb = bdata[(j - bx) + (i - by) * b.Width];
if (colora.A != 0 && colorb.A != 0)
return 1;
}
}
}
return 0;
}
这个函数要六个参数,分别是两个精灵,及它们的坐标。它适合检测不旋转的精灵之间是否有碰撞。而旋转碰撞函数我正在学习中,因为对Xna的矩阵函数及对矩阵本身还不是很熟悉,所以旋转碰撞函数很难学,而我却有了自己的旋转碰撞想法了,不过还是等代码写出来了再介绍吧。如果写不出,又只好弄懂后“偷”用别人的代码了。
4、粒子碰撞机~
看名字好夸张,其实它的作用不是特别大,不过这周最令我高兴的事就是写了它的代码了。它具体的作用就是检验上面的像数碰撞函数。因为运用上面碰撞函数最令我头痛的事就是所得到的碰撞检测边界与想得到的不符。而在不知道其实际边界是什么的情况下,很难查出是哪里代码出问题了。所以就写了这个“粒子碰撞机”的代码,它就是帮我绘出精灵的实体(碰撞)轮廓。
写碰撞机的具体思路是,首先,有很多面积很小的精灵(碰撞机里的精灵是用红色的一个像素点,用电脑配有的画图工具画的)与所检精灵碰撞,然后可以得出被检测精灵调用像素碰撞函数后的碰撞边界是什么样的。碰撞机的具体代码:
const int dotnum = 2800;
int[] dotmovebool = new int[dotnum ];
Texture2D[] dots = new Texture2D[dotnum];
int[] dotx = new int[dotnum];
int[] doty = new int[dotnum];
定义2800个点,因为Xna窗口是600*800的,所以就是要用600个点排成直线从窗口左边界向右移,用600个点排成直线从窗口右边界向左移,用800个点从窗口上界向下移,最后800个点从窗口下界向上移。这样就可以确保从各方位“进攻”!具体代码:
for (int i = 0; i < 600; i++)
{
dotx[i] = 0;
doty[i] = i;
}
for (int i = 0; i < 600; i++)
{
dotx[600 + i] = 800;
doty[600+i] = i;
}……
这是定义点的起始位置,其它点的定义类似。
for (int n = 0; n < dotnum; n++)
{
if (n < 600 && dotmovebool[n] == 1)
dotx[n]++;
}
dotmovebool是用来检测粒子是否发生碰撞的。如果发生碰撞后粒子将会不再移动,这样就能“绘出”被检测物体“实体”(非透明像素)的边界了。
拜它所赐,我迅速地查出了函数里的故障并排除了。
5、血与能量的代码。
进攻时能量的退减,不动时(不按Up键时)能量的积蓄的函数还没写好,原因就是旋转碰撞函数还没写好。不过被攻击时血的退减与能量的变动函数写好了,它们并不是突然一下子能量条或血条就变短了,而是像素单位式地变长或变短,这样可以增加视觉效果。代码如下:
if (bloodlength/3 > lifevalue)
bloodlength--;
if (energylength / 4 <= energyvalue)
energylength++;
if (energylength == 1384)
twinklebool = 1;
if (twinklebool == 1)
twinkle();
其中lifevalue与energyvalue记录血条与能量条的“应该”长度,而bloodlength与energylength实际地控制着血条与能量条的长度。twinkle函数是用来提示玩家能量已经充满了的,当能量充满时,能量条会“闪烁”几次。
制作心得:
以前总是认为美工太难而想通过代码来实现某些“美工”,但是却因为用代码更麻烦最后不了了之。这周的学习,我发现美工并不难,或者说尽管它有点难,不过“美工”后的收获不小。我的美工不只是要实现我的游戏里的内容,同时还表达我的审美观,这次的背景自我感觉就完成的不错。不过这次的背景制作的方式是偶然的。除了上面实现了的效果外,其实还有很多效果没写好代码,或者没“美工(V)”好,本来想一口气一周内写好一个可玩的游戏的,不过实在是“功课重重”,没办法了。只好再拖一周了。
/Files/sysudm/MachineTime1.rar
浙公网安备 33010602011771号