如何以干净,易于管理的方式跟踪我的所有Box2D碰撞?

时间:2012-10-03 07:31:24

标签: actionscript-3 flash box2d collision-detection

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

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

根据我使用的教程,我创建了自己的b2ContactListener子类,并添加了BeginContact()方法的覆盖。 BeginContact()在调用时收到的参数将引用b2Contact的实例,通过该实例,我可以访问两个b2Fixture实例(两个已发生冲突的实例)。然后,我可以访问与每个b2Body相关联的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 );正确使用该方法的方法是什么?

3 个答案:

答案 0 :(得分:2)

是的,确实有点令人讨厌。实际上我认为你拥有它的方式非常典型。

fwiw Box2D在测试灯具是否重叠时必须处理类似的问题。有一堆功能,如b2CollideCircles,b2CollidePolygonAndCircle,b2CollidePolygons等,当两个灯具相互靠近时,引擎会选择应该使用哪些功能。

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

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

答案 1 :(得分:1)

我使用了c++Box2d,但我认为同样的方法可以在actionscript中使用。我创建了一个类Object,它包含一个b2Body *_body指针和一个指向图形表示的指针。 _body的{​​{1}}被设置为指向UserData。 class 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*(一个包含有关冲突的所有必需信息的类)并将其放入其内部{{1稍后交付。

返回ContactData方法后,list会将所有联系信息提供给要处理的对象。交付延迟,以便您可以在处理碰撞时(在执行更新时不允许)创建新的实体,关节等等。

此外,如果在碰撞处理过程中删除了其中一个碰撞的主体,您必须通知b2World::update(只需在ContactListener内放一个指针),这样就可以使相应的联系人无效而不会传递它们

答案 2 :(得分:1)

我提出了比原作更好的东西。

首先,我只是让我的Being课程(拥有b2Body)将自己设置为其身体&#39; 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(如果没有Being分配给碰撞b2Body&#39;则为空。 s UserData)反对Being&#39; 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的每个子类中,我可以轻松地为某种类型的其他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.
            // ...
        }
    }
}