Hello everyone! I haven't done much work when it comes to Arrays so I would like some advice on how to approach this setup; I want to have an Array that grabs a list of 2D Nodes (instanced scenes in a level under a YSort node) and from this list of objects pick randomly from a specified amount and run a function present in those nodes (they all use the same script).

In short; I have several objects in my level that can "break" from a function in their script. I want to specify an amount of these objects to randomly "break" upon starting the level.

Thanks!

You can make the mechanism that you propose work.


Your use case:

I admit that I have no idea what kind of a scene you are using to build your nodes... so we'll use an example of a scene that I've created:

res://scenes/Npc.tscn

extends Sprite

var stats = null
var dialogue = null
var char_name = null

func _ready():	
	pass

func init_as(npc_name):
	#  Npc has a child node called "Label", so set that to the npc_name
	get_node("Label").text = npc_name 
	char_name = npc_name
	if(npc_name=="Fred"):
		dialogue = "Hi, I'm Fred!  Listen as I rant."
	elif(npc_name=="Thompson"):
		dialogue = "Hi, I'm Thompson!  Hear me roar!"
	elif(npc_name=="Aldebert"):
		dialogue = "Hi, I'm Aldebert.  Watch me run!"
	elif(npc_name=="Craig"):
		dialogue = "Hi, I'm Craig!  I do *NOT* wet the bed!"
		# ... uhh... nobody ever said you did... : (

func get_dialogue():
	return dialogue

func get_npc_name():
	return char_name

Get random npc from array:

onready var npc_scene = preload("res://scenes/Npc.tscn")

func get_random_npc_dialogue():
	var npc_array = []
	
	var npc1 = npc_scene.instance()
	npc1.init_as("Fred")
	npc_array.add(npc1)

	var npc2 = npc_scene.instance()
	npc2.init_as("Aldebert")
	npc_array.add(npc2)

	var npc3 = npc_scene.instance()
	npc3.init_as("Thompson")
	npc_array.add(npc3)

	var npc4 = npc_scene.instance()
	npc4.init_as("Craig")
	npc_array.add(npc4)

	var array_size = npc_array.size()
	
	randomize()

	var index = randi()%array_size
	var rand_npc = npc_array[index]
	
	print(rand_npc.get_dialogue)

All of this should satisfy most of the conditions you presented, except for this condition:

"In short; I have several objects in my level that can "break" from a function in their script. I want to specify an amount of these objects to randomly "break" upon starting the level."

Mostly because I couldn't figure out what you meant by "break" from a function; but the above should give you a general idea.

--- My use case:

Below is an example where I have used an array of data definitions to build and tear down nodes in a scene:

The purpose of the code below is to grab a list of "exit_definition" data (these are NOT nodes) and to build "Exit" nodes from the 'Exit.tscn' scene.

exit_definition.gd

extends Node

var x = 10
var y = 10

var exit_name = "genericExit"
var entrant_name = "genericEntrant"
var entrant_area = "genericArea"

tear_down_exits routine:

	#  area.clean_exits provides an array of "exit_definition" data
	for exit in area.clean_exits:
		var node = get_node(exit.exit_name)
		remove_child(node)

build_exits routine:

	onready var exit_scene = preload("res://scenes/Exit.tscn")
	
	#  area.exits provides an array of "exit_definition" data.
	self.exits = area.exits
	for exit in area.exits:
		#  Note that 'this_exit' is a node, 'exit' is an 'exit_definition'... which can not be created as a node
		var this_exit = exit_scene.instance()
		this_exit.set_name(exit.exit_name)
		add_child(this_exit)
		this_exit.translate(Vector2(exit.x, exit.y))

Note the "set_name". I recommend that you use a predictable naming convention when you initialize your nodes (this will save you from some frustration and heartache in the future).

I have made a class called PickingBag which I find most useful for these kind of scenarios. You start by adding any amount and any kind of objects to the bag. Then you can randomly pick an object from the bag, which is then also removed from the bag so it can't be picked again.

extends Reference
class_name PickingBag

var _object = []

func remove(an_object):
	_object.erase(an_object)
	
func peek():
	if not _object.empty():
		var r = randi() % _object.size()
		return _object[r]
	return null
	
func add(an_object):
	_object.append(an_object)
	
func pick():
	if not _object.empty():
		var r = randi() % _object.size()
		var o = _object[r]
		_object.remove(r)
		return o
	return null
	
func is_empty():
	return _object.empty()

To use it in your case, you probably would write something like this:

	# first put all your nodes into the bag
	var bag = PickingBag.new()
	for o in YourYSortNode.get_children():
		bag.add(o)
	
	# then create an empty result array
	var result = []
	
	# randomly pick from the bag
	# the required amount of nodes
	# and put in result
	for i in range(YourRequiredAmount):
		var picked_node = bag.pick()
		result.append(picked_node)

Now you have an array of randomly selected nodes to use:

	for o in result:
		o.DoYourBreakingLogic()

Hope this helps!

Huge thanks for the help! How would one go about clearing the array each time a level starts up? Right now it always have the same pieces every time I try it on any level. I've tried a few things but can't get it to work.

Clearing an array is as easy as calling clear() on the array:

your_array.clear()


Here's everything you need to know about arrays: docs.godotengine.org/en/3.2/classes/class_array.html

To get a different selection of nodes each time, call

randomize()

somewhere in your game's initialization code.

EDIT: Maybe you'll need to call randomize() every time you load a new level.

2 years later