Top down 3d shooter, bullets dont track mouse

Godot Version

` 4.2

Question

here is my code for the bullet

var camera = get_viewport().get_camera_3d()
var mouse_pos_2d = get_viewport().get_mouse_position()

# Get a ray from the camera through the mouse position
var from = camera.project_ray_origin(mouse_pos_2d)
var to = from + camera.project_ray_normal(mouse_pos_2d) * 1000  # Extend far

# Define a fixed shooting height (adjust based on your game world)
var shooting_plane_height = global_position.y  # Keeps bullet at its spawn height

# Define a horizontal plane at the bullet's height
var shooting_plane = Plane(Vector3.UP, shooting_plane_height)
var mouse_pos = shooting_plane.intersects_ray(from, to)

if mouse_pos:
    direction = (mouse_pos - global_position).normalized()
    
else:
    direction = Vector3.FORWARD  # Default forward if intersection fails

print("Mouse Pos:", mouse_pos_2d)
print("Bullet Pos:", global_position)
print("Direction:", direction)
print("shootingplane", shooting_plane_height)”

player code is

extends CharacterBody3D
@onready var anim = $AnimationPlayer
@onready var bullet_scene = preload(“res://bullet.tscn”)
var speed = 100

func _physics_process(delta):
velocity.x = 0
velocity.z = 0
if Input.is_action_just_pressed(“click”):
var bullet_instance=bullet_scene.instantiate()
add_child(bullet_instance)
bullet_instance.global_position = global_position + Vector3(0, 0.5, 0)

if Input.is_action_pressed("ui_down"):
    velocity.z = speed * delta
    anim.play("walkz")

if Input.is_action_pressed("ui_up"):
    velocity.z = -speed * delta
    anim.play("walkz")

if Input.is_action_pressed("ui_left"):
    velocity.x = -speed * delta
    anim.play("walkx")
    

if Input.is_action_pressed("ui_right"):
    velocity.x = speed * delta
    anim.play("walkx2")
    

move_and_slide()

The problem is the my bullet code uses a raycast from the camera to the floor to figure out my mouse position

when the bullet spawn height is 0 0 0 the bullet shoots to the mouse position, this is the intended behavior

the problem is when i make the spawn height of the bullet 0,5,0 the bullet is now offset and will not travel to the mouse location.

I need a way to fix this.

see the video for more:

My suggestion:

  • calculate the position on the floor like you did before
  • calculate the direction from the player’s feet to the target position as you did before, and build your motion vector off that
  • set the initial position of the bullet at the height you want it to fly

If you do this, your bullets should travel parallel to the floor.

You’ll’ probably want a further tweak; if you do the above, the mouse will need to click at the feet of the zombie to hit it; clicking on the body of the zombie will effectively project the mouse back to wherever it would have hit the floor if the zombie wasn’t in the way.

So you may actually want to collide the mouse with an invisible plane that’s offset from the floor, or even consider object/mouse collision tests.

this isn’t the full bullet code, right? I can’t see the movement code.

good catch

func _process(delta):
velocity = direction * SPEED * delta
move_and_slide()

func _on_area_3d_body_entered(body):
if body.is_in_group(“baddies”):
body.queue_free()

im not 100 percent sure what your saying could you be more clear

the addition vector depend on the camera angle to the ground can you try

var camera = get_viewport().get_camera_3d()
var mouse_pos_2d = get_viewport().get_mouse_position()

# Get a ray from the camera through the mouse position
var from = camera.project_ray_origin(mouse_pos_2d)
var to = from + camera.project_ray_normal(mouse_pos_2d) * 1000  # Extend far

# Define a fixed shooting height (adjust based on your game world)
var shooting_plane_height = global_position.y  # Keeps bullet at its spawn height

# Define a horizontal plane at the bullet's height
var shooting_plane = Plane(Vector3.UP, shooting_plane_height)
var mouse_pos = (shooting_plane.intersects_ray(from, to)) + vector3(0,0.5,0.5)

if mouse_pos:
    direction = (mouse_pos - global_position).normalized()
    
else:
    direction = Vector3.FORWARD  # Default forward if intersection fails

print("Mouse Pos:", mouse_pos_2d)
print("Bullet Pos:", global_position)
print("Direction:", direction)
print("shootingplane", shooting_plane_height)”

Your code worked before, except everything was on the ground plane, right? As in, if you clicked on the target’s feet, a shot would go from the player’s feet to the target’s feet.

So, you could keep the old code, but when you spawn the shot, spawn it a meter above the floor, and have it move towards a point a meter above the target.

Alternately, you could do all your calculations on an XZ plane that’s 1m above the floor. That would let the player click the middle of a target to hit it rather than the feet.

Another option would be to do something like:

func player_shoot(screen_pos: Vec2):

    if collide_mouse_against_targets(screen_pos):
        fire_shot_at(target)
    else:
        fire_shot_at_ground(screen_pos)