Windows Phone 7范例游戏Platformer实战4——冲突检测的实现
本小节我们就开始真正的XNA游戏编程实践了。前面三节解决了游戏的规则和大部分游戏对象,接下来轩辕对游戏中需要的一些基础类进行讲解和逐步实现。当遇到XNA游戏开发的一些专有名词时,我也会穿插着讲解。
正所谓画龙画虎难画骨,在游戏开发中,最难的不是场景的画面实现,而是一些底层的游戏算法或者基础类。只有将地基夯实了,我们才能在这个基础上构建出高楼大厦。同样的道理,我们也需要将Platformer游戏需要的基础类先实现了,才能在以后的开发中构造出需要的效果。
何谓Platformer的基础类呢,比如说冲突检测、动画实现、加速度传感器状态读取、触摸屏输入等等都是需要我们事先考虑的。毕竟所有的游戏场景、英雄和僵尸怪的移动都是构建于这些类之上的。
在Platformer场景中,程序无时无刻不在对冲突进行检测和判定。比如说当英雄落下时,需要和踏脚石进行冲突检测,以便确定是继续下落还是停留在踏脚石上。英雄还需要和僵尸怪进行冲突检测,如果发生了碰撞,那么很不幸,关卡立马终止。此外,英雄还需要和宝石进行冲突检测,以便确认是否可以将宝石收入囊中。
下图就是将发生碰撞的临界状态:
目前2D或3D场景中的冲突检测存在成百上千种实现和描述,所谓的冲突检测大体的描述就是游戏中两个精灵图元是否存在相交周末一个判定过程。现在游戏中常用的冲突检测方法就是使用边界,顾名思义,就是使用一个特定的、可用数学描述的形状将精灵图元进行大概的轮廓确认。
比如下面这个飞机图片,它本身是一个不规则的图元,我们需要在子弹碰到飞机的任何一个部位发生爆炸,那么我们可以将飞机用两个矩形进行囊括。虽然这样没有精确到飞机本身的每个像素,但是大体来说还是可以实现我们需要的效果。
同样在Platformer游戏中,我们也是采用类似的方法实现各种可视对象的边界,比如说英雄、踏脚石、僵尸怪,都是使用一个刚好包含自身图元的矩形来作为其边界。但是宝石例外,它的边界形状是一个圆。下图将这些对象的边界形状用圆形和矩形进行了描绘,让你一目了然。
就那英雄和踏脚石的碰撞来说,我们可以转换为两个矩形的是否相交。这个判定需要一些简单的数学知识,希望你还没有还给老师。下面是两个矩形是否相交的示意图,以便你可以更加清楚地理解这个判定过程。
我们可以选取两个矩形的中心点centerA和centerB,而后用centerA.X - centerB.X和centerA.Y - centerB.Y即可得到这两个矩形在X和Y坐标上的距离。如果两个矩形的中心点在X和Y坐标上的距离大于等于各自坐标上的“半径”相加距离的话,那么很显然这两个矩形还未相交,反之则是发生了碰撞。
由此可得两个矩形是否冲突的代码如下:
2 /// 矩形扩展类
3 /// </summary>
4 public static class RectangleExtensions
5 {
6 /// <summary>
7 /// 计算两个矩形见的相交深度
8 /// </summary>
9 /// </returns>
10 public static Vector2 GetIntersectionDepth(this Rectangle rectA, Rectangle rectB)
11 {
12 // 计算两个矩形的“半径”大小
13 float halfWidthA = rectA.Width / 2.0f;
14 float halfHeightA = rectA.Height / 2.0f;
15 float halfWidthB = rectB.Width / 2.0f;
16 float halfHeightB = rectB.Height / 2.0f;
17
18 // 计算矩形的中心点
19 Vector2 centerA = new Vector2(rectA.Left + halfWidthA, rectA.Top + halfHeightA);
20 Vector2 centerB = new Vector2(rectB.Left + halfWidthB, rectB.Top + halfHeightB);
21
22 // 计算两个矩形当前在X和Y轴上的的中心点距离,以及未相交时的X和Y轴的最小距离
23 float distanceX = centerA.X - centerB.X;
24 float distanceY = centerA.Y - centerB.Y;
25 float minDistanceX = halfWidthA + halfWidthB;
26 float minDistanceY = halfHeightA + halfHeightB;
27
28 // 如果没有相交, 返回 (0, 0).
29 if (Math.Abs(distanceX) >= minDistanceX || Math.Abs(distanceY) >= minDistanceY)
30 return Vector2.Zero;
31
32 // 计算相交的深度
33 float depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
34 float depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
35 return new Vector2(depthX, depthY);
36 }
37
38 /// <summary>
39 /// 获得矩形下边缘中心点的坐标
40 /// </summary>
41 public static Vector2 GetBottomCenter(this Rectangle rect)
42 {
43 return new Vector2(rect.X + rect.Width / 2.0f, rect.Bottom);
44 }
45 }
至于宝石和英雄的碰撞检测,可以抽象为一个矩形和圆是否相交的过程。这里轩辕不再详细阐述了,大家可以多思考下,下面是碰撞判定的代码,写是极其精湛。
2 /// 2D圆边界
3 /// </summary>
4 struct Circle
5 {
6 /// <summary>
7 /// 圆的中点
8 /// </summary>
9 public Vector2 Center;
10
11 /// <summary>
12 /// 圆的半径
13 /// </summary>
14 public float Radius;
15
16 /// <summary>
17 /// 构造一个新的圆
18 /// </summary>
19 public Circle(Vector2 position, float radius)
20 {
21 Center = position;
22 Radius = radius;
23 }
24
25 /// <summary>
26 /// 确定圆是否和矩形相交
27 /// </summary>
28 /// <returns>相交返回true,反之返回false.</returns>
29 public bool Intersects(Rectangle rectangle)
30 {
31 Vector2 v = new Vector2(MathHelper.Clamp(Center.X, rectangle.Left, rectangle.Right),
32 MathHelper.Clamp(Center.Y, rectangle.Top, rectangle.Bottom));
33
34 Vector2 direction = Center - v;
35 float distanceSquared = direction.LengthSquared();
36
37 return ((distanceSquared > 0) && (distanceSquared < Radius * Radius));
38 }
39 }
冲突检测完成之后,轩辕将对WP7中的加速度感应器和触控操作两大新概念的编程进行探索,敬请期待。