Raycast points down

Godot Version

Godot 4.5.1

Question

I am currently trying to code an enemy that fires at the player

The enemy works with a node for the launcher and the bullet.

Both nodes have a raycast

But for some reason the bullet raycast always faces down with my code not even insinuating to do this and the transform not even being set down, so the bullet clips to the floor instead of firing at the player

I cannot work out why on earth this is happening because frankly it makes no sense

Launcher code:

extends Node3D

const bullet = preload("res://bullet.tscn")

@onready var soldier = $".."
@onready var timer: Timer = $Timer
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	if soldier in get_tree().get_nodes_in_group("canShoot"):
		timer.start(0.1)
		var fire = bullet.instantiate()
		add_child(fire)
		fire.global_transform = global_transform
		if Input.is_action_just_pressed("check"):
			print(global_rotation)

Bullet code:

extends RayCast3D

@export var Speed : float = 50.0
var debug = 0
@onready var timer = $Timer
@onready var despawn = $Timer2

func _ready() -> void:
	timer.start(0.1)
func _physics_process(delta: float) -> void:
	#print("Fire")
	if not is_colliding():
		position += global_basis * Vector3.FORWARD * Speed * delta
		#target_position = Vector3.FORWARD * Speed * delta
		target_position = Vector3.FORWARD * Speed * delta
		force_raycast_update()
		var collider = get_collider()
		if is_colliding():
			if is_colliding():
				global_position = get_collision_point()
				#cleanup()
				set_physics_process(false)
			

Thank you

1 Like

It looks like the target position is only changed if the raycast isn’t already colliding with something. Is it possible it’s colliding with something when it’s added, so the code to change the target position never runs?

1 Like

No, I had a print function there and when I jump it’s not colliding

So I see three issues:

  1. Your bullet is being made a child of the launcher instead of the scene. That means that when it is created, it is going to be attached to the launcher, and affected by its movement. It needs to be added to the level scene. (I think you haven’t encountered this problem yet.)
  2. You are relying on the parent of the bullet, the launcher, to determine global position when you create it. However, you are creating it in its default rotation and scale. You are then attempting to change position, rotation and scale after it has already been added to the scene. Which means its _physics_process() function may have already run and moved the bullet before you update it. Which means that it’s going to test everything based on it original position, scale and rotation, before it’s actually turned to face the direction you want. Try moving the transform assignment to before you add it to the tree.
  3. You are assuming that your gun is rotating so that the barrel is facing towards -z at all times. This may be true, but it may not be true, depending on how you are applying rotation and/or transforms to the player when you move it.

I would recommend adding a Marker3D to the end of the barrel in line with center point of the gun model, so that drawing a vector from the launcher to the Marker3D creates a vector that points in the direction you want to point the bullet. Then rotate the bullet to face that vector before adding it to the level scene.

this is running every frame.

you start the timer but don’t do anything with it. usually we check if the timer has stopped before firing the next bullet to make the delay (called rate of fire).
another thing to do is await for timeout:

timer.start(0.1)
await timer.timeout

this halts execution of the code until the timer finishes.

you can put this in _input since it’s for debugging. _process is tied to the current frame.

you can just set your timer to autostart on the node.

I hope you copied the indentations wrong, because this code doesn’t have an alternative execution path (else/elif), so it’s not going to do anything if it does collide.

there is no collision, this should go under is_colliding(), not not is_colliding()

you are calling the same method twice.

as pointed out, bullet is a child of the emiter.
either:
1 - make bullet top_level and assign rotation, which you are already doing when assigning global_transform. just go to the node and enable top_level under Node3D.
2 - add the bullet somewhere else. you can use add_sibling() to add it parallel to the emiter so it doesn’t copy the transforms, or get_tree() to add it globaly, or store a reference to a node containing all projectiles.

erase the node once it’s not used anymore, you are going to accumulate a trillion nodes that do nothing.
to delete a node, call queue_free() on the node script.

1 Like

I did this but now no bullets spawn at all.

That was an error. I must have switched things around at some point and not noticed that misplacement

Maybe because you are running it on _process, so the signal is sent at a frame that is skipped by _process.
I’ve used await on one time things inside a for loop, that way code can be run multiple times at an interval.

for your case, maybe your shooting code should be tied to the timeout signal instead:


func _ready() -> void:
	timer.timeout.connect(fire)

func fire() -> void:
	var fire_projectile = bullet.instantiate()
	add_child(fire_projectile)
	fire_projectile.global_transform = global_transform

this connects the timeout of your timer to the fire function, the fire function spawns the bullet.
everytime the timer ends it fires. all you would have to do then is set the timer to 0.1 in the inspector.

1 Like

Still not firing anything

I’m now getting an error;

Invalid call. Nonexistent function ‘add_child’ in base 'PackedScene

You need to instantiate the PackedScene, like in @jesusmora 's example.

I did?

I’ve always had an instantiate?

It’s not complaining about the argument of add_child(), but from what object add_child() is called. Something like this:

var scene = preload(...)

...
	scene.add_child(...)

should cause the error message you have.

Instead of the packed scene, add_child() needs to be called an any node. I would usually go with get_tree().current_scene.add_child(...).

That stopped the error, thanks but now the bullet spawns in and doesn’t move and despawns

Maybe it instantly collides with something (e. g. the enemy that spawned it)? You could try to print the collider, to see when and with what it collides.

Good theory, I’ll look into that