How do I make a projectile arc and always hit a moving target?

Hi all I’m new to Unity as well as programming in C# (about 3 weeks in). I’ve been searching for over a week now and trying different things that I can think of to no avail. I’m making a tower defense style game and I’m stuck on getting the projectile for my catapult to work how I intent. The idea is I have a catapult positioned about 20 units up in the Y direction on top of a building and the enemy is moving straight towards the building at ground level (Y=0). The enemy is moving at a constant speed with a simple transform.translate for now. When the enemy hits the collider I have setup that means the enemy is in range and the catapult begins to fire on it. I want the projectile to make a nice arc and hit the enemy every time no matter the speed the enemy is traveling at because different enemies will move at different speeds. The best example I can give is the catapults in Plants Vs. Zombies on the roof level. At first I thought adding a force and letting unity’s physics take care of it would make a nice arc for the projectile, but I found this code here on unity answers that deals with velocity and thought it would be what I was looking for… (modified for C# and the way my project is setup)

void Start()
{
	rigidbody.velocity = BallisticVel(myTarget);
}

Vector3 BallisticVel(Transform target) 
{
	dir = target.position - transform.position; // get target direction
	h = dir.y;  // get height difference
	dir.y = 0;  // retain only the horizontal direction
	dist = dir.magnitude ;  // get horizontal distance
	dir.y = dist;  // set elevation to 45 degrees
	dist += h;  // correct for different heights
	vel = Mathf.Sqrt(dist * Physics.gravity.magnitude);
	return vel * dir.normalized;  // returns Vector3 velocity
}

So what happens is the catapult will instantiate the projectile at a location of an empty game object when the enemy collides with the catapult trigger. I attached this code to my projectile and it has no errors and is super close, however given that my catapult is a higher elevation then the enemy and the enemy is moving constantly towards the building the projectile will only hit the enemy if the enemy is moving very slow (.5). Anything with a speed of 1 or higher the projectile will overshoot the enemy and land where the enemy was at the time the projectile was created and not where its going to be. I figure somehow I have to incorporate the speed the enemy is moving into my code, and I passed the speed of the enemy to the projectile but I’m not sure how to incorporate that or if that is even the best way to go about this. Any options would be greatly appreciated. Sorry the post is long I wanted to make what I was trying to accomplish as clear as possible.

simple use Vector3.RotateToawrds.

if your target is moving the projectile will rotate towards it as it moves.
to make it “Arc” you will need to make a parabolic path towards it which you have basically already done. when the project tile reaches the “Apex” use RotateToawrds to seek the opponent. try this and if it works you may not need the enemy speed.

if it turns out you do need it, just instantiate a empty game object based on the enemies dir ,speed and distance. how to do this can vary if you have bi-level projection or a horizontal projection. you basically need to calculate the time to hit where the enemy is now, then calculate where the enemy will be after that time has passed by using his direction , speed and his position this will be where the empty game object will go. from that you can calculate the projection that will hit the enemy even if he moves using that gameobject as a target predication. if your enemy is constantly changing directions, he can actually “Dodge” the projectile if he moves out of the direction. since you projectile tile will be pre-calculated. if this is the case you might need to do an calculation that updates regularly to keep up with the enemy

your basically predicting where the enemy will be after that set amount of time. pretty cool huh?. let me know if you have more questions

I also wrote a blog post on this topic. It shows code that adds an arc (using the parabolic equation) to otherwise straight motion. It also shows how to make your sprite point in the direction it’s going (in case it’s something like an arrow, not round like a cannonball).

http://luminaryapps.com/blog/arcing-projectiles-in-unity/

The script as shown keeps a fixed target point (targetPos). If you wanted to track a moving target, then you would just replace that Vector3 with a Transform (let’s call it target), and then use target.position instead of targetPos. That should do it.

I wrote a function that targets a projectile using physics and recursion to predict where the target will be by the time the projectile arrives. The recursion is necessary (it seems to me) because in order to find the target’s projected location, you need to know how long it will take the projectile to get there - but to know that, you need to know the projected location of your target… I solved this by doing first one calculation, then the other, using each result to refine the next, and found that after ten iterations, it’s almost always close enough. If you need more accuracy, just adjust “accuracy” parameter to add more iterations to the loop. None of the calculations involved are very expensive.

This function uses plain physics: what’s fired is a rock, not a rocket. The velocity is set (albeit with inhuman precision) from the moment it’s launched. It doesn’t “cheat” and steer towards its target in midair. Therefore, it’s possible for the target to dodge if by changing course while the projectile is en route. This may or may not be what you want, but it is realistic.

float projectileSpeed = 100; // or whatever
float accuracy = 10;

Vector3 BallisticVelocity(Vector3 source, Vector3 target, Vector3 targetVelocity)
{
	// use a few iterations of this recursive function to zero in on 
	// where the target will be, when the projectile gets there
	Vector3 horiz = new Vector3(target.x - source.x, 0, target.z - source.z);
	float t = horiz.magnitude / projectileSpeed;
	for (int a = 0; a < accuracy; a++)
	{
		horiz = new Vector3(target.x + targetVelocity.x * t - source.x, 0, target.z + targetVelocity.z * t - source.z);
		t = horiz.magnitude / projectileSpeed;
	}
	// after t seconds, the cannonball will reach the horizontal location of the target -
	// so all we have to do is make sure its 'y' coordinate zeros out right there
	float gravityY = (.5f * Physics.gravity * t * t).y;
	// now we've calculated how much the projectile will fall during that time
	// so let's add a 'y' component to the velocity that will take care of the rest
	float yComponent = (target.y - source.y - gravityY) / t + targetVelocity.y;

	horiz = horiz.normalized * projectileSpeed;
	return new Vector3(horiz.x, yComponent, horiz.z);

}

**

strong text

**

I’ve wrote an article on this too, and it comes with visuals to describe the math behind it. There are 2 parts to the article, the first part describes how to make a projectile always track a target (just like in RTS games): Coding projectiles for your tower defense game — Terresquall Blog

The second part describes how to get a projectile to arc, using a sin curve. If you’re not interested in the math, you can also just skip all of that and copy the code at the end of the article. It’s a usable component with settable attributes which you can just plug and play: Coding projectiles for your tower defense game — Part 2 — Terresquall Blog