Help! Invalid call. Nonexistent function 'velocity_toward' in base 'Nil'

Help, I have been working on a topdown shooter and I have been working on level design and I have put down more than one enemy and i get this error: Invalid call. Nonexistent function ‘velocity_toward’ in base ‘Nil’, here is the code for the enemy ai:

extends Node2D

signal state_changed(new_state)

enum State {
	PATROL,
	ENGAGE
}

@onready var player_detection_zone = $playerdetectionzone
@onready var patrol_timer = $patroltimer

var current_state: int = -1 : set = set_state
var actor: CharacterBody2D = null
var player: Player = null
var weapon: Weapon = null

var origin: Vector2 = Vector2.ZERO 
var patrol_location: Vector2 = Vector2.ZERO
var patrol_location_reached := false
var actor_velocity: Vector2 = Vector2.ZERO


func _ready() -> void:
	set_state(State.PATROL)



func _process(delta: float) -> void:
	match current_state:
		State.PATROL:
			if not patrol_location_reached:
				actor.rotation = lerp(actor.rotation, actor.global_position.direction_to(patrol_location).angle(), 0.1)
				actor.velocity = actor_velocity
				actor.move_and_slide()
				actor.rotate_toward(patrol_location)
				if actor.global_position.distance_to(patrol_location) < 5:
					patrol_location_reached = true
					actor_velocity = Vector2.ZERO
					patrol_timer.start()
		State.ENGAGE:
			if player != null and weapon != null:
				var angle_to_player = actor.global_position.direction_to(player.global_position).angle()
				actor.rotate_toward(player.global_position)
				if abs(actor.rotation - angle_to_player) < 0.1:
					weapon.shoot()
			else:
				print('In the engage state but no weapon or player')
		_:
			print("ERROR: FOUND A STATE FOR OUR ENEMY THAT SHOULD NOT EXIST")

func initialize(actor, weapon: Weapon):
	self.actor = actor
	self.weapon = weapon

func set_state(new_state: int):
	if new_state == current_state:
		return
	
	if new_state == State.PATROL:
		origin = global_position
		patrol_timer.start()
		patrol_location_reached = true
	
	current_state = new_state
	emit_signal("state_changed", current_state)

func _on_playerdetectionzone_body_entered(body: Node) -> void:
	if body.is_in_group("player"):
		set_state(State.ENGAGE)
		player = body

func _on_playerdetectionzone_body_exited(body) -> void:
	if player and body == player:
		set_state(State.PATROL)
		player = null

func _on_patroltimer_timeout() -> void:
	var patrol_range = 50
	var random_x = randf_range(-patrol_range, patrol_range)
	var random_y = randf_range(-patrol_range, patrol_range)
	patrol_location = Vector2(random_x, random_y) + origin
	patrol_location_reached = false
	actor_velocity = actor.velocity_toward(patrol_location)

here is the code for the enemy itself:

extends CharacterBody2D

signal killed(points)

@onready var health_stat = $Health
@onready var ai = $AI
@onready var weapon = $weapon

@export var speed: int = 100
@export var points: int = 100

func _ready() -> void:
	ai.initialize(self, weapon)

func rotate_toward(location: Vector2):
	rotation = lerp(rotation, global_position.direction_to(location).angle(), 0.1)

func velocity_toward(location: Vector2) -> Vector2:
	return global_position.direction_to(location) * speed

func handle_hit():
	health_stat.health -= 20
	if health_stat.health <= 0:
		killed.emit(points)
		queue_free()

i don’t know what’s happening, when there is one enemy it works fine, but when there is multiple, it crashes.

here is a screenshot:

i have been following a bunch of tutorials for godot 3 and i have been using godot 4. help would be appreciated, thank you !

I would put a safety check here.

If ( is_instance_valid(self.actor) )
  actor_velocity = actor.velocity_toward(patrol_location)

I suspect your timer could go off if an ai isn’t initialized, or actor could be deleted from underneath it.

Since the AI is a child, it will call ready and start it’s patrol timer, during ready set_state(patrol), before the enemy parent node calls initialized.

I would maybe change the call on set_state to when enemy calls initialize, as another possible solution.

I am so sorry to bother you, but now I get the error: Invalid get index ‘rotation’ (on base: ‘Nil’). Do you have a possible solution to this?

Get rid of this above.

And add it here

Also

func _process(delta: float) -> void:
  if ( not is_instance_valid(self.actor) )
     return
  match current_state:
    ...

Or you could disable processing until initialized

I am really sorry but now only one enemy on the level functions on the level and the rest are still and don’t take bullets.

here is a screenshot in case it helps:

As far as I can tell from what you have provided handle_hit is not called.

But hey your no longer calling a nill object :wink:

Thank you for your help, and you’re right, at least that error has been fixed
:slight_smile:

I don’t know why your other agents aren’t moving. Are they really an enemy class?

As far as I can tell, if they were enemy class it should all work the same… unless they have different settings?

It’s all good now I fixed it. It turns out that the enemy’s actual script wasn’t connected to the enemy, and that’s why only one of the enemies were moving.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.