1
\$\begingroup\$

I would like to implement something that behaves much like a distance joint / revolute joint in Box2D, however I do not want to throw a whole physics engine at this problem. I'm curious where I might start research to implementing such a thing.

Currently I only know them by their Box2D names, so googling that leads of course back to Box2D, so even that would be helpful.

\$\endgroup\$
  • 1
    \$\begingroup\$ You might want to consider just using box2D for now an evaluate how much of an issue this really is at the end of your project. \$\endgroup\$ – OriginalDaemon Nov 16 '12 at 10:43
  • \$\begingroup\$ That is of course definitely an option. However like I said I as a coder would prefer to implement this on my own if only as a learning exercise. \$\endgroup\$ – onedayitwillmake Nov 16 '12 at 17:19
1
\$\begingroup\$

The complexity of doing this in a physics solver comes from trying to do it in a way that is both numerically stable (doesn't freak out and send your rigid bodies flying) and that conserves energy.

Erin Catto (the author of Box2d) has several GDC presentations that show how to do this with impulses so you wouldn't need an entire physics solver to make it happen.

http://code.google.com/p/box2d/downloads/list

\$\endgroup\$
3
\$\begingroup\$

Implementing any constraint in an impulse/velocity solver comes down to finding the relative velocity between the two connected bodies that you want to remove.

In the case of the distance constraint, this relative velocity is always in the direction between the constraint anchor points.

Vector between the anchor points

If you get the relative velocity of the two bodies, project it onto the normalised vector between the anchor points, you'll have the velocity that you want to remove.

Velocity of each body projected onto the vector between anchor points

Then, all you'll need is an equation which lets you remove velocity by calculating an impulse. Plug in the velocity to remove and out pops an impulse which you can then apply equally and opposite to both rigid bodies.

If you'd like to read more about this process, I wrote an article a while back which covers designing a constraint in a impulse/velocity solver:

http://www.wildbunny.co.uk/blog/2011/04/06/physics-engines-for-dummies/

Hope it helps!

\$\endgroup\$
  • \$\begingroup\$ In your equation: I = (1+e)*N*(Vr • N) / (1/Ma + 1/Mb) (from the article you linked), what does each identifier represent? \$\endgroup\$ – theonlygusti Aug 26 '15 at 21:06
1
\$\begingroup\$

Why don't you take a look at the Box2D Source. In there you'll be able to see how the distance joint works. It might be a bit complex, but the part I would start with is "solveVelocityConstraints" in the b2DistanceJoint.

If all you are looking for is a very simplistic implementation then you could try this;

  • Find the distance between ObjectA and ObjectB
  • Find the difference the distance and the desired rest distance.
  • Apply a force, proportional to this difference value, to each body, in the direction of the other body.

It might look similar to the following for a basic implementation (disclaimer, I don't have the ability to test this right now).

Vec3 dir = ObjectB.position - ObjectA.position;
float mag = (dir.length - targetLength) * 0.5f;
dir.normalise();
ObjectA.velocity += dir * -diff;
ObjectB.velocity += dir * diff;

Keep in mind this is a very simplistic implementation and is potentially unstable. Also keep in mind that this would never let the body "rest", so the two objects would be constantly moving towards and then away from each other, although it might be too small to see. This is why you would be best either using box2D or working out how it works and then replicating it.

\$\endgroup\$
0
\$\begingroup\$

There are a couple things you can do shorthand that do not require an entire physics solver.

See example. https://youtu.be/UDkccb5fRwY

The only real code in the solver for a distance joint is to propagate the change. What you need to do is find the current distance of that object, assume the object is pulled and figure where it's new position should be. Then figure the pull of that change and propagate it down the line.

My code for the example is:

public void tugEvent(LayerIndex index, double toX, double toY) {
        double newX, newY;
        int posPost;
        if (index == null) return;
        if (!index.isValid()) return;
        PointSeries points = index.getLayer().getPointSeries();
        newX = toX;
        newY = toY;
        posPost = index.pointIndex;
        for (int pose = points.size() - 1; posPost < pose; posPost++) {
            Point c = points.getPoint(posPost);
            Point n = points.getPoint(posPost + 1);
            double originalDistance = Geometry2D.distance(c.getX(), c.getY(), n.getX(), n.getY());
            invalidate((float) c.getX(), (float) c.getY());
            if (posPost != index.pointIndex) {
                c.setLocation(newX, newY);
            }
            double newDistance = Geometry2D.distance(newX, newY, n.getX(), n.getY());
            double direction = Geometry2D.angleR(n.getX(), n.getY(), newX, newY);
            double difference = newDistance - originalDistance;
            if (Math.abs(difference) <= 0.1d) {
                break;
            }
            newX = n.getX() + (Math.cos(direction) * difference);
            newY = n.getY() + (Math.sin(direction) * difference);
            if (posPost + 1 >= pose) {
                n.setLocation(newX, newY);
                break;
            }
        }
        newX = toX;
        newY = toY;
        int posPrior;
        posPrior = index.pointIndex;
        for (int pose = 0; posPrior > pose; posPrior--) {
            Point c = points.getPoint(posPrior);
            Point n = points.getPoint(posPrior - 1);
            double originalDistance = Geometry2D.distance(c.getX(), c.getY(), n.getX(), n.getY());
            if (posPrior != index.pointIndex) {
                c.setLocation(newX, newY);
            }
            double newDistance = Geometry2D.distance(newX, newY, n.getX(), n.getY());
            double direction = Geometry2D.angleR(n.getX(), n.getY(), newX, newY);
            double difference = newDistance - originalDistance;
            if (Math.abs(difference) <= 0.1d) {
                break;
            }
            newX = n.getX() + (Math.cos(direction) * difference);
            newY = n.getY() + (Math.sin(direction) * difference);
            if (posPrior - 1 <= 0) {
                n.setLocation(newX, newY);
                break;
            }
        }
        invalidateRange(index.getLayer().getPointSeries(), posPrior, posPost);
    }

It does it in both directions (hence the duplicate code block, but descending) so I can pull any point and abort when it gets a pull below a certain threshold. That's all you actually need. How does the change in location tug the next point down the line, and when you apply that vector to it, calculate it for the next attached values.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.