Having problem moving a node2D with Tween

DanySnowymanDanySnowyman Posts: 15Member

Hi again. As I said earlier, I'm making mi first game, a Space Invaders clone. I made some design mistakes but I'm on the good way. For now, I made a Node2D to do the tasks of creating formations and move they. I wanted the formation drop 16 píxels every time a invader touchs the limit I want. I though that a Tween node will do the work well but every time I try to run the scene with the Tween node attached as child of the Node2D (the way I think has to be) I obtained an error "Invalid get Index 'x' (on base: 'Nil') at line 31. It's strange because before adding the tween node, or even if I delete it, the scene works well. ¿Any idea? I can't understand the interrelation between the Tween and the error message.

extends Node2D

export (PackedScene) var Baboso_Basic
onready var tween = get_node("Tween")

const formation = Vector2 (6, 3)

var velocity = Vector2(1, 0)
var speed = 25


func _ready():
    position = Vector2(24, 24)

    for y in range (formation.y):
        var babosoPos = Vector2(0, (y * 20))
        for x in range (formation.x):
            babosoPos.x = (x * 24)
            var baboso = Baboso_Basic.instance()
            baboso.position = babosoPos
            add_child(baboso)


func move_formation(delta):
    position += (velocity * speed) * delta


func if_border_reached():
    for baboso in get_children():
        if left_border_reached(baboso) or right_border_reached(baboso):
            get_down()
            velocity = -velocity
            break


func left_border_reached(baboso):
    if velocity.x < 0.0:
        if baboso.global_position.x < 20:
            return true
    return false


func right_border_reached(baboso):
    if velocity.x > 0.0:
        if baboso.global_position.x > 300:
            return true
    return false


