Scale Box2D圆形及其碰撞检测?

时间:2016-02-17 09:51:22

标签: box2d collision-detection game-physics

如果一个圆圈在发射阶段伸展,并变得拉长(获得高度和失去宽度)如何动态调整碰撞形状(在Box2D中),因为它的缩放?这可以通过所有步骤的每一帧完全拉伸并回到圆形吗?

同样地,当弹跳时,它会挤出一点,并需要检测更多的宽度和更少的高度。

enter image description here

想象一下,这是一个游戏中的英雄角色,他有点跳投,甚至可以做半空跳'。但大多数伸长和压扁都发生在地面相互作用过程中。像这样:

enter image description here

1 个答案:

答案 0 :(得分:2)

我们有两个问题:

  1. 动态更改灯具形状

    Box2D支持各种类型的形状,如:

    • CircleShape (简单)
    • PolygonShape (简单)
    • ChainShape EdgeShape (复杂)

    这里的情况取决于您需要的形状类型。如果“简单”类型的形状足够,那么解决方案非常简单,并且(对于PolygonShape)类似

    //! I'm using LibGDX in code examples but you can translate it to other engines
    
    Body body;
    
    ...
    
    //assuming that body has one fixture - if not just keep it's reference and call it directly
    ((PolygonShape) body.getFixtureList().first().getShape() ).setAsBox(hx, hy);
    

    请注意,对于CircleShape,您甚至不需要投放它,因为Shape类包含setRadius方法

    如果您需要“复杂”的形状,情况会更复杂,但仍然很容易。处理它的方法是销毁当前夹具并使用其他形状重新创建,如

    Body body;
    FixtureDef fixtureDef;
    
    ...
    
    //creating fixturDef - for further usage (setting friction, density etc...)
    
    ...
    
    //again assuming that body has one fixture - but also can keep reference
    body.destroyFixture( bb1.getFixtureList().first() );
    
    ChainShape shape = new ChainShape();
    //creating shape...
    
    fixtureDef.shape = shape;
    
    body.createFixture(fixtureDef);
    
    //you must dispose shape after fixture creating!
    shape.dispose();
    
  2. 创建椭圆

    虽然Box2D支持各种形状没有椭圆类型,你需要使用一些技术来自己生成它。在互联网上有很多方法描述 - 我将向您展示如何生成椭圆作为ChainShape实例。

    我将parametric equation for ellipse0 <= t < 2π一起使用(以弧度表示)。该方法如下:

    //overriding method to handle default STEPS value
    ChainShape createEllipse(float width, float height)
    {
        return createEllipse(width, height, 64);
    }
    
    ChainShape createEllipse(float width, float height, int STEPS)
    {
        ChainShape ellipse = new ChainShape();      
        Vector2[] verts = new Vector2[STEPS];
    
        for(int i = 0; i < STEPS; i++)
        {
            float t = (float)(i*2*Math.PI)/STEPS;
            verts[i] = new Vector2(width * (float)Math.cos(t), height * (float)Math.sin(t));
        }
    
        ellipse.createLoop(verts);
        return ellipse;
    } 
    

    其中:

    • width - 椭圆的宽度
    • height - 椭圆的高度
    • STEPS - 创建椭圆的点数(较大的STEPS =更平滑的椭圆,但生成成本更高)

    总而言之 - 假设您在模拟过程中知道身体的大小(因为如果您不知道它比这个问题更大的主题),那么情景就是:

    • 检查我是否应该更换灯具(是否更改了车身尺寸?
    • 如果
      • 破坏旧夹具
      • 生成新尺寸的新形状
      • 创建新夹具
      • 处理创建的形状

    在代码中它将类似于:

    //in render method
    if( bodySizeChanged() ) //this method is checking somehow if you should change fixture
    {
        //again assuming that body has one fixture - but also can keep reference           
        body.destroyFixture( body.getFixtureList().first() );
    
        ChainShape ellipse = createEllipse(newWidth, newHeight);
        fixtureDef.shape = ellipse;
    
        body.createFixture(fixtureDef);
        ellipse.dispose();
    }
    

    当然每次创建新形状效率不高,更好的方法是在加载时创建一些形状Array<ChainShape>甚至FixtureDef的集合(例如在show()方法中)和只需在渲染方法中获得所需的 shape / fixtureDef

  3. 编辑 - 例如,如果您有动画精灵集合,则可以为每个精灵生成另一个形状集合 - 但仅限于之前的动画帧具有其他尺寸不要将形状改变为相同的形状 - 它会是这样的:

        Sprite[] frames = new Sprite[N]; //N sprites for animation
        ChainShape[] shapes = new ChainShape[N]; //N shapes
    
        ...
    
        Vector2 lastSize = new Vector2(0,0); //or another initial value - depends on what sizes your sprites have
    
        for(int i = 0; i < frames.length; i++)
        {
            Sprite s = frames[i];
    
            if(lastSize.x != s.getWidth() || lastSize.y != s.getHeight())
            {
                shapes[i] = createEllipse(s.getWidth(), s.getHeight());
                lastSize.set(s.getWidth(), s.getHeight());
            }
            else
            {
                shapes[i] = null;
            }
        }
    

    然后在渲染方法

        //render method
        int index = getCurrentAnimationFrameIndex(animation); //some function to get index of current sprite of character
    
        if(shapes[index] != null)
            //change fixture
    

    当然,您应该记得在应用程序结束时处理所有非空形状(至少如果它是Libgdx)!

    我在这里发现的一件有趣的事情是,ChainShape 在碰撞过程中不会旋转对我来说有点奇怪。实际上它可能对你有好处(我猜球是某​​种角色而且它不应该有旋转 - 也许你甚至会在它的身体上设置一些setFixedRotation但是如果你想让身体旋转这样做的方法是定义MassData,将 I (惯性)设置为某个值,然后将此MassData附加到正文

        MassData m = new MassData();
    
        m.center.set(0, 0);
        m.I = 100; //example value
        body.setMassData(m); 
    

    不幸的是,我不能告诉你应该在那里设置什么值,但你可以在Wikipedia site上阅读更多内容。