Hello all.

I've completed the "First 2D Game" tutorial recently, and decided to build upon it to practice simple programming concepts.

I have images spawned with the same timer from the tutorial falling down from the top of the screen. I'd like them to delete themselves when entering a key.

Here is my code for now :

So far, it deletes all the spawned children on screen, but it doesn't delete a specific one.

I have tried using get_node("water").get_child(0)

where get_child(0) would be the first item created in the "water" group, but it doesn't work.

Any help would be appreciated.

Thanks!

So that is almost correct. get_nodes_in_group() returns an Array with a list of all those water nodes, which could be large. If you want one specific one, you need a way to identify it, either with a unique name or by storing the reference in another Array or variable. One way to do this is to give it a name after you create it.

# at top of script
var count = 1
# in timeout
hanzi = hanzi_scene.instance()
hanzi.name = "hanzi_" + str(count)
count += 1
# in input
var hanziWater = get_node("hanzi_0") # first one
hanziWater = get_node("hanzi_" + str(count - 1)) # last one
hanziWater = get_node("hanzi_3") # third one, assuming 3 have been created
hanziWater.queue_free()

    cybereality
    Thank you for your helpful answer.

    It worked as expected and I implemented another type of word to test out the same concept.
    However, since the instances' children get spawned quite fast and increment the "count" variables which store their
    references, it would be difficult to access them in an efficient way (I would maybe have to write get_node("write_0"),
    get_node("write_1"), get_node("write_2") and so on.

    So I thought about implementing an if statement to make sure some words wouldn't slip past the pressed key, i.e

    if "hanzi" + str("count -3") was still here, first delete that one with queue_free() first,
    then "hanzi
    " + str("count -2"), queue_free(),
    then the most recent one with "hanzi_" + str("count -1")

    So I tried this :

    It doesn't work, and I'm sure I'm missing something obvious, but I can't wrap my head around this bit yet.

    Thanks again.

    cybereality

    Sorry if I wasn't clear enough.

    Ideally, I'd like to make a typing game.
    There would be different "words" represented by images that would fall down at random speeds or timings, and the player would have to input the corresponding word for each "image".
    If the entered word is correct, then the corresponding word gets deleted, otherwise, it hits the "player zone" at the bottom of the screen and remove a point on the player's health.
    The challenge here is that the player would have to input the words in the order they appear on screen. Say there are two "water" words on screen, the first one (closer to the player zone) must be dealt with before the second one.

    Because the words get spawned randomly by timer, I'd like to make sure the player has the time to enter the input for the first word, so your first suggestion is valid and worked, but as different words get spawned, their names get harder to track.
    Basically I'd like a way to "sort" the names automatically as they get spawned and removed.

    Something like :
    for each "word" scene that gets spawned
    first focus on the word closer to the player zone , check if it either hits the player, or get deleted
    then focus on the second word, check the same thing
    and so on
    in a backwards fashion, from the player zone to the spawn zone.

    Hope this makes more sense, still trying to wrap my head around all this stuff.

    I just thought of (maybe) an array where the word closer to the player would be "item0", and then it gets freed, the second word in the array then becomes "item0", so this way could allow me to write a code to check item0 first and then the rest?

    Will try this out when I get home.

    So you can do this all with code. Probably what I would do, is use get_nodes_in_group() for a selected word (say "water") and then sort that list by come criteria. For example, if the words all start at the top and move down at a uniform speed, you can use the y position. So put all the words under a single Node2D (say it is called "Words") and then use get_children() to get the children and then sort those.

    extends Node2D
    
    func _ready():
    	var lowest_word = get_lowest_word()
    	print(lowest_word.name)
    	
    func get_lowest_word():
    	var words = get_node("Words")
    	var all_words = words.get_children()
    	all_words.sort_custom(Sorter, "sort_y")
    	return all_words[0]
    
    class Sorter:
    	static func sort_y(a, b):
    		return a.position.y > b.position.y

    If you want to do it based on when they are created, then modify the code to save the Unix timestamp when you spawn the word, and then add a sort function to compare that (rather than the y value). You can get the time with OS.get_unix_time().

    extends Node2D
    
    func _ready():
    	var test = 10
    	var words = get_node("Words").get_children()
    	for word in words:
    		word.time = OS.get_unix_time() + test
    		test += 10
    	var oldest_word = get_oldest_word()
    	print(oldest_word.name)
    	
    func get_oldest_word():
    	var words = get_node("Words")
    	var all_words = words.get_children()
    	all_words.sort_custom(Sorter, "sort_time")
    	return all_words[0]
    
    class Sorter:
    	static func sort_time(a, b):
    		return a.time < b.time

      Note that the beginning of _ready() is mostly test code (just to set fake time codes). In the real app you would just need one line to set the time.

      word.time = OS.get_unix_time()

      And time is a variable I created on the script that is attached to the word.

      cybereality

      Thanks for the detailed answer.
      I understand your code snippets, unfortunately, being a newbie, I've failed to integrate them completely with my existing code.

      Before seeing your answer, I actually had been trying something that seems to work (for now at least):

      After declaring an array for each word, I then add them after spawning

      I can then retrieve the value of the item0 and queue_free() it, then remove() it from the array, effectively allowing the next spawned item to become first one

      I can then code my score accordingly

      It's obviously very basic, and I can see the code becoming very bloated quickly with each new word.
      Thank you again for the code snippets you've provided. I'll keep trying adapting them into my code.

      Okay, well I'm pretty sure my code will work, so try to keep reading it and figure out how you can make it work.

        cybereality
        Ok, so I'm trying to test out your code in sort of a "vacuum" first to see how it works and be sure to understand the logic behind it.

        I've created a Node2D called "WORDS" and put my two words "FIRE" and "WATER" under it (their nodes). I've attached a script to the WORDS node, where I spawn the two words' instances and put them in an array.

        In my MAIN node, I call the get_lowest_word() function, but the main problem I've encountered so far is the function
        will call the WORDS node and their children, and will literally look at the order of the children in the "Outliner" on the left.
        So if I move HANZI_WATER at the top, the get_lowest_word() function will return HANZI_WATER, and same for HANZI_FIRE.

        It's not looking at the scripts attached and the codes inside, but just at the nodes themselves. I'm not sure that's the intended purpose.

        I've also used get_nodes_in_group() to get each word's array list, to feed it to the get_lowest_word() function, and put it in my MAIN node.

        I think my main trouble is figuring out where those functions go (which node), so they can relate to each other.
        I'm sure I've done something wrong, can't figure out what yet.

        Lowest word is based on the y-axis of position of the node on the screen. My code would go on the WORDS node, so it can access the others (or on a Node above). Try my project.

          cybereality

          Thank you for this. I think I understand the logic behind it better.

          I've played with your project a bit and adapted it into mine.
          So far, everything works as expected, except for the spawned instances.

          I've imported the Node2D "Words" as a packed scene, and used instance() and add_child() to have it spawn its children nodes randomly.
          The only problem is it spawns only the base node "Word" (the base node for all the children nodes).

          So I tried creating a script to choose them randomly, and another one to modify the instance() to have a random children spawn instead of the base node.

          It works but after spawning one random children, it immediately disappears again, not staying on screen and having the gravity do its thing. So they all appear and disappear with the timer's timeout.

          I've spent the last night looking up the docs and the forums for this problem, but haven't come across something similar yet.

          What is words_scene and what is $Words? That code doesn't really seem to make sense. You are calling instance, but this is doing nothing, since you override that reference on like 86, before you add it to the node. Like 95 just adds the randomWordName (which I don't even know what it is) to the current node the script is on, which also doesn't make much sense. I'm just a little confused.

            cybereality

            Silly me, I completely forgot to contextualize the rest of the variables.

            words_scene refers to a packed scene I imported at the top of the script to help me instancing nodes (since it looks like one of the best ways to do so)

            $Words is to help me get to the Words node and its children (I couldn't find a way to do that with the imported scene above)

            So basically, my idea was to import the Words node in the Main scene (with the export(PackedScene)), to be able to access its children. I couldn't do that, so I then called the $Words.get_children() function. I randomized them and picked them through the randomWordChoose function.

            And I just wanted to assign the value of the result to word1, so the random-picked child could be assigned to the instance to spawn with add_child(word1).

            I'm a bit confused as you can see, but I'll try figuring all this out nonetheless.

            Each word should just be an instance of a saved scene (like word.tscn). That scene can have dynamic properties, like changing the text, color, size, whatever. You don't want to create a duplicate scene for each word, as if the game had 100 or 500 words that would be impossible to work with. It's also not a flexible design. You don't need to use packed scene. Just save the scene (the base word scene) as a file `word.tscn' and then do this to reference it.

            onready var word_scene = preload("word.tscn")
            
            func _ready():
                var water = word_scene.instance()
                water.get_node("label").text = "Water"
                water.position = Vector2(300, 400)
                add_child(water)

              cybereality

              That dit it, I can properly instance my scenes now.

              I clearly got overwhelmed and confused, so I'm going to practice a bit more.

              Thanks a lot for helping me out on this one, the code snippets and for the tips as well.
              That'll come real handy for my next practice project.