func get_down():
    tween.interpolate_property(self, "position",
        position, position + Vector2(0, 16), 2,
        Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
    tween.start()


func _process(delta):
    move_formation(delta)
    if_border_reached()

Best Answer

  • cyberealitycybereality Posts: 651
    Accepted Answer

    Ah, I get it now, I don't think it is a bug.

    When "get_children()" is called on line 29, it includes the babosos but also the tween node itself. Then the functions "left_border_reached()" and "right_border_reached()" are called with the baboso, but then that object is a tween node, it then checks for "global_position" which does not exist on tween. That is the whole issue.

    Probably you don't want to use "get_children()" but rather create an array to store the object references, but you'll have to manage add/deleting objects from the array if the enemies are killed, that is one way of doing it. You can also check the class name to make sure the object is a baboso before doing stuff to it.

    func if_border_reached():
        for baboso in get_children():
           if baboso.get_class() == "BabosoClass":
              ...
    

    On the baboso class:

    func get_class(): return "BabosoClass"
    

    Hope that helps.

Answers

  • MegalomaniakMegalomaniak Posts: 1,999Admin
    edited February 3

    If the tween is the child of node 2D the problem is simple. Read this whole page:
    https://docs.godotengine.org/en/3.1/getting_started/step_by_step/scene_tree.html

    As per the above linked page:

    Tree order

    Most node operations in Godot, such as drawing 2D, processing, or getting notifications are done in tree order. This means that parents and siblings with a lower rank in the tree order will get notified before the current node.

    “Becoming active” by entering the Scene Tree

    1. A scene is loaded from disk or created by scripting.
    2. The root node of that scene (only one root, remember?) is added as either a child of the “root” Viewport (from SceneTree), or to any child or grandchild of it.
    3. Every node of the newly added scene, will receive the “enter_tree” notification ( _enter_tree() callback in GDScript) in top-to-bottom order.
    4. An extra notification, “ready” ( _ready() callback in GDScript) is provided for convenience, when a node and all its children are inside the active scene.
    5. When a scene (or part of it) is removed, they receive the “exit scene” notification ( _exit_tree() callback in GDScript) in bottom-to-top order

    TL;DR:

    The code should be implemented probably outside the _ready() and implement a if statement to check if the nodes are ready then run your code. (basically if Tween node isn't ready, pass through the function)

  • DanySnowymanDanySnowyman Posts: 15Member

    Hi. I'm pleased you answered me. I'm not a programmer, only a beginner from one month ago. I already had read all the Godot documentation and a lot of tutorials before start trying do things by myself. Anyway, I read that section once again. I understand every node of the tree, from parents to childs, do a callback when enters the scene and, when everyone has entered, then, in reverse, they do a "ready" callback. If this is so, I think the node Tween will necessarily be also "ready" when his parent node ("Babosos_formation") be. Maybe the problem is with the instanced invaders but... I don't understand merely whats the problem. I did some test moving things with Tween outside the project and worked well. I can't see why it doesn't work here. Sorry but I am so newbie. Can you enlighting my mind in some way? Any little clue? I spent two more hours with the code on it after I saw your answer and I'm not still sure about what to do. ¿It's correct that the Tween node must be child of the node we want to manipulate? ¿Can he transform a Node2D with scenes instanced (invaders)? Thanks again, I hope you can help me a little more. Greetings.

  • MegalomaniakMegalomaniak Posts: 1,999Admin
    edited February 3

    The very first time that tween is being called upon the node is not ready yet, so you get the warning/error. If you implement a condition check that simply bypasses the code within get_down() if Tween node isn't yet in the scene tree and ready then you bypass the error. The code isn't wrong, but it only works if the node has been spawned and readied.

    try something like this(untested, but should work):

    func get_down():
        if tween.is_inside_tree() and self.is_inside_tree() == true:
            tween.interpolate_property(self, "position",
                position, position + Vector2(0, 16), 2,
                Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
            tween.start()
        else:
            pass
    
  • DanySnowymanDanySnowyman Posts: 15Member

    Hi. Still doesn't work. I don't understand why the Tween node its not ready when the game starts. But, in any case, It doesn't work either if I try to bypass the function. Teorically, the error should show on when the function get_down() is called (when the invader surpases the 300 pixels mark) but the error splashes instantly when I run the project. It has no sense to me. Literally I'm blocked. As I said, I did things for practice with Tween nodes outside this projects and all ran ok...

  • MegalomaniakMegalomaniak Posts: 1,999Admin
    edited February 4

    hmm, any chance you can share the project?

    edit: looking at the image more closely it's baboso that isn't instanced yet, I think. But it would probably still be easier to diagnose with the project at hand.

  • DanySnowymanDanySnowyman Posts: 15Member

    I don't know how to share it. I know there's something called Github. Maybe I can upload the project there but I need to learn first some about that, and looks so complicated. The most strange thing about the error is that if I simply delete the Tween node, the scene is executed well, wich leads me to think that the baboso is well instanced. Give me some time to get how to do the Github thing, or if there's any other way to share the project with you, let me know how.

  • MegalomaniakMegalomaniak Posts: 1,999Admin

    I often just use my google drive for this, but there's also dropbox you could use, or if you want to figure it out anyways, yes, github is a good candidate too.

  • DanySnowymanDanySnowyman Posts: 15Member

    Wait, ¿its enough if I share you the project directory? If it is, no problem. I tryed to configure a page for a project in Github but seems too complicated for me at this moment and I prefer to focus in the project for now.
    Here is a link tot he project. I hope you can help me to solve the issue, because I'm truly blocked.

    https://my.pcloud.com/publink/show?code=kZ0EBWkZy1xwSc4SXHbMeDso4u0Rh4JGb7By

  • MegalomaniakMegalomaniak Posts: 1,999Admin

    Right, it's probably the line in if_border_reached where you use get_children(), without the Tween node the array from get_children() works fine, since there's only your baboso instances in there, not sure why or how exactly it intereferes there though, it doesn't make sense to me either.

    I just moved the Tween node out of the formation hierarchy so it's on the same level with player and the formation and changed the onready var for the tween to onready var tween = $"../Tween"

    Thats probably the easiest solution.

  • DanySnowymanDanySnowyman Posts: 15Member

    Ok. Finally was true that something strange was happening. Thanks for answering & helping me that much. I was think that the Tween node needed to be set necessarily as child of the node I wanted to move. But I tried your solution and I learned that I can put the Tween wherever I want. This fix this issue forever, but, maybe the problem is some bug that we must report to someone for fixing?

  • cyberealitycybereality Posts: 651Member
    Accepted Answer

    Ah, I get it now, I don't think it is a bug.

    When "get_children()" is called on line 29, it includes the babosos but also the tween node itself. Then the functions "left_border_reached()" and "right_border_reached()" are called with the baboso, but then that object is a tween node, it then checks for "global_position" which does not exist on tween. That is the whole issue.

    Probably you don't want to use "get_children()" but rather create an array to store the object references, but you'll have to manage add/deleting objects from the array if the enemies are killed, that is one way of doing it. You can also check the class name to make sure the object is a baboso before doing stuff to it.

    func if_border_reached():
        for baboso in get_children():
           if baboso.get_class() == "BabosoClass":
              ...
    

    On the baboso class:

    func get_class(): return "BabosoClass"
    

    Hope that helps.

  • MegalomaniakMegalomaniak Posts: 1,999Admin
    edited February 6

    Yeah, thats it!

    Using the class name method is indeed probably the best option, but also the tween really can be anywhere so long as you can get to it, basically so long as you know where in the scene tree it is. I think many different nodes might be able to call on it and so you can use it even as a general purpose one if you want.

  • DanySnowymanDanySnowyman Posts: 15Member

    Wow! Thats's it Cybereality! It makes all the sense now. I was very miss with this. Thanks a lot. At the other side, all that things about classes, well, I need to study about it. Everything seems new to me. Sometimes I get desperate but I learn new things every day.

    I don't know how to tag this discussions as "ansewered". How is it done?

  • MegalomaniakMegalomaniak Posts: 1,999Admin
    edited February 8

    @DanySnowyman I've switched the topic type, you should see a question at the end of each post that allows you to mark a post as answer.

  • DanySnowymanDanySnowyman Posts: 15Member

    Finally I put back the Tween node as child of Babosos_Formation and solved the issue with this line:

    for baboso in get_tree().get_nodes_in_group("total_babosos"):

    I put this here just in case in the future someone has some similar problem ;)

  • cyberealitycybereality Posts: 651Member

    Right. Good solution.

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file