How can i turn this Kenney Starter kit into a infinte round based game

Godot Version

Godot 4.3 stable

Question

How can i turn this Kenney Starter kit into a infinite round based game
Kenney Starter Kit
`I tried already, how the game works is that when the enemy is “killed” then it removes it/ delets it. So I modified it so that it would spawn more enemys when all are gone. But when they spawn the second time I get this error in the enemy script.

Invalid access to property or key 'position' on a base object of type 'Nil'

This is how I am Creating them for round 2 in the main script

var scene = load("res://objects/enemy.tscn") var enimie = scene.instantiate() $Enemies.add_child(enimie)

Is there anything I am missing?

If you want to check how many enemies are in the scene, try assigning the enemies to a group of some kind. The function get_nodes_in_group(“group name”) will return an array of all the enemy nodes. you can just if the length of this array is equal to zero to determine when to spawn more.

I already have a way of checking them, but when they respawn they give a error

(How I got how many enemies there were)

Do you mind posting the error you get?

len() might be the wrong function to call.

enimies.is_empty() or enimies.size() == 0
might be better options to use.

I dont think the error is with the len or detcting the amount of enimies,
error:

Invalid access to property or key ‘position’ on a base object of type 'Nil

I see you’re correct.

Do you mind posting your entire script?

Since the error is pointing towards referencing the position of a ‘Nil’ object Id like to help you find the sequence that it making that happen.

It may be that the object has already been deleted from the scene or never existed in the first place.

Here is the code

extends Node3D

@export var player: Node3D

@onready var raycast = $RayCast
@onready var muzzle_a = $MuzzleA
@onready var muzzle_b = $MuzzleB

var health := 100
var time := 0.0
var target_position: Vector3
var destroyed := false

# When ready, save the initial position

func _ready():
	target_position = position


func _process(delta):
	self.look_at(player.position + Vector3(0, 0.5, 0), Vector3.UP, true)  # Look at player
	target_position.y += (cos(time * 5) * 1) * delta  # Sine movement (up and down)

	time += delta

	position = target_position

# Take damage from player

func damage(amount):
	Audio.play("sounds/enemy_hurt.ogg")

	health -= amount

	if health <= 0 and !destroyed:
		destroy()

# Destroy the enemy when out of health

func destroy():
	Audio.play("sounds/enemy_destroy.ogg")

	destroyed = true
	queue_free()

# Shoot when timer hits 0

func _on_timer_timeout():
	raycast.force_raycast_update()

	if raycast.is_colliding():
		var collider = raycast.get_collider()

		if collider.has_method("damage"):  # Raycast collides with player
			
			# Play muzzle flash animation(s)

			muzzle_a.frame = 0
			muzzle_a.play("default")
			muzzle_a.rotation_degrees.z = randf_range(-45, 45)

			muzzle_b.frame = 0
			muzzle_b.play("default")
			muzzle_b.rotation_degrees.z = randf_range(-45, 45)

			Audio.play("sounds/enemy_attack.ogg")

			collider.damage(5)  # Apply damage to player

the weird thing is, that the error occurs only when I respawn the enemys

Does the code point to which line of code the error occurs on?

My first thought would be to check if “$Enemies” is returning a node. It may be that the “Enemies” Node has been moved and is the using the wrong path in the Scene Tree.

print($Enemies)

Should output the Class Name or Node Type.

Next I would see if a position for the spawned enemy gets set after adding it to the Enemies Node as a child.

print(position)

Should output a Vector2/3.

Ok, ill try, where should i put the lines? i don’t think they get a position when spawned but that doesn’t explain the error.

I think it is your enemy scene that is the problem. Perhaps there is something your are not setting before you instantiate it?

Have you actually set the player in your export variable in the editor?
@export var player: Node3D

Yeah its in the script
image

I can upload the project files for you to look at if that would be helpfull

So @export will give you field to complete in the editor. Have you put your player node in there?

Screenshot 2024-10-09 153134

1 Like

Yes but i think that when they respwn (the enemies) they don’t have this set, is there a better way to set the player object in gdscript?

image

1 Like

It depends on if you have enemies you have added in script or enemies you have in the scene already. And yes, that export variable will be empty when you instantiate a new child from that scene.

If you are adding them in script you can set this in code. I don’t know where you are spawning enemies but something like:

# Reference to your player node
var PlayerNode: Node = $player

# When spawning set the player variable before adding child
func spawn_enemy():
	var NewEnemy = EnemyScene.instantiate()
	NewEnemy.player = PlayerNode
	add_child(NewEnemy)

Something like that at least. You only really need an export variable for something you are going to be changing constantly, like shield power or weapon type. For your player node, I would just have it as a normal var, so in your enemy set the place holder here:

var PlayerNode: Node

And then set that when you instatiate the enemy to be whatever your actual player node is.

Hope that helps and fixes the problem for you.

this is how i spawn the enimies


The only thing i dont know how to do is set the player to a object inside of the enemy script but the node tree looks like this,
image

How do i refrence a node higher in the tree?

get_node("../../Player") will let you access a node higher in the tree. Although it is not recommended because it will fail if you decided to move the player somewhere else in your SceneTree later.

A better practice amongst Godot developers would be todo what @pauldrewett suggested.

In your main script you can set the player variable after you instantiate the enemy.

var scene = load("res://objects/enemy.tscn")
var enimie = scene.instantiate()
enimie.player = $Player
$Enemies.add_child(enimie)

This is slightly better because your main script already has a reference to the player that the enemy can use to set its own variables.

How do i set the position of where it spawns?