Projectile Parabola Motion and Rotation (Arrows fly on an arc)

EDIT - Mihai from the future here - You can find an updated version of this in this topic. It incorporates most of the changes in the next videos (ex:fireball / homing / garbage collection )
Updated - Projectile Parabola Motion and Rotation (Arrows fly on an arc)

If anyone is interested, here is how I was able to implement a Parabola and Rotation to my arrows. I’m not gonna lie :rofl: - I have no idea how this works… the maths are wayyyyy beyond my comprehension.

For anyone interested in how this works here is where I “borrowed” the Launch function from - Projectile Motion Tutorial for Arrows and Missiles in Unity3D – Volkan Ilbeyli – Graphics Programmer

Here’s how it looks in action

And here’s how my Projectile.cs looks like. I’ve also added a RigidBody to my projectile Prefab for this to work.

public class Projectile : MonoBehaviour
{ 

    [SerializeField] private float speed = 0.4f;
    private Health target = null;
    private float damage = 0f;
    private Vector3 initialPosition;
    private Quaternion initialRotation;
    private float launchAngle = 20f;
    private Rigidbody rigidbody;

    private void Start()
    {
        rigidbody = GetComponent<Rigidbody>();
        initialPosition = transform.position;
        initialRotation = transform.rotation;
    }

    void Update()
    {
        if(target==null) return;
        transform.LookAt(GetAimLocation());
        //transform.Translate(Vector3.forward*speed*Time.deltaTime);
        Launch();
    }

    public void SetTarget(Health target,float damage)
    {
        this.target = target;
        this.damage = damage;
    }

    private Vector3 GetAimLocation()
    {

        Vector3 aimLocation = new Vector3(target.GetComponent<Collider>().bounds.center.x, target.GetComponent<Collider>().bounds.center.y,
            target.GetComponent<Collider>().bounds.center.z);

        return aimLocation;
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.GetComponent<Health>()!=target) return;
        target.TakeDamage(damage);
        Destroy(gameObject);
    }

    private void Launch()
    {
        Vector3 projectileXZPos = new Vector3(transform.position.x, 0.0f, transform.position.z);
        float R = Vector3.Distance(projectileXZPos, GetAimLocation());
        float G = Physics.gravity.y;
        float tanAlpha = Mathf.Tan(launchAngle * Mathf.Deg2Rad);
        float H = target.transform.position.y - transform.position.y;

        float Vz = Mathf.Sqrt(G * R * R / (speed * (H - R * tanAlpha)));
        float Vy = tanAlpha * Vz;

        Vector3 localVelocity = new Vector3(0f, Vy, Vz);
        Vector3 globalVelocity = transform.TransformDirection(localVelocity);

        //This makes the projectile go forward
        rigidbody.velocity = globalVelocity;
        //This rotates the projectile correctly on the arc
        transform.rotation = Quaternion.LookRotation(rigidbody.velocity) * initialRotation;
        
    }

2 Likes

Well done implementing this!
What this is doing is simulating the gravity using trigonometry. Since the angle of the shot is fixed (20 degrees), you can determine the elevation of the shot by using the distance travelled.

The physics system (rigidbody) could handle this, but you would have to calculate the correct angle and shot power to hit the location. That’s a different set of equations.

1 Like

Thanks for the explanation Brian! Appreciate it. Fun fact - I actually tried implementing this with Unity’s built-in Physics by using a simple AddForce on the Rigidbody.

Unfortunately the results …were bad :sweat_smile: and I didn’t know where to start fixing the system. My guess - I wasn’t getting the correct rotation and didn’t have a start angle for it. Stumbled over the other tutorial and the Launch function looked like a perfect addition to the Projectile class.

The basic concept I tried:

projectile.GetComponent<Rigidbody>().AddForce(projectile.transform.forward * speed);

Privacy & Terms