How to access Childs in Parent Scene

Godot Version

Godot 4.3 [77dcf97d8]

Question

Hello my dear friends, I have a problem accessing my child nodes. I have an Scene called Enemy with the following code:

func _on_sceen_start():
	var y: int = starting_grid_position_y -1
	var x: int = starting_grid_position_x -1
	position = tile_list[y][x].position
	curren_tile_index = [y,x]
	current_tile = tile_list[y][x]
	pass_values_after_ready_to_childs()

func pass_values_after_ready_to_childs():
	print(get_child_count())
	var children = get_children()
	for child in children:
		if child.has_method('after_parent_fully_loaded'):
			child.after_parent_fully_loaded()

This Scene lives in the Main scene, that calls the _on_sceen_start function after it calls it _ready function to pass down information, that was not available on the child _ready functions

But the enemy do not find its Childs it say get_child_count() = 0
This is the hierarchy:
Main->Enemy Scence

Enemy->Sprite Node
Enemy->Movement Node

Do I need to register the children on enemy before? im not sure what’s wrong.

thanks for your help in advance!

Can you share a screenshot of the full Enemy script?

at the moment im refactoring so I hope its not too confusion, here is the whole script im breaking up, my goal is to separate everything move related into the movement component and for that I want to call the movement node from player but it do not find it.:

class_name Enemy
extends Node2D

@export var jumping_distance  :int
@export var jumping_high : int
@export var starting_grid_position_y :int
@export var starting_grid_position_x : int
@export var speed: int
@export var show_debug_tile: bool 

signal dmg_dealed(dmg: int)
var hit: bool =false

var input_allowed = false
var is_moved_in_current_tick = false

var timer_reset_miss_sing
var timer_started = false

var tile_list
var grid_size
var next_tile
var next_tile_index


var current_tile
var current_next_distance
var curren_tile_index
var position_before_movement

var left_movement = false
var right_movement = false
var down_movement = false
var up_movement = false

#pathfinding_vars

var player
var temp_player_tile_index
var relativ_offset_for_astar = [[-1,0],  # oben
								[1,0],   # unten
								[0,-1],  # links
								[0,1],   # rechts
								] 
var side_moves =[[-1,1],  # oben recht
				[-1,-1], # oben links
				[1,-1], # unten rechts
				[1,1]]
var calculated_astar_tile_map = []
var processed_tiles = []

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	pass
	
	
	
func _on_sceen_start():
	var y: int = starting_grid_position_y -1
	var x: int = starting_grid_position_x -1
	position = tile_list[y][x].position
	curren_tile_index = [y,x]
	current_tile = tile_list[y][x]
	pass_values_after_ready_to_childs()

func pass_values_after_ready_to_childs():
	print(get_child_count())
	var children = get_children()
	for child in children:
		if child.has_method('after_parent_fully_loaded'):
			child.after_parent_fully_loaded()
	
	
func _process(delta: float) -> void:
	calculate_path()
	if next_tile_index == player.curren_tile_index:
			
		attack(-1)
		return
				
	
	if calculated_astar_tile_map or next_tile and not is_moved_in_current_tick:
		calc_next_move()
		print("next tile index: ", next_tile_index)
		print("current path in _process: ", calculated_astar_tile_map)
				
		if next_tile != null:
			for x in range(speed):
				
				jump_animation_2()
		else:
			reset_inputs("_process")
	
	
#___________________________________________ATTACK______________________________

func attack(value):
	if not hit:
		hit=true
		dmg_dealed.emit(value)
		print("deals dmg: ",value)

#___________________________________________ATTACK_END__________________________
		
#___________________________________________MOVEMENT____________________________

func calc_next_move():
	
	if input_allowed:
		print('calc next tile')
		next_tile_index = calculated_astar_tile_map.pop_front()
		if next_tile_index != null:
			calculate_next_movement()
			input_allowed = false 
	else:
		print('dont calc next tile')


func calculate_next_movement():
	if next_tile_index != null:
		calc_direction(curren_tile_index,next_tile_index)
		next_tile = tile_list[next_tile_index[0]][next_tile_index[1]]
		
		position_before_movement = position
		current_next_distance = abs(next_tile.position-position)	

func calc_direction(current_index,next_index):

	var x :int
	var y :int
	if current_index[1] > next_index[1]:
		x = -1
	elif  current_index[1] < next_index[1]:
		x = 1
	else:
		x = 0
		
	
	if current_index[0] > next_index[0]:
		y = -1
	elif  current_index[0] < next_index[0]:
		y = 1
	else:
		y = 0
			
	print('next moving direction: ', [y,x])
	
	if [y,x] == [-1,0]:
		up_movement =true
	if [y,x] == [1,0]:
		down_movement =true
	if [y,x] == [0,-1]:
		left_movement= true
	if [y,x] ==[0,1]:
		right_movement=true
	

func jump_animation_2():
	
	if right_movement:
		print('move right')
		jump_tile_right()
		movement_animation_2()
	elif left_movement:
		print('move left')
		jump_tile_left()
		movement_animation_2()
	elif down_movement:
		print('move down')
		jump_tile_down()
	
	elif up_movement:
		print('move up')
		jump_tile_up()
	else:
		print('not allowed to mover')
	
func jump_tile_up():
	debug_jump()
	if position.y != next_tile.position.y:
		position.y -=jumping_distance
		position.x = next_tile.position.x
	else:
		curren_tile_index = next_tile_index	
		reset_inputs('jump_up_tile')
		
func jump_tile_down():
	debug_jump()
	if position.y != next_tile.position.y:
		position.y +=jumping_distance
		position.x = next_tile.position.x
	else:
		curren_tile_index = next_tile_index	
		reset_inputs('jump_down_tile')		
		
func jump_tile_right():
	debug_jump()
	if position.x != next_tile.position.x:
		position.x +=jumping_distance
	else:
		curren_tile_index = next_tile_index	
		reset_inputs('jump_right_tile')
		
func jump_tile_left():
	debug_jump()
	if position.x != next_tile.position.x:
		position.x -=jumping_distance
	else:
		curren_tile_index = next_tile_index	
		reset_inputs("jump_left_tile")

func debug_jump():
	print('current position x', position.x)
	print('next tile position x', next_tile.position.x)
	print('name current tile', self.name)
	print('next tile', next_tile.name)

func reset_inputs(called_by):
	print("reset input ,Called by ", called_by)

	hit = false
	left_movement = false
	right_movement = false
	up_movement = false
	down_movement = false

func movement_animation_2():
	
	if abs(next_tile.position-position) > (current_next_distance/2):
	
		print(abs(next_tile.position-position))
		print((current_next_distance/2))
		position.y -=jumping_high 
	else:

		if position_before_movement.y == position.y:
			pass
		else: 
			position.y+=jumping_high

#___________________________________________END MOVEMENT________________________

#___________________________________________PATHFINDING_________________________
func calculate_path():
	if player:
		if temp_player_tile_index != player.curren_tile_index:
			calculated_astar_tile_map = []
			processed_tiles = []
			astar_pathfinder(curren_tile_index,player.curren_tile_index, null, )
			temp_player_tile_index = player.curren_tile_index
			debug_path()


func debug_path():
	print("current path: ", calculated_astar_tile_map)

func astar_pathfinder(start_position, end_position, step_count_last_iteration):
	var current_step_count
	var g_value
	var h_value
	var f_value
	var cheapest_f_value
	var current_position_for_calculation = start_position
	
	if step_count_last_iteration == null:
		current_step_count = 0  
	else:
		current_step_count = step_count_last_iteration	
	
	if start_position==end_position:
		return
	## print("_____________________Evaluate_Position___________________")
	## print(start_position)
	## print("_____________________with relative position______________")
	## print(relativ_offset_for_astar)
	for relativ_position in relativ_offset_for_astar:
		
		var relativ_calc_position = calc_offset_position(
										current_position_for_calculation,
										relativ_position)
										
		if relativ_calc_position in processed_tiles:
			continue
			
		## print('____________________relative position_______________________')
		## print(relativ_calc_position)
		if tile_exists_and_walkable(relativ_calc_position):
			g_value = calc_g(current_step_count)
			h_value = calc_h(relativ_calc_position, end_position)
			f_value = g_value+h_value
			
			#var format_string = "g_value: %s , h_value: %s f_value: %s"
	#
			#var actual_string = format_string % [g_value, h_value,f_value]
			#
			## print(relativ_calc_position)
			## print(actual_string)
			processed_tiles.append(relativ_calc_position)
			if cheapest_f_value == null:
				cheapest_f_value = [relativ_calc_position, f_value]
			else:
				cheapest_f_value = evaluate_f_value(relativ_calc_position, 
													f_value, 
													cheapest_f_value)
			if show_debug_tile:
				debug_tile(relativ_calc_position,g_value,h_value,f_value)
	if cheapest_f_value != null:
		calculated_astar_tile_map.append(cheapest_f_value[0])
		astar_pathfinder(cheapest_f_value[0], end_position, g_value)
			

func evaluate_f_value(tile_position, f_value,  current_cheapstes_f_value):
		if f_value < current_cheapstes_f_value[1]:
			return  [tile_position,f_value]
		else:
			return current_cheapstes_f_value
			
		
func calc_h(current_pos, target)->int: # euclidian distance
	var euclid_distance = 0
	euclid_distance = (pow((current_pos[0]-target[0]), 2)+pow((current_pos[1]-target[1]),2))
	return euclid_distance
			
			
			
func calc_g(step_count)->int:
	step_count += 1 
	return 	step_count		


func debug_tile(tile_index,g_value,h_value,f_value):
	var tile = tile_list[tile_index[0]][tile_index[1]]
	var name_label = tile.get_child(1)
	var format_string = "%s, %s, \n %s \n %s"
	
	var actual_string = format_string % [g_value, h_value, f_value, tile_index]
	name_label.text = actual_string
	name_label.show()

	

func calc_offset_position(current_position, relative_position):
	var new_y = current_position[0] + relative_position[0]
	var new_x = current_position[1] + relative_position[1]
	return [new_y,new_x]
	
func tile_exists_and_walkable(position)-> bool:
	
	if 	position[0] < 0 or position[0] > grid_size[0]:
		return false	
	elif position[1] < 0 or position[1] > grid_size[1]:
		return false
	else:
		var tile = tile_list[position[0]][position[1]]
		if tile.is_movable():
			return true
		return false

#_______________________________END PATHFINDING________________________________
func _on_metronom_area_entered(area: Area2D) -> void:
	input_allowed = true
	is_moved_in_current_tick = false

	
	

func _on_metronom_area_exited(area: Area2D) -> void:
	input_allowed = false
	hit= false

I got an bug so I needed to upload the images in two separate replies.

Your _ready() function is empty and I don’t see any piece of code that calls the _on_sceen_start() function

That’s called from the main node after it’s ready. My understanding at the moment is, that every _ready function is called recursive from the node to the root. So I call after the root functions _ready is called the pass _values_after_ready_to_childs to give information, that could not be provided at the ready function of the Childs:

extends Node

@export var black_tile: PackedScene
@export var white_tile: PackedScene
@onready var game_metronom = $GameMetronom
@onready var player = $player

# Called when the node enters the scene tree for the first time.
var global_tile_list 
var grid_size

var current_enemy_count
func _ready() -> void:
	pass_values_after_ready_to_childs()


func pass_values_after_ready_to_childs():
	pass_to_player()
	pass_to_enemys()


func pass_to_enemys():
	var root = get_tree().root
	var enemys = get_tree().get_nodes_in_group("enemy")
	print(enemys)
	current_enemy_count = len(enemys)
	for enemy in enemys:
		enemy.tile_list = global_tile_list
		enemy.grid_size = grid_size
		enemy.timer_reset_miss_sing = game_metronom
		enemy.player = player
		enemy._on_sceen_start()		

func pass_to_player():
	player.tile_list = global_tile_list
	player.grid_size = grid_size
	player.timer_reset_miss_sing = game_metronom
	player._on_sceen_start()	
	
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	pass



func _on_terrrain_ready() -> void:
	global_tile_list = get_child(0).terrain_list
	grid_size = get_child(0).grid_size


	
func on_enemy_died():
	current_enemy_count -=1

func on_enemy_created():
	current_enemy_count +=1

and it gets called, in the the debugger I can place a breakpoint and it stops there but the get_children() call of enemy do not return the children.

Found the error: somehow it was a sprite 2d not a 2d node anymore, I created a second enemy and it worked:

1 Like