5

我第一次在我正在开发的中型 Flash 游戏中认真使用 Box2D。我目前对 Box2D 的体验仅限于创建一个世界、身体并以功能性的方式将这些身体添加到世界中。

我发现将 Box2D 集成到我的游戏环境中很容易,维护编写良好的代码并完成了一些处理碰撞的教程。我现在面临的问题是我的游戏将有许多实体,每个实体都以不同的方式与其他实体交互,而且我发现很难编写自己的b2ContactListener子类而不让它变得非常混乱。

根据我使用的教程,我创建了自己的子类并添加了该方法b2ContactListener的覆盖。BeginContact()调用时接收的参数BeginContact()将引用 的一个实例b2Contact,通过它我可以访问两个b2Fixture实例(两个发生碰撞的实例)。然后我可以访问与这些sb2Body中的每一个关联的实例。b2Fixture

  • 问题:目前我有一个迂回的方法来找出两个东西相撞(即它们是墙和导弹,还是玩家和树等),它使用GetUserData()并看起来像这样作为示例:

    var f1Player:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Player
    var f2Player:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Player
    var f1Tree:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Tree
    var f2Tree:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Tree
    // ... continutes with all possible combinations.
    
    
    // Example of managing a collision:
    if(f1Player && f2Tree)
    {
        // Player (FixtureA) and Tree (FixtureB)
    }
    
    if(f2Player && f1Tree)
    {
        // Player (FixtureB) and Tree (FixtureA)
    }
    

    如您所见,这最终会变得非常漫长且难以管理。我还必须编写每组操作以执行两次,以适应某个元素是 FixtureA 或 FixtureB,反之亦然(显然是以交换参数的函数调用的形式,而不是从字面上重写)。

这显然不是正确的方法,但我无法找到更彻底地解释碰撞检测管理的资源。

有没有人可以分享使用 Box2D 进行碰撞检测管理的经验?另外,是否使用SetUserData( entityThatOwnsTheBody );正确的方法来使用该方法?

4

3 回答 3

2

是的,确实有点麻烦。实际上,我认为您拥有它的方式很典型。

fwiw Box2D 本身在测试夹具是否重叠时必须处理类似的问题。有很多函数,例如 b2CollideCircles、b2CollidePolygonAndCircle、b2CollidePolygons 等,当两个灯具彼此靠近时,引擎会选择应该使用其中的哪个函数。

它通过将函数指针放在一个二维数组中来做到这一点,然后通过使用两种形状类型作为索引在这个数组中查找适当的函数。有关详细信息,请参阅 b2Contact.cpp 中的前三个函数。

当然,如果你不能在 AS3 中传递这样的函数引用,那么我想这个答案并没有多大帮助,但我想我还是会发布,因为 C/C++/JS 用户可能会来。

于 2012-10-03T21:03:26.930 回答
1

我使用c++了 的版本Box2d,但我认为同样的方法也适用于 actionscript。我创建了一个类Object,其中包含一个b2Body *_body指针和一个指向图形表示的指针。_body'sUserData被设置为指向Object *. 类Object有以下方法:

virtual bool acceptsContacts    ();
virtual void onContactBegin     (const ContactData &data);
virtual void onContactEnded     (const ContactData &data);
virtual void onContactPreSolve  (const ContactData &data);
virtual void onContactPostSolve (const ContactData &data);

当在b2ContactListener子类中检测到碰撞时,它会检查碰撞的物体是否有用户数据。如果是这样,它会将他们的用户数据投射到Object*并且如果任何碰撞的对象接受了联系人 - 它创建ContactData(一个包含有关碰撞的所有必需信息的类)并将其放入它的内部list以便稍后交付。

b2World::update方法返回时,ContactListener将所有联系信息传递给对象进行处理。交付被延迟,以便您可以在处理碰撞时创建新的身体、关节等(执行更新时不允许这样做)

此外,如果在碰撞处理期间删除了一个碰撞体,您必须通知ContactListener(只需在其中放置一个指针),因此它可以使适当的联系人无效而不是传递它们ContactData

于 2012-10-03T08:59:45.190 回答
1

我想出了比原来更好的东西。

首先,我只是让我的Being班级(拥有 a b2Body)将自己设置为它的身体UserData。此类还将包含一个onContact()方法,类似于以下内容:

public class Being
{

    private var _body:b2Body;


    public function Being()
    {
        // Define the body here.
        // ...

        _body.SetUserData(this);
    }


    public function onCollision(being:Being = null):void
    {
        //
    }

}

然后在我自己的b2ContactListener实现中,我只是将碰撞Being(或 null,如果没有Being分配给碰撞b2Body的 's UserData)传递给对方Being的 's onCollision()

override public function BeginContact(contact:b2Contact):void
{
    var bodyA:b2Body = contact.GetFixtureA().GetBody();
    var bodyB:b2Body = contact.GetFixtureB().GetBody();

    var beingA:Being = bodyA.GetUserData() as Being || null;
    var beingB:Being = bodyB.GetUserData() as Being || null;

    beingA && beingA.onCollision(beingB);
    beingB && beingB.onCollision(beingA);
}

最后,在我的每个子类中,我可以轻松地为特定类型的Being其他 s 之间的冲突准备逻辑:Being

class Zombie extends Being
{
    override public function onCollision(being:Being = null):void
    {
        if(being && being is Bullet)
        {
            // Damage this Zombie and remove the bullet.
            // ...
        }
    }
}
于 2012-10-11T09:58:10.937 回答