Make a Vortex in Box2D

后端 未结 2 1341
灰色年华
灰色年华 2021-01-07 02:44

I\'m trying to make a spiral vortex in Box2D on C++ / Objective C by applying forces. What I would like to realize is a vortex that pushes the bodies from a point, or that

相关标签:
2条回答
  • 2021-01-07 02:52

    I once did a rotating platform by making a circular sensor which applied a tangential speed change to the body given by a circular vector field. The circular vector field which I used was this:

    V = (y, -x)
    

    A visual representation of this vector field can be found here: http://kasadkad.files.wordpress.com/2009/09/derham1.png?w=450&h=427

    The y and x are the relative position of the body to the centre of the sensor, so you can do something like this:

    Vector getTangentVector(Vector relativePosition, bool invert)
    {
        Vector vec;
        if(invert) //if it's cw or ccw
        {
            vec.setY(relativePosition.x());
            vec.setX(-relativePosition.y());
        }
        else
        {
            vec.setY(-relativePosition.x());
            vec.setX(relativePosition.y());
        }
        return vec;
    }
    

    Then on the update method of my program I do something like this:

    for (b2ContactEdge* ce = platformBody->GetContactList(); ce; ce = ce->next)
    {
    
        b2Contact* c = ce->contact;
    
        if(c->IsTouching())
        {
            const b2Body* bodyA = c->GetFixtureA()->GetBody();
            const b2Body* bodyB = c->GetFixtureB()->GetBody();
    
            const b2Body* targetBody = (bodyA == platformBody)?bodyB:bodyA;
    
            Vector speed = getTangentImpulse(
                              getRelativePosition(platformBody, targetBody),
                              true);
    
    
            speed *= CONSTANT; // CONSTANT = 1.8, 
                               // this is to account for the targetBody attrition, 
                               // so it doesn't slip on the platform
    
            Vector currentSpeed;
            currentSpeed.setX(targetBody->GetLinearVelocity().x);
            currentSpeed.setY(targetBody->GetLinearVelocity().y);
    
    
    
            Vector diff = speed - currentSpeed;
            diff *= 0.01; //should depend on time, but this worked nicely. It makes the
                          //body change its linear velocity to be the same as "speed" at a 
                          //0.01 change rate.
    
            currentSpeed += diff;
            targetBody->SetLinearVelocity(
                                   b2Vec2(currentSpeed.x(), 
                                   currentSpeed.y()));
    
        }
    
    }
    

    There are a lot of workarounds in this solution, for example, I don't use impulses and manually change the speed, it worked better for me. Also, I use a constant to account for attrition.

    Still it produced the effect that I needed so I hope it works for you. For the vortex I guess you just need to connect a joint, like a mouse joint, attached to the centre and grabbing the body. If you use just the joint, it will be hard to make it strong enough to grab the body, but weak enough to make it rotate around the centre. Together with my code it might be easier to achieve this just by playing around with the constants.

    I hope this helps.

    edit: I just remembered that, with the platform code you also ensure the direction of the rotation, which is not possible with just the joint.

    0 讨论(0)
  • 2021-01-07 03:11

    I created a series of rotating asteroids around a central point by using a distance joint to each asteroid and then applying tangential force to make them "swirl" around the center.

    You could replace the distance joint with an actual force pointed towards the center of the vortex. You could also slowly decrease the distance in the distance joint to have the items "spiral" in. I'm going to go with this as it gives you more control over the speed at which items move into the vortex.

    I'm calling "myBodyA" the body that is the center of the vortex.

    From the Box2d Manual:

    b2DistanceJointDef jointDef;
    jointDef.Initialize(myBodyA, myBodyB, worldAnchorOnBodyA, worldAnchorOnBodyB); jointDef.collideConnected = true;
    world->CreateJoint(&jointDef);
    

    You will have to apply some force to your body (let's say it was myBodyB) to get it moving in the right direction so it rotates around myBodyA. This is going to require some math.

    What you want to do is calculate the vector that points perpendicular to the vector pointing from myBodyB to myBodyA. You can do this by finding the vector from myBodyB to myBodyA, normalizing it, taking the skew of it (rotate by PI/2), and then using it as a force direction. Something like:

    // Calculate Tangent Vector
    b2Vec2 radius = myBodyB.GetPosition()-myBodyA.GetPosition();
    b2Vec2 tangent = radius.Skew();
    tangent.Normalize();
    
    // Apply some force along tangent
    myBodyB.ApplyForceToCenter(body->GetMass()*acceleration*vTangent);
    

    If you read this correctly, you can see F = m * a * tangentVector; that is to say, you are applying force in the direction of the tangent. I believe it will rotate it clockwise (if I did the math right). Regardless, a negative on the force will move it in the opposite direction.

    To stop the body rotating, you can use the SetLinearDamping(dampingValue) on it. So as long as you apply force, it will continue to rotate. If you stop applying force, it should gently stop. You can control the speed up/slow down by the acceleration value and the dampingValue parameters.

    If you really want good control over the speed, I believe you can do this to clamp the speed:

    b2Vec2 linVel = myBodyB.GetLinearVelocity();
    linVel.Normalize();
    linVel *= maxSpeed;
    myBodyB.SetLinearVelocity(linVel);
    

    Was this helpful?

    0 讨论(0)
提交回复
热议问题