FPS Rigid Body Projectile

Godot Version

master branch build commit 51991e20143a39e9ef0107163eaf283ca0a761ea
Also tried in the 4.2 branch

Question

I am trying to fire a rigid body projectile using

var inst = bullet.instantiate()
inst.global_transform = camera.global_transform
inst.global_transform.origin += camera.global_transform.basis.z * 2 #to move the projectile outside of the player collision sphere (2 is probably too high)
inst.apply_central_impulse(camera.transform.basis.z * 50)
#get_tree().get_root().add_child(inst)
add_child(inst)

using get_tree().get_root().add_child(inst) has the projectile firing from 0,0,0 at odd angles depending on camera orientation. (aiming up sends it down, aiming down sends it up).
Using add_child(), which seems wrong, spawms the projectile near the player and fires it but still does not go where i am aiming.

I referenced: Shooting in 3D, vector math problem - #2 by system

the camera variable is coming from my fps player controller in

@onready var camera: Camera3D = $Camera

Which is working properly since camera is used to rotate the camera with the mouse.

I feel like i am missing something simple, in unreal c++ i use

const FRotator SpawnRotation = PlayerController->PlayerCameraManager->GetCameraRotation();
const FVector SpawnLocation = GetOwner()->GetActorLocation() + SpawnRotation.RotateVector(MuzzleOffset);

Which i wasn’t able to translate to godot

I haven’t tried your code, but I think I understand your problem.

I can recommend checking out this video by Chaff Games: https://www.youtube.com/watch?v=ZaEzjnoIy3M, especially the part about Projectile Weapons.

To summarize their approach (which I’ve also used and works great in my opinion):
You want to use your camera to create a raycast, which in turn gets you a destination point (either at the end of the raycast or the point it collides with an object).

Then you use that destination point and your projectile spawn point to create a directional vector, in pseudocode something like:
direction_vector = destination_point - projectile_spawn_point).normalized()

which you can then apply to your projectile to shoot it:
projectile.set_linear_velocity(direction_vector * projectile_velocity)

(I use set_linear_velocity instead of apply_central_impulse )

Hope this gives helps you out a bit.

1 Like

I appreciate the link and the tips!

I got it working by using a similar concept to what you described:

var inst = bullet.instantiate()
get_tree().get_root().add_child(inst)
			
var start = camera.project_ray_origin(get_viewport().get_size()/2)
var end = start + camera.project_ray_normal(get_viewport().get_size()/2) * 2
var direction = (end - camera.global_transform.origin).normalized()
		
inst.global_transform.origin = end #use the end of the rsycast for the muzzle offset/projectile start location
inst.apply_central_impulse(direction * 50)

This seems to fire a projectile perfectly. Although if the velocity is high it seems to miss collisions even with CCD and with physics ticks at 240

1 Like

I was actually able to get the original code working as well by doing this:

var inst = bullet.instantiate()
get_tree().get_root().add_child(inst)
inst.global_transform.origin = camera.global_transform.origin + (camera.global_transform.basis.z * -2)
inst.apply_central_impulse(camera.global_transform.basis.z * 250 * -1)

negative global_transform.basis.z was what i needed. also did not need to set

inst.global_transform = camera.global_transform

Both the raycast way and this way have the same final result although id assume the math on the transforms is cheaper then raycasting from a 2d point in the viewport. Not sure though.

Thanks!

1 Like