I'm working on adding items to my top down game that you can pickup to add to your inventory. I have all the items in my game extending the class "Item". I thought the easiest way to add a pickup option for the player would be that if the player presses the pick up button it will look through all instances of the "Item" class and if any are under the pick up distance radius then they get added to the inventory. I could not find anything about iterating through the instances of a specific class searching the forum or googling. Is this not something I can do? Or is this a strange way of going about doing this? I figure I can implement effectively the same thing by just attaching a collision circle child to the player and looking through the items inside the collision circle but I didn't want my player scene to become cluttered and I figured this way of implementing pickups would be somewhat trivial but here I am a couple hours later with no progress lol. Is it better just to use a collision body? Regardless for future reference can you loop through all instances of a class? If you can is it inadvisable (slow, clunky, etc)? Is this the use case of groups?
Iterating through all nodes of a class
"Or is this a strange way of going about doing this?" I'd say yes because one does not generally just say "give me all of one class", you could instead get all the objects of that instantiated class, but since godot works around nodes in the scene tree, using that scene tree makes more sense. For example, when you hit the "pick up" button, ask the floor node or whichever node is parent to all the objects on the floor and iterate through all the children (floor items) and you could do distance compares to see if they're close enough to the player to be picked up.
If you just put items in the scene tree mixed with a bunch of other nodes, you can test for your class name with 'if n is Item'
Yes you could also do this with a collision body which would be useful in that you can visualize the pick up radius- I would probably do it that way, then you might not need a "floor node" middle man to help you find your list of items. I never use groups, but I assume you could use groups here as well.
- Edited
So there are a few ways to do it. Your suggestion is not a great idea, and highly inefficient, but I will post the code here for educational purposes (while telling you never to use this):
func _ready():
var items = find_all_items(get_tree().root, [])
for item in items:
print(item.name)
func find_all_items(node, list) :
var children = node.get_children()
for child in children:
if child is Item:
list.append(child)
for child in children:
find_all_items(child, list)
return list
One other way is to use groups. You can add all your items to a group called Item. You can do this by clicking on the Node tab on the far right and then click Groups and add a group called Item. This is better because there is less code and it should be more optimized in the engine (meaning it will be faster).
var items = get_tree().get_nodes_in_group("Item")
for item in items:
print(item.name)
However, this is still not a good idea. Because after you get the items (which could be a list of dozens or hundreds of items in a large game), you will need to do distance or collision checks on each one, which is usually one of the most computationally expensive calls you can make in a game. So this will be horribly slow. The list generation will be faster than the first method, but both suffer from manual distance checking, so will not scale to a real game, and should not be used unless it is a small demo or limited type of game.
What you want to do is use the physics and collision functions that are included with Godot. This is the main reason to use an off-the-shelf engine in the first place, so you can take advantage of highly optimized C++ written by experts and not cobble together inefficient solutions yourself (wasting your time, and making the game slow and buggy). There are many ways to do this in Godot, like using collision bodies, areas, ray casts, etc. There is no right answer here, as it will depend on your game.
However, if the items don't need physics, then using Area2D or Area would probably be best. These are fairly cheap, and you can detect overlap easily. This is the code to get the mouse over on an Area2D (and you can easily get clicks or whatever you want). This is the easiest and fastest method IMO.
extends Area2D
func _ready():
connect("mouse_entered", self, "mouse_enter")
func mouse_enter():
print(self.name)
I'm doing something like this, but I want the object to be in front of the player so I use a raycast. Then check if the collision is a item and print the name over it. I may use an area around the player to show all objects in a given area, but then I would maybe change their emission or something like that just for an aid in locating objects.
- Edited
You can use collision layers with an area. Specify a 'pickups' layer and have a proximity area on your character only have the pickup bit set in the collision layer.
Set the proximity area have no bits set in its own collision mask settings. This means it will only 'see' pickups and not interact with any thing in physics else wise.
collision mask sets which collision layer bits a (physics) object belongs to collision layer sets which physics object layer bits the object may 'receive' collisions from.