0 votes

When connecting PinJoint2Ds before runtime everything works fine. But when I do the EXACT same thing at runtime, everything just explodes. I even looked a thousand times at the remote tree and it's EXACTLY the same structure as when connected before runtime with editor.

Node RopeLink:

RigidBody2D
Sprite
CollisionShape2D
PinJoint2D

Node Rope:

func _ready():
    _spawn(3)
    pass

func _spawn(link_num):
    var link_pos = []
    for i in range(link_num):
        var link = RopeLink.instance()
        if i == 0:link.set_mode(RigidBody2D.MODE_STATIC)
        link_pos.append(link.get_position())
        add_child(link)
        link.set_position(Vector2(link_pos[i].x, link_pos[i].y + i * 200))
    for i in range(link_num - 1):
        var link = get_child(i)
        var next_link = get_child(i + 1)
        var joint = link.get_child(2)
        joint.position.y += 0
        joint.node_a = "../../" + link.name
        joint.node_b = "../../" + next_link.name
    pass
in Engine by (840 points)

2 Answers

+1 vote
Best answer

This is the problem:

add_child(link)
link.set_position(Vector2(link_pos[i].x, link_pos[i].y + i * 200))

You add the link to the scenetree and this leads to the effect that the physics engine is taking control of its position. You have to set the position first. So just swap the lines.

link.set_position(Vector2(link_pos[i].x, link_pos[i].y + i * 200))
add_child(link)

By the way, your code can be refactored:

  • Remove meaningless pass
  • Remove meaningless joint.position.y += 0
  • Add some comments so it's not hard to understand what is going on
  • Use godots function API to make your code more readable:

Change this:

joint.node_a = "../../" + link.name
joint.node_b = "../../" + next_link.name

To this:

joint.node_a = joint.get_path_to(link)
joint.node_b = joint.get_path_to(next_link)

I tried to rewrite your code the way I would like to read it. You don't have to agree because everyone has their own style. It's just a suggestion:

var RopeLink = preload("res://RopeLink.tscn")

func _ready():
    _spawn(3)

func _spawn(link_num):
    # Keep a list of creating links
    var links = []

    # Create those links
    for i in range(link_num):
        # Instance and set a relative position
        var link = RopeLink.instance()
        link.set_position(link.position + i * Vector2(0, 200))

        # First link should be fixed
        if i == 0:
            link.set_mode(RigidBody2D.MODE_STATIC)

        add_child(link)
        links.append(link)

    # Set their PinJoint2Ds
    for i in range(links.size() - 1):
        var joint = links[i].get_node("PinJoint2D")  # The node can be found by its name
        joint.node_a = joint.get_path_to(links[i])  # Relative paths are way easier to maintain
        joint.node_b = joint.get_path_to(links[i+1])
by (1,028 points)
selected by

Thanks alot! What you said makes sense. And now I also understand better why my approach with doing the spawning in the _init(): function works.

You are welcome. Using the _init function is also a valid way of implementing this. It depends on your needs and you should always consider every way possible.

0 votes

Solved this myself. Turns out using _init() gets rid of the problem, since spawning links in _ready() causes the joints to mess up due to gravity being applied while spawning them.

by (840 points)
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.