# How to make projectiles shoot towards their target at variable distances?

Hi guys,

Let me start off by saying I checked google, looked through the documentation and searched the answers and have not found what I was looking for.

I have a canon in my game that sits in the middle of the level that shoots fireballs at the player wherever the player is… The canon rotates to always face the player and fires the fireballs in the direction of the player, but I am having a hard time trying to calculate the force of the fireballs so that when the player moves the force of the fireball is adjusted to compensate for the distance between the player and the canon.

Here is my script so far:

``````var fireballObject : Rigidbody;
var delayAmount = 2.0;
var target : Transform;

private var timeDelay = 0.0;

var fireballObject : Rigidbody;
var delayAmount = 2.0;
var target : Transform;

private var timeDelay = 0.0;

function Update () {

if(Time.time > timeDelay)
{
timeDelay = Time.time + delayAmount;
Debug.Log("Fireball Time!");
createFireball();
}

else
{
Debug.Log("Still Waiting");
}

}

function createFireball()
{
var newFireballObject : Rigidbody = Instantiate(fireballObject, transform.position, transform.rotation);
newFireballObject.name = "fireball";

newFireballObject.rigidbody.velocity = transform.TransformDirection(Vector3(0, 0, 0));
}
``````

I know I need to adjust the transform.TransformDirection(Vector3(0, 0, 0)); to compensate for the distance between the canon and the player but I’m just not sure how to go about it. Does anyone have any suggestions?

Also to be more specific this canon does not shoot upwards first, it shoots straight out and the projectile, which is a sphere with a rigidbody attached moves forward at a certain force before succumbing to gravity.

Thank you all so much for the advice! I haven’t got to try any new implementations yet because its finals week but I will try soon

Ok it appears I have it working and it will work with rigidbodies!

This code is better optimized if anyone wants it:

``````//Exposed variables
var fireballObject : Rigidbody;
var target : Transform;
var delayAmount = 2.0;
var forceMultiplier = 1.5;

private var timeDelay = 0.0; //Delay the amount of time until next fireball

function Update () {

//Create variables to hold the vectors
var targetVector = target.position;
var sourceVector = transform.position;
var newVector = Vector3((target.position.x - transform.position.x), (target.position.y - transform.position.y), (target.position.z - transform.position.z));

/*
The equation for this is:
T = SQRT( 2h/g) However the sqrt can stay out of the code.
Make sure to get the absolute value of the equation to avoid a negative square root
*/
//Solve for time
var emitterYPos = transform.position.y; //Y distance from ground
var Ay = Physics.gravity.y; //Ay (Acceleration Y)
var t = Mathf.Abs( (2 * emitterYPos) / (Ay) ); //Time element

/*
The equation for this is:
Vix = d * sqrt(g/2H) However the square root can be left out of the code
*/
//Solve for the initial X Velocity
var targetXPos = target.position.x - transform.position.x; //X distance from source
var targetZPos = target.position.z - transform.position.z; //Z distance from source
var Ax = 0; //Ax (Acceleration X)

//Calculate the X and Z distances from the target
var Vix = targetXPos * (Mathf.Abs(Ay / (2 * emitterYPos)));
var Viz = targetZPos * (Mathf.Abs(Ay / (2 * emitterYPos)));

//Check to see if it is time to shoot a fireball
if(Time.time > timeDelay)
{
timeDelay = Time.time + delayAmount;

//Compare X to Z distances and use whichever is greater for the trajectory
if(Mathf.Abs(Vix) > Mathf.Abs(Viz))
{
createFireball(Vix);
}

else
{
createFireball(Viz);
}
}
}

//Create fireball object
function createFireball(V)
{
var newFireballObject : Rigidbody = Instantiate(fireballObject, transform.position, transform.rotation);
newFireballObject.name = "fireball";

newFireballObject.rigidbody.velocity = transform.TransformDirection(Vector3(0, 0, Mathf.Abs(V * forceMultiplier)));

}
``````

You will need to assign a target to it to get it to work.

Check out the accurate lob example for iTween… it does pretty much exactly what you’re describing:

http://itween.pixelplacement.com/examples.php

Although, if you’re able to get it going with rigid bodies, I’d like to know as well!

I’m VERY close, I just can’t get it quite right.

My calculations on paper come up correct, but I can’t it it to work in Unity. The canon follows the player, and shoots the fireballs, but the behavior isn’t correct. If I have my script like below:

``````function Update () {

//Create variables to hold the vectors
var targetVector = target.position;
var sourceVector = transform.position;
var newVector = Vector3((target.position.x - transform.position.x), (target.position.y - transform.position.y), (target.position.z - transform.position.z));

/*
The equation for this is:
T = SQRT( Y / ((0.5 * Ay) + (Viy * T)) )
*/

var emitterYPos = transform.position.y; //Y distance from ground
print("emitterYPos = " + emitterYPos);
var Ay = Physics.gravity.y; //Ay (Acceleration Y)
print("Ay = " + Ay);
var t = Mathf.Sqrt( (emitterYPos * -1) / (0.5 * Ay) ); //Entire equation, don't forget to multiply distance from ground by a negative to avoid a negative square root

print("T = " + t);

/*
The equation for this is:
Vix = (x + 0.5 * Ax * T^2) / T
*/

var targetXPos = target.position.x; //X distance from source
var Ax = 0; //Ax (Acceleration X)
var Vix = (targetXPos / t);

print("VIX " + Vix);

//Check to see if it is time to shoot a fireball
if(Time.time > timeDelay)
{
timeDelay = Time.time + delayAmount;
Debug.Log("Fireball Time!");
createFireball(Vix);

print("TEST");
print("T = " + t);
print("X = " + targetXPos);
print("Y = " + emitterYPos);
print("Ay = " + Ay);
print("VIX = " + Vix);
print("VIX2 = " + Mathf.Abs(Vix));
}

else
{
Debug.Log("Still Waiting");
}
``````

}

//Create fireball object
function createFireball(vX)
{
var newFireballObject : Rigidbody = Instantiate(fireballObject, transform.position, transform.rotation);
newFireballObject.name = “fireball”;

``````newFireballObject.rigidbody.velocity = transform.TransformDirection(Vector3(0, 0, vX));
``````

}

Then the fireballs are very weak until I get closer to the canon. If I take this line:

var targetXPos = target.position.x; //X distance from source

and change it to

var targetXPos = target.position.z; //Z distance from source

then the result is almost perfect, except when I approach the Z axis. Then the fireballs drop right in front of the canon Anyone have any ideas?

The basic way to “lead” someone is to estimate time to hit them, and then aim at where they will be in that many seconds (the time to the new target won’t be exactly the same, but close enough. Can add tricks later.) The basic way to “lead” is:

``````float flightTime = (targ.position - transform.position).magnitude / shotSpeed;
Vector3 aimPos = targ.position + targ.rigidbody.velocity * flightTime;
// NOTE: if not an RB, look up their script speed

Vector3 aimDir = aimPos - transform.position;
``````

Giving the shot a Y speed so it “drops” at the correct distance is a separate problem, and makes the 1st problem more difficult (since angling up reduces flat speed.) Giving the shots a fixed “no gravity” flight time makes it even worse (since the common solutions don’t have that in them.)

I’d do it with no gravity first, then “fake” the gravity by aiming a tiny bit up at anything beyond my range (maybe a small random, to make it look like the gunner is guessing.)

I have no idea how you’d go about implementing it (math/physics is not my strong point), but this seems to be the formula you’re after:

http://hyperphysics.phy-astr.gsu.edu/hbase/traj.html#tra7