1

I am trying to create a simple chain between two fixed endpoints. The problem is that I can't seem to get the chain to reach a stable state. Further, the chain is always biased toward one of the endpoints.

Here are my constants and variables:

const NUMBER_OF_LINKS = 100
const CHAIN_LENGTH = 400
const DIST_BETWEEN_LINKS = CHAIN_LENGTH / NUMBER_OF_LINKS
const ITERATIONS = 20 # number of times we iterate through constraints each frame
const GRAVITY = Vector2(0, 300)

var anchor1
var anchor2

var prev_link_positions = []
var links = []

Here's what I do on initialization:

create_links(NUMBER_OF_LINKS)

var first_link = links[0]
var last_link = links[NUMBER_OF_LINKS - 1]

first_link.global_position = anchor1.global_position
last_link.global_position = anchor2.global_position

And here are the adjustments I do each frame:

links[0].global_position = anchor1.global_position
links[NUMBER_OF_LINKS - 1].global_position = anchor2.global_position

for i in range(NUMBER_OF_LINKS):
    var acceleration = GRAVITY
    acceleration *= delta * delta
    var prev_position = links[i].global_position
    if not i in [0, NUMBER_OF_LINKS - 1]:
        links[i].global_position = 2*links[i].global_position - prev_link_positions[i] + acceleration
    prev_link_positions[i] = prev_position

for iteration in range (ITERATIONS):
    for i in range(NUMBER_OF_LINKS):
        var link = links[i]     

        var prev_link_position
        if i == 0 or i == NUMBER_OF_LINKS - 1:
            prev_link_position = links[i].global_position
        else:
            prev_link_position = links[i-1].global_position

        var next_link_position
        if i == 0 or i == NUMBER_OF_LINKS - 1:
            next_link_position = links[i].global_position
        else:
            next_link_position = links[i+1].global_position

        # get vector to previous link and adjust position
        var vec_to_prev_link = prev_link_position - link.global_position
        link.global_position += vec_to_prev_link.normalized() * max(vec_to_prev_link.length() - DIST_BETWEEN_LINKS, 0) / 2.0

        # get vector to next link and adjust position
        var vec_to_next_link = next_link_position - link.global_position
        link.global_position += vec_to_next_link.normalized() * max(vec_to_next_link.length() - DIST_BETWEEN_LINKS, 0) / 2.0

When I run this, I almost get what I want, except the chain contorts and never reaches a stable state. Also, it is biased in the first direction I look at when solving the constraints.

It looks something like this:

enter image description here

What am I doing wrong? How can I get a chain that stabilizes with links more uniformly distributed?

DyingIsFun
  • 1,307
  • 1
  • 17
  • 39

1 Answers1

2

The problem is that you first move every link closer to one of its neighbors, then closer to the other, without saving the original position. These two should happen at the same time, so that the position of the current links stays fixed throughout.

    # get vector to previous link and adjust position
    var vec_to_prev_link = prev_link_position - link.global_position
    var adjust1 = vec_to_prev_link.normalized() * max(vec_to_prev_link.length() - DIST_BETWEEN_LINKS, 0) / 2.0

    # get vector to next link and adjust position
    var vec_to_next_link = next_link_position - link.global_position
    var adjust2 = vec_to_next_link.normalized() * max(vec_to_next_link.length() - DIST_BETWEEN_LINKS, 0) / 2.0

    link.globalPosition += adjust1 + adjust2

The alternative is to get the vectors pointing to the previous and next chains before you adjust the current one

Bálint
  • 14,887
  • 2
  • 34
  • 55