- Home /
How do I calculate the arc of a throw, given two positions and an angle of attack?
I want to save you many hours of research and hours going through physics calculations
PROBLEM:
I have a player on the screen and he moves around a cross-hair target across the ground to the location where he wants to attack another object/player. All I want to tell the ball is where the destination is and what the degree-angle into the air I want to throw the ball and I want the ball do to the rest of the calculations.
SETUP:
Create a script called "ThrowArcBehavior", then create a sphere and apply this script to that sphere..... That's it!!!
You could of course, adjust the gravity, the angle, or anything else, but this should help to get you started!
SPECIAL THANKS TO:
ShervinM, who helped to provide the basis for this code in 3d geometry. Original 2d code and the conversation can be found here: Link To Webpage
CODE:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThrowArcBehavior : MonoBehaviour
{
//Gravity of the simulation
public float gravity = 9.8f;
// save CPU resources by only running the code when InFlight=true
public bool inFlight;
// the angle thrown into the air
public float targetInclination;
// the destination of our target
public Vector3 targetDestination;
// IF RUNNING COLLISSIONS ie: if you want to trigger something like "send a message to the player if the ball or rock hits him"
// the radius to send the collission message
public float radiusToTrigger;
// the layerMask to check for collissions.
private LayerMask layerToCheck;
private Vector3 startPosition;
private float xVelocity;
private float yVelocity;
private float flightDuration;
private float elapsedTime;
// Start is called before the first frame update
void Start()
{
// IF THIS is a Stand-Alone test then run this code below....
// Vector3 testDestination = new Vector3 (5f,1f,5f);
// Initialize (45f, testDestination, 5f, LayerMask.GetMask("default"));
inFlight = true;
startPosition = transform.position;
elapsedTime = 0f;
CalculateVariables();
}
// Update is called once per frame
void Update()
{
if (inFlight)
{
UpdateProjectilePosition();
}
}
public void Initialize(float inclination, Vector3 destination, float radiusOfTrigger, LayerMask layerToCheckAgainst)
{
targetInclination = inclination;
targetDestination = destination;
radiusToTrigger = radiusOfTrigger;
layerToCheck = layerToCheckAgainst;
inFlight = true;
}
public void CalculateVariables()
{
// CALCULATIONS if there are DIFFERENT START and END HEIGHTS
// Calculate the range from the projectile to the target by zero-ing each out in their y-axis
Vector3 zeroedOrigin = new Vector3(startPosition.x, 0, startPosition.z);
Vector3 zeroedTarget = new Vector3(targetDestination.x, 0, targetDestination.z);
Vector3 zeroedDirection = (zeroedTarget - zeroedOrigin).normalized;
float angleRad = targetInclination * Mathf.Deg2Rad;
float heightDifference = startPosition.y - targetDestination.y;
float targetDistance = Vector3.Distance(transform.position, targetDestination);
float targetRange = Vector3.Distance(zeroedOrigin, zeroedTarget);
// Calculate the velocity needed to throw the object to the target at specified angle.
// Velocity can be solved by re-arranging the general equation for parabolic range:
// https://en.wikipedia.org/wiki/Range_of_a_projectile
float projectile_Velocity
= (Mathf.Sqrt(2) * targetRange * Mathf.Sqrt(gravity) * Mathf.Sqrt(1 / (Mathf.Sin(2 * angleRad)))) /
(Mathf.Sqrt((2 * targetRange) + (heightDifference * Mathf.Sin(2 * angleRad) * (1 / Mathf.Sin(angleRad)) * (1 / Mathf.Sin(angleRad)))));
// Extract the X Y componenent of the velocity
xVelocity = projectile_Velocity * Mathf.Cos(angleRad);
yVelocity = projectile_Velocity * Mathf.Sin(angleRad);
// Calculate flight time.
flightDuration = targetRange / xVelocity;
// Rotate projectile to face the target.
transform.rotation = Quaternion.LookRotation(zeroedDirection);
}
public void UpdateProjectilePosition()
{
if (elapsedTime >= flightDuration)
{
// if we finished our our flight/trajectory of the item thrown, then lets end it
ProjectileLanded();
return;
}
// However if NOT enough time has elapsed to complete the arc throw....
float x = 0f;
float y = (yVelocity - (gravity * elapsedTime)) * Time.deltaTime;
float z = xVelocity * Time.deltaTime;
// May need to switch around Z and X axis depending on the "forward-direction" of the object
this.transform.Translate(x, y, z);
elapsedTime += Time.deltaTime;
}
public void ProjectileLanded()
{
Debug.Log("This Projectile Landed");
inFlight = false;
//Physics.OverlapSphere ()
}
}