How to shoot a 3d arrow

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By ObsidianWhisper

I’m having a hard time wrapping my brain around how to code shooting an arrow with an arc. There have been plenty of tutorials for how to do this in 2d, but I haven’t found one that explains for 3d and I can’t figure out how to translate between the two in this case.

I have the attacker and the target’s translations. My current version uses move_and_collide

The attacker instances a kinematic arrow (don’t know which physics body to use) and should pass it the initial angle. The arrow has an initial force(?) or velocity vector.

I can’t figure out how to calculate the initial angle in 3d. Including the possibility of differing heights.
I also know that there should be 2 possible angles as well. I’d also like the arrow to point in the direction of travel, which again is a 3d angle I can’t figure out.

This is my first 3d project using physics so I’m pretty much in the dark on this one and frustrated that this shouldn’t be terribly difficult math. It’s just that I’ve forgotten it all.

Any assistance would be greatly appreciated.

:bust_in_silhouette: Reply From: ObsidianWhisper

Alright, so I figured it out. First, terminology is important. I should have been searching for Projectile motion. Secondly, having an understanding of transforms was what I lacked.

I’m going to post the code I cobbled together to make it work for anyone else who runs into the same problem.

extends KinematicBody

# force is the forward movement of the projectile
export var force = 20.0
export var g = Vector3.DOWN * 9.8
# t was just to keep track of time
var t = 0
# speed is just to make things move faster
export var speed = 1

var velocity = Vector3()

# init_xy is the initial upward angle while init_xz is the angle pointing toward the target.
var init_xy = 0
var init_xz = 0

# I initialized the angles beforehand in a different scene so all I had to do was
# add the arrow to the tree for it to shoot.
func _ready():
	rotate_x(init_xy)
	rotate_y(init_xz)
	velocity += -transform.basis.z * force

# Just to keep time and free the arrow after a bit.
func _process(delta):
	if t > 10:
		queue_free()
	t += delta

func _physics_process(delta):
    # Add gravity
	velocity += g * delta * speed
    # This makes the arrow point in the direction of travel, like in real life.
	look_at(transform.origin + velocity.normalized(), Vector3.UP)
    # pretty much does the work for you. Although this might be different if you aren't
    # using a KinematicBody.
	move_and_collide(velocity*delta*speed)

You can find the math for the initial angles pretty easily. I hope this saves someone a lot of head ache.