PS:自己翻译的,转载请著明出处
与Platforms一起玩
我恨碰撞检测教程。他们全都是相同的。他们解释的错综复杂,详细介绍两个对象如何发生了碰撞,但总是留给你来做些什么。我真的不关心马利奥的边框合是否改变了颜色,这不会去保持它站在地面上而是不下落。
这不是一个碰撞检测的文章。它是一个碰撞响应的文章。这里我使马利奥站在地面上,当它撞到墙了就停止,在它向天花版跳跃时,让它碰撞到头。
尽管这种文章很缺乏,它实际上非常简单去区分两个边框盒子,一旦你发现它们已经碰撞了。我将创建一个小的-platformer去证明它。如果你没有创建一个sideScroller,请不要烦恼,从代码中删除重力的部分,并且碰撞检测和响应只是对其他类型有关。
获取样本在我们开始之前,因为我不会粘贴功能代码的巨大部分。WASD移动的玩家(非常不现实)。
Getting Started
一个bounding盒正是听起来那个样子。这个盒子它包围一个对象去定义它的边界。这里定义了Min点(左上角)和Max点(右下角)。
2 #########
3 #########
4 #########
5 #########
6 ######### (32, 32) - Max
现在你知道什么是bouding盒子了吧,是时候去谈下某些缺点了:Bounding盒子不是很完美的对象表示。它们通常太大或者太小去完美的封装一个对象。
你有两个操作:
1.接受这个对象将会被标记为碰撞,这一点都不清楚。
2.减少bounding盒子的大小并且让你的精灵的一些象素被认为是"player leeway".
第二是个不错的方法,因为它总是很好的裁减一些松懈的玩家,然后当一个透明的象素碰到另一个透明的象素时它们就会死。
Collision Detection and Response(碰撞检测和响应)
那么到底如何,我们如何区分两个bounding盒呢?第一步是找出他们实际上是否碰撞(我说谎,它不考虑碰撞检测)。第二部分是计算最少的数量,为了解决这个碰撞我们需要去取代一个对象。这是被称为minimum translation检测或者简称MTD。
这看起来很难去理解,但是它摆在这里:
2 public static Vector2 CalculateMinimumTranslationDistance(BoundingBox left, BoundingBox right)
3 {
4 // Our displacement result vector containing the translation (movement) information
5 // that resolves our intersection.
6 Vector2 result = Vector2.Zero;
7 // This is re-used to calculate the difference in distance between sides.
8 float difference = 0.0f;
9 // This stores the absolute minimum distance we'll need to separate our colliding object.
10 float minimumTranslationDistance = 0.0f;
11 // Axis stores the value of X or Y. X = 0, Y = 1.
12 // Side stores the value of left (-1) or right (+1).
13 // They're used later in calculating the result vector.
14 int axis = 0, side = 0;
15 // Left
16 difference = left.Max.X - right.Min.X;
17 if (difference < 0.0f)
18 {
19 return Vector2.Zero;
20 }
21 {
22 // These braces are superfluous but should make it more
23 //clear that they're similiar to the if statements below.
24 minimumTranslationDistance = difference;
25 axis = 0;
26 side = -1;
27 }
28 // Right
29 difference = right.Max.X - left.Min.X;
30 if (difference < 0.0f)
31 {
32 return Vector2.Zero;
33 }
34 if (difference < minimumTranslationDistance)
35 {
36 minimumTranslationDistance = difference;
37 axis = 0;
38 side = 1;
39 }
40 // Down
41 difference = left.Max.Y - right.Min.Y;
42 if (difference < 0.0f)
43 {
44 return Vector2.Zero;
45 }
46 if (difference < minimumTranslationDistance)
47 {
48 minimumTranslationDistance = difference;
49 axis = 1;
50 side = -1;
51 }
52 // Up
53 difference = right.Max.Y - left.Min.Y;
54 if (difference < 0.0f)
55 {
56 return Vector2.Zero;
57 }
58 if (difference < minimumTranslationDistance)
59 {
60 minimumTranslationDistance = difference;
61 axis = 1;
62 side = 1;
63 }
64 // Intersection occurred:
65 if (axis == 1) // Y Axis
66 result.Y = (float)side * minimumTranslationDistance;
67 else // X Axis
68 result.X = (float)side * minimumTranslationDistance;
69 return result;
70 }
我们计算在一个bounding盒子的边靠着的另外bounding盒子的对边的距离。如果这个距离比0小,我们知道,这里决不会有盒子的互相接触。如果它是大于等于0,我们继续我们的测试。我们同样保存很小的距离作为我们的最终的MTD来使用。
为了帮助准确的理解继续会发生什么,仔细看下两个盒子的图象。数字是它们的Min和Max位置。让我们从我们方法的If声明中用A的位置取代"左",用B的位置取代"右"。
Check #1: difference = 64 - 32 = 32
- Result: difference > 0, 有一个碰撞的可能,让我继续检查
Check #2: difference = 96 - 0 = 96
- Result: difference > 0, 有一个碰撞的可能,让我继续检查
Check #3: difference = 64 - 32 = 32
- Result: difference > 0, 有一个碰撞的可能,让我继续检查
Check #4: difference = 96 - 0 = 96
- Result: difference > 0, 如果我们仍在这里,那么有个明确的碰撞。
现在我们知道了他们碰撞了,我们计算我们的MTD向量:
简单的转换(移动)A的bounding盒子通过(0,-32)并且你会看见,碰撞已经被解决了。
一点都不难,对吗?
Wrapping Up(打包)
现在我们知道如何去检查碰撞和(更重要的)解决它们,你已经生成了一个platformer。
这个样例添加两个类,Entity和World。Entity接收一个bounding盒子和添加物理信息到它里面。World是造成这些实体的物理控制的原因。唯一的不平凡的代码是World.Update方法:
2 {
3 for (int i = 0; i < entities.Count; ++i)
4 {
5 // Add some gravity to their velocity.
6 entities[i].Velocity += gravity * entities[i].Weight;
7 // Move 'em.
8 entities[i].Move(entities[i].Velocity * deltaTime);
9 // Check for collisions.
10 for (int j = 0; j < entities.Count; ++j)
11 {
12 if (entities[i] == entities[j])
13 continue;
14 Vector2 displacement = BoundingBox.CalculateMinimumTranslationDistance(entities[i].BoundingBox, entities[j].BoundingBox);
15 // Tell the entity that it's colliding with an object.
16 if (displacement != Vector2.Zero)
17 {
18 entities[i].OnCollision(entities[j], displacement);
19 }
20 }
21 }
22 }
希望本文可以帮你解决你的碰撞响应痛苦,使你感觉容易点。请随时反馈,让我不断的更新,你可以对代码进行任何的更新。
源代码:http://www.ziggyware.com/readarticle.php?article_id=134
(完)