0
\$\begingroup\$

I'm trying to implement the movement of a chain of segments connected to each other (gravity, mass, etc. are not involved). The chain can only be dragged by its "head" segment. Each segment is represented with 2 vectors (front and back) and has fixed length. Here's my method that adjusts the position of a segment after its previous segment was moved:

public void AdjustLocation()
{
    Front = PrevSegment.Back;

    Vector delta = Front - Back;
    float f = SegmentSize.Width / delta.Length;

    Back = Front - (delta * f);
}

So it does two things:

  1. Sets the front location of a segment equal to the back location of its previous segment.
  2. Moves the back location towards the new front location in a straight line maintaining the fixed length.

But the result of that approach is not realistic enough. Like here: If we pull this chain down, the rear end of it (on the left side) shouldn't move at all until this "knot" straightens out, but it follows its previous segments too much:

As I understand it, when the "head" segment is moved, it just pulls the rest with it, and there's supposed to be a tension that pulls previous segments back.

How can I implement it or make it look slightly more realistic? I've read about simple constraints that just pull objects towards each other to the middle, but I don't understand how is it supposed to work when there are many constraints. Is it necessary to implement complex physics for this?

\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Ok, I think I figured it out.

I've changed the structure a little bit so that every segment is represented by two nodes.

Some pesudo structure:

public class Chain
{
    public List<Node> Nodes;
    
    public void Update(float dt)...
    private void Simulate(float dt)...
    private void ApplyConstraints()...
}

public class Node
{
    public Vector Position;
    public Vector PrevPosition;

    public Node PrevNode;
    public Node NextNode;
    
    public void Simulate(float dt)...
    public void AdjustLocation()...
}

Correcting the position of two adjacent nodes:

public void AdjustLocation()
{
    if (NextNode == null)
    {
        return;
    }

    Vector delta = Position - NextNode.Position;
    float distance = delta.Length;
    float restDistance = SegmentSize.Width + SegmentSpacing;

    if (distance > restDistance)
    {
        float f = (restDistance - distance) / distance;
        Vector pull = delta * f * 0.5f;

        // We don't want to pull the leading node back
        if (PrevNode != null)
        {
            Position += pull;
        }

        NextNode.Position -= pull;
    }
}

Then it's something like:

private void ApplyConstraints()
{
    int iterations = 100; // the more iterations, the more accurate the result
    while (iterations-- > 0)
    {
        foreach (var node in Nodes)
        {
            node.AdjustLocation();
        }
    }
}

Though even with a large number of iterations the chain was stretched when moving. So I've also implemented verlet integration, which added some inertia:

public void Simulate(float dt)
{
    const float friction = 0.95f;

    // Velocity verlet: x(t + dt) = 2x(t) - x(t - dt) + a * dt^2
    Vector velocity = (Position - PrevPosition) * friction;
    PrevPosition = Position;
    Position += velocity; // there is no acceleration though, can remove dt
}

In the chain class:

public void Update(float dt)
{
    Simulate(dt);
    ApplyConstraints();
}

private void Simulate(float dt)
{
    foreach (var node in Nodes)
    {
        node.Simulate(dt);
    }
}

And you can change the location of a node without changing its velocity like this:

node.Position = position;
node.PrevPosition = node.Position;
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .