Search code examples
godotgodot4

Why isnt my spike shooters child showing up in the scene in godot?


I am trying to code in a spike shooter to add into my game levels. I have a spike shooter scene and a separate scene for the spike. The spike shooter instantiates and then adds the spike as a child. However the spike does not show up in the scene despite me checking its position and whether visibility was set to true all of which came out as expected the spike scene also shows up in the remote view. When I add the spike to the scene normally everything works fine. What is wrong and how do I fix it?

this is the function I use to shoot the spike in the spike shooter:

func shootSpike():
    print("shoot")
    var spikeScene = preload("res://spike.tscn")
    var shootSpike = spikeScene.instantiate()
    shootSpike.position = Vector2(self.position.x - 25, self.position.y - 5)
    shootSpike.visible = true
    print("position",shootSpike.position)
    self.get_parent().get_node("World_Objects").add_child(shootSpike) 
    shootSpike.setChildNodePositions(shootSpike,shootSpike.position)
    canShoot = false

this is the spikes _ready function:

var direction = -1

func _ready():
    print("hello")
    print("node2Dpos",self.position)
    self.visible = true
    get_node("CharacterBody2D/Marker2D").scale.x = direction * -1
    setChildNodePositions(self,self.position)
    self.set_owner(self)


func setChildNodePositions(node = self,nodePosition = self.position):
    for child in node.get_children():
        child.position = nodePosition
        # Recursively call the function for child nodes
        setChildNodePositions(child,child.position)

Solution

  • The position you set BEFORE adding the instance to the scene will be relative to its eventual parent.

    Note: In general the position is relative to the parent of the node. But on one hand, before you add the node to the parent, it has no parent... Instead Godot stores the value for when it has a parent. And on the other, the global_position should be relative to the origin of the world, but when there is no parent, it simply sets the position. Thus even the global_position you set BEFORE adding the instance to the scene will be relative to its eventual parent.


    There are multiple approaches to correctly set the position. The easier being to set the global_position AFTER you added the child.

    However, as soon as you add the node, physics interactions can happen.

    Thus, if you want to do this safely, compute the position based on where you will place it. Which means we get the position in global coordinates first:

    var spike_global_pos := Vector2(global_position.x - 25, global_position.y - 5)
    

    Then we convert it to locals of the eventual parent, and that is what we set:

    var spike_parent:Node = get_parent().get_node("World_Objects")
    shootSpike.position = spike_parent.to_local(spike_global_pos)
    

    Thus, like this:

    var spike_global_pos := Vector2(global_position.x - 25, global_position.y - 5)
    
    var spike_parent:Node = get_parent().get_node("World_Objects")
    shootSpike.position = spike_parent.to_local(spike_global_pos)
    spike_parent.add_child(shootSpike)
    

    In the code in the question, this position:

    Vector2(self.position.x - 25, self.position.y - 5)
    

    Is being computed in the coordinates of the parent of node where the script is attached.

    But this position:

    shootSpike.position
    

    Will be relative to the sibling with name "World_Objects" because that is where you will add the node:

    self.get_parent().get_node("World_Objects").add_child(shootSpike) 
    

    Those aren't the same coordinate system.

    This would not be a problem in the case that the sibling "World_Objects" is in the same position (and same orientation, and same scale and skewing) as the node where the script is attached. But that is not guaranteed.


    Remember that the position is relative to the parent. In fact, when you move your Node2D all its children move with it (the same holds for Node3D, btw).

    Thus, I believe the method setChildNodePositions is not only not necessary, but detrimental.

    For example:

    • Let us say the position of the spike is (100, 100), which relative to its parent, which is the node with name "World_Objects".
    • And now you set that same position to the children of the spike...
    • That position of the children of the spike is relative to the spike.
    • So the position of the children of the spike relative to the node with name "World_Objects" (which is the parent of the spike) will be (200, 200).
    • Why? Because their position relative to the spike will be (100, 100) but the position of the spike relative to the "World_Objects" is (100, 100), for a total of (200, 200).

    If didn't mean to set the position relative to its parent, but the position in global coordinates (i.e. relative to the origin of the world), then you would set the global_position. Except you do not need to do that, because the children move with their parent (when the parent moves, the children move).