Godot Version
4.2.1
Question
Hello fellow Godoteers! I’m working on a 3D game where I have a vehicle that when it enters a specific Area3D the main_world.gd
triggers a function to instantiate a “3 zombies and a road block” scene . However, I am encountering an issue with the instantiated nodes within the scene not having access to a vehicle reference, resulting in null errors for the zombie FSM (target_in_range) checks.
If I manually move a zombie into the scene everything works fine, which leads me to believe that the instantiated zombie’s do not “see” or have a reference for the vehicle even though it is setup in their script.
For context, I have main_world.gd
script attached to the parent node of the scene. When the vehicle enters an Area3D
, it triggers the _on_zombie_spawner_area_3d_vehicle_entered_zombie_spawner
function. Inside this function, I instantiate zombie_road_block
scenes using zombie_road_block_scene.instantiate()
.
I needed to use @onready var vehicle = $"../Vehicle/vehicle"
for the zombie.gd because the zombie was referencing the vehicle node that contained the model, and I changed it so now it uses the model global position.
@onready var vehicle = $Vehicle
is the scene node that can been seen in the structure screenshot below.
I have tried to set a global variable and “hard code” the vehicle variable but that breaks more things than it fixes so I need help rethinking about what solution I should use and how to set it up.
When func _on_zombie_spawner_area_3d_vehicle_entered_zombie_spawner():
is called the only print that makes it to output is _on_zombie_spawner_area_3d_vehicle_entered_zombie_spawner called
. The later prints get interrupted by the null instance errors.
What additional information can I provide?
Thank you in advance for your help! Cheers!
zombie.gd
# zombie.gd
extends CharacterBody3D
@export_group("Zombie Stats")
@export var movement_speed: float = 4.0
@export var Health = 1
##Zombie was referencing the vehicle node that contained the model, changed so now it uses the model global position.
@onready var vehicle = $"../Vehicle/vehicle"
@onready var nav_agent: NavigationAgent3D = get_node("NavigationAgent3D")
@onready var sfx_zombie_run_over = $"../sfx_zombie_run_over"
@onready var zombies_killed_label = get_node("hud/Score/Zombies_Killed/zombies_killed_label")
@onready var idle_Label3D = $"idle_Label3D"
@onready var pursue_Label3D = $"pursue_Label3D"
@onready var attacking_Label3D = $"attacking_Label3D"
signal zombie_killed
enum State {IDLE, PURSUE, MELEE_ATTACK}
var current_state: int = State.IDLE
func _physics_process(delta):
process_state(delta)
func process_state(delta):
match current_state:
State.IDLE:
idle_Label3D.visible = true
pursue_Label3D.visible = false
attacking_Label3D.visible = false
if is_target_in_range(10.0):
current_state = State.PURSUE
State.PURSUE:
idle_Label3D.visible = false
pursue_Label3D.visible = true
attacking_Label3D.visible = false
move_towards_target(delta)
if is_target_in_attack_range():
current_state = State.MELEE_ATTACK
State.MELEE_ATTACK:
idle_Label3D.visible = false
pursue_Label3D.visible = false
attacking_Label3D.visible = true
if is_target_in_attack_range():
melee_attack()
else:
current_state = State.PURSUE
func is_target_in_range(range: float) -> bool:
var distance_to_target = global_transform.origin.distance_to(vehicle.global_transform.origin)
return distance_to_target <= range
func is_target_in_attack_range() -> bool:
var distance_to_target = global_transform.origin.distance_to(vehicle.global_transform.origin)
return distance_to_target <= 3.0 # Adjust the attack range as needed
func move_towards_target(delta):
var direction = vehicle.global_transform.origin - global_transform.origin
direction = direction.normalized()
velocity = lerp(velocity, direction * movement_speed, delta)
move_and_slide()
main_world.gd
# main_world.gd
extends Node3D
@onready var vehicle = $Vehicle
var zombie_road_block_scene = preload("res://assets/zombie_road_block.tscn")
func hud_increase_zombie_killing_count_method():
$hud._on_zombie_killed()
func _physics_process(delta):
get_tree().call_group("Enemies", "update_target_location", vehicle.global_transform.origin)
func _on_zombie_spawner_area_3d_vehicle_entered_zombie_spawner():
print("_on_zombie_spawner_area_3d_vehicle_entered_zombie_spawner called")
# Spawn road blocks
var progress_step = 1.0 / 10.0 # Divide the range of progress ratios into 10 equal parts
var current_progress = 0.0 # Start at the beginning of the path
for i in range(10): # Spawn 10 road blocks
var zombie_road_block = zombie_road_block_scene.instantiate()
print("Spawned road block at position: ", zombie_road_block.global_transform.origin)
var road_block_spawn_location = get_node_or_null("ZombieSpawnPath/ZombieSpawnLocation")
if road_block_spawn_location:
print("Spawn location found: ", road_block_spawn_location.global_transform.origin)
else:
print("Spawn location not found!")
# Assign a progress ratio from one of the 10 equal parts with some randomness
road_block_spawn_location.progress_ratio = current_progress + randf() * progress_step * 0.5
# Add a small random offset
var offset_x = randf_range(-0.1, 0.1)
var offset_z = randf_range(-0.1, 0.1)
road_block_spawn_location.position.x += offset_x
road_block_spawn_location.position.z += offset_z
# Generate a random rotation angle in degrees, excluding 330-30, 60-120, 150-210, and 240-300 degrees
var random_rotation_degrees
while true:
random_rotation_degrees = randi() % 360
if (random_rotation_degrees >= 30 and random_rotation_degrees < 60) or \
(random_rotation_degrees >= 120 and random_rotation_degrees < 150) or \
(random_rotation_degrees >= 210 and random_rotation_degrees < 240) or \
(random_rotation_degrees >= 300 and random_rotation_degrees < 330):
break
# Convert the rotation angle to radians
var random_rotation = deg_to_rad(random_rotation_degrees)
# Create a Transform3D with the random rotation
var transform = Transform3D()
transform = transform.rotated(Vector3.UP, random_rotation)
transform.origin = road_block_spawn_location.global_transform.origin
zombie_road_block.global_transform = transform
add_child(zombie_road_block)
# Move to the next part of the progress range for the next road block
current_progress += progress_step