[翻译]XNA外文博客文章精选之nine


PS:自己翻译的,转载请著明出处

                                                                        与Platforms一起玩
                                  我恨碰撞检测教程。他们全都是相同的。他们解释的错综复杂,详细介绍两个对象如何发生了碰撞,但总是留给你来做些什么。我真的不关心马利奥的边框合是否改变了颜色,这不会去保持它站在地面上而是不下落。
                                  这不是一个碰撞检测的文章。它是一个碰撞响应的文章。这里我使马利奥站在地面上,当它撞到墙了就停止,在它向天花版跳跃时,让它碰撞到头。
                                  尽管这种文章很缺乏,它实际上非常简单去区分两个边框盒子,一旦你发现它们已经碰撞了。我将创建一个小的-platformer去证明它。如果你没有创建一个sideScroller,请不要烦恼,从代码中删除重力的部分,并且碰撞检测和响应只是对其他类型有关。
                                  获取样本在我们开始之前,因为我不会粘贴功能代码的巨大部分。WASD移动的玩家(非常不现实)。

Getting Started
                                  一个bounding盒正是听起来那个样子。这个盒子它包围一个对象去定义它的边界。这里定义了Min点(左上角)和Max点(右下角)。

1 (0,0- Min
2  #########
3  #########
4  #########
5  #########
6  ######### (3232- Max

                                 现在你知道什么是bouding盒子了吧,是时候去谈下某些缺点了:Bounding盒子不是很完美的对象表示。它们通常太大或者太小去完美的封装一个对象。
            你有两个操作:
            1.接受这个对象将会被标记为碰撞,这一点都不清楚。
            2.减少bounding盒子的大小并且让你的精灵的一些象素被认为是"player leeway".
                                 第二是个不错的方法,因为它总是很好的裁减一些松懈的玩家,然后当一个透明的象素碰到另一个透明的象素时它们就会死。

Collision Detection and Response(碰撞检测和响应)
                                 那么到底如何,我们如何区分两个bounding盒呢?第一步是找出他们实际上是否碰撞(我说谎,它不考虑碰撞检测)。第二部分是计算最少的数量,为了解决这个碰撞我们需要去取代一个对象。这是被称为minimum translation检测或者简称MTD。
                                 这看起来很难去理解,但是它摆在这里:

 1 // Thank you Microsoft for intellisense with a name like that. ;)
 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向量:

1 Result = (0-1 * 32) or (0-32) [The -1 is taken from our "side" variable]

                                 简单的转换(移动)A的bounding盒子通过(0,-32)并且你会看见,碰撞已经被解决了。
                                 一点都不难,对吗?

Wrapping Up(打包)
                                现在我们知道如何去检查碰撞和(更重要的)解决它们,你已经生成了一个platformer。
                                这个样例添加两个类,Entity和World。Entity接收一个bounding盒子和添加物理信息到它里面。World是造成这些实体的物理控制的原因。唯一的不平凡的代码是World.Update方法:

 1 public void Update(float deltaTime, float totalTime)
 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 }
                               通过每一个实体简单的循环,添加重力,为碰撞移动并检查他们。这里你可以看见我们使用(非常详细)CalculateMinimumTranslationDistance()方法为这个第一次。我们支持向量,它计算这个Entity的OnCollision()方法,这个方法决定如何处理它。
                               希望本文可以帮你解决你的碰撞响应痛苦,使你感觉容易点。请随时反馈,让我不断的更新,你可以对代码进行任何的更新。
源代码:http://www.ziggyware.com/readarticle.php?article_id=134
(完)

posted on 2009-09-08 15:43  一盘散沙  阅读(464)  评论(0)    收藏  举报

导航