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.

1 Like
: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.

1 Like

Hi! I came across to your post and before I was using a RigidBody with apply_force and indeed “gravity” doesn’t seam to work on the same way, because my arrows were descending faster than they were supposed to. Any reason why one works and the other not?

I look more intuitive to use a RigidBody than a CharacterBody

You can adjust the gravity in the project settings if you think the default values look bad in your project.

I solve it that is not the problem. You to make damp Mode to replace so that velocity component X and Z don’t decrease.

image

So in that way you can do something like this:

func fire(direction) -> void:
	if direction:
		initial_direction = direction  
	else:
		initial_direction = Vector3.FORWARD
	apply_impulse(initial_direction * speed)
	
func _integrate_forces(state):
	if !stop:
		look_at(global_transform.origin + linear_velocity, Vector3.UP)

They do exactly the something, but you are using rigid bodies has intended and you can land an arrow on the target stop with and acceptable error (approx. 1-2m).
Because you only want to decrease Y component (gravity).