Tuesday, September 1, 2020

Jump on point

 Okay, so the first thing to bear in mind is that the goal to solving this is to treat the object's motion along the two axes separately, which we'll call and y. Although the results of this can be used in 3d space, this problem only concerns two axes, which is nice. Our given values (the ones we have) are the position of our object, position of the target, and the angle that we want to fire at. From this, we know the planar distance between the object (d) as well as the offset on the y axis between the two. We want the initial velocity to fire the object at to strike the target t.




As stated above, we'll find the velocity of the components, x and y. vX (velocity x) is constant (there's no air resistance or anything) and so is the following:

Code (csharp):
  1. v0 * cos(angle)
...where v0 is our initial velocity. We know that the distance an object travels is equal to it's velocity*time, so the distance x travels is vX*t, or how long our object is in the air.

vY is just as easy to solve for, giving us:

Code (csharp):
  1. v0 * sin(angle)
However, y is not a constant velocity, but rather has the acceleration of gravity on it throughout it's time in the air. So the velocity of y is equal to the initial velocity, PLUS however much acceleration has acted on it over the course of the time in the air.

Code (csharp):
  1. vY = -gt + v0 * sin(angle)
So as you can see, at t=0, y's velocity is just the initial velocity, but over time more acceleration is added. Our y position is then:

Code (csharp):
  1. -1/2*g*t^2 + v0 * sin(angle)*+ y0
Now that we can calculate the position of our object on either two axes at any time, we can create a formula to solve for initial velocity. Our initial position is equal to x0 = 0, and y0 = yOffset. We also know that the final x position we want is just equal to distance, and the final y position is equal to 0. We can insert all these into the above two equations, and solve for the both of them (which eliminates t) to get the following:

Code (csharp):
  1. v0 = (1 / Mathf.Cos(angle)) * Mathf.Sqrt((0.5f * gravity * Mathf.Pow(distance, 2)) / (distance * Mathf.Tan(angle) + yOffset));
It's a bit to take in! I had to figure this our a few years back for a project, and I got most of the info from this physics exchange post. Implementing the above in Unity looks like this:

Code (csharp):
  1.  
  2. using UnityEngine;
  3. using System.Collections;
  4.  
  5. public class ProjectileFire : MonoBehaviour {
  6.  
  7.     [SerializeField]
  8.     Transform target;
  9.  
  10.     [SerializeField]
  11.     float initialAngle;
  12.  
  13.     void Start () {
  14.         var rigid = GetComponent<Rigidbody>();
  15.  
  16.         Vector3 p = target.position;
  17.  
  18.         float gravity = Physics.gravity.magnitude;
  19.         // Selected angle in radians
  20.         float angle = initialAngle * Mathf.Deg2Rad;
  21.  
  22.         // Positions of this object and the target on the same plane
  23.         Vector3 planarTarget = new Vector3(p.x0, p.z);
  24.         Vector3 planarPostion = new Vector3(transform.position.x0, transform.position.z);
  25.  
  26.         // Planar distance between objects
  27.         float distance = Vector3.Distance(planarTarget, planarPostion);
  28.         // Distance along the y axis between objects
  29.         float yOffset = transform.position.y - p.y;
  30.  
  31.         float initialVelocity = (1 / Mathf.Cos(angle)) * Mathf.Sqrt((0.5f * gravity * Mathf.Pow(distance, 2)) / (distance * Mathf.Tan(angle) + yOffset));
  32.  
  33.         Vector3 velocity = new Vector3(0, initialVelocity * Mathf.Sin(angle), initialVelocity * Mathf.Cos(angle));
  34.  
  35.         // Rotate our velocity to match the direction between the two objects
  36.         float angleBetweenObjects = Vector3.Angle(Vector3.forward, planarTarget - planarPostion);
  37.         Vector3 finalVelocity = Quaternion.AngleAxis(angleBetweenObjects, Vector3.up) * velocity;
  38.  
  39.         // Fire!
  40.         rigid.velocity = finalVelocity;
  41.  
  42.         // Alternative way:
  43.         // rigid.AddForce(finalVelocity * rigid.mass, ForceMode.Impulse);
  44.     }
  45. }
  46.  
Which also rotates our velocity so you can fire in any direction, since we only solve it for the x and y plane.



No comments:

Post a Comment