- Home /
Bomb Trajectory for a "Water Canon" (Maths)
Hey there! I have the following problem:
I have a Particlesystem that shoots waterparticles with the local vel = 10
There is a target that is not necessarily at the same height as the canon
The Gif should explain what it should be doing, even though there is no variance in the height.
So I started with this formula:
This formula calculates the needed angle with a given distance ,velocity and height, it looks like so in C#
private float FindAngleToShoot(float vel, float distance, float height) {
float g = Physics.gravity.y;
return Mathf.Atan(Mathf.Pow(vel, 2) + Mathf.Sqrt(Mathf.Pow(vel, 4) - g * (g * Mathf.Pow(distance, 2) + 2 * height * Mathf.Pow(vel, 2))) / g * distance);
}
I don't really know why this does not work, but it did not change the angle properly.
I started to google again and found another formula, in C# like so:
private float FindAngleToShoot2(float v, float s) {
float g = Physics.gravity.y;
return Mathf.Sin(2*(Mathf.Pow(v,2) * g) / Mathf.Pow(s,2)) * Mathf.Rad2Deg;
}
Now I was able to see the angle changing its value, but it changed not properly. It is hard to describe what actually happens, but it was "flattering" around when I moved the canon forward and backward. At some point the water really hit the fire.
So I thought about an appropiate approach and thought you guys could help me.
find the distance to target
be able to decide to have a high angle approach or a low angle approach
setting the angle and velocity of the water
Shouldn't that be all?
And I am going to attach the whole "water" class that should do all the maths.
public class Water: MonoBehaviour {
public Transform target;
private Vector3 targetPos;
public float speed = 12; //Speed of the localVelocity of the particle System
void Start() {
targetPos = target.position;
}
void Update() {
transform.localEulerAngles = new Vector3(FindAngleToShoot2(speed, Vector3.Distance(transform.position, targetPos)), 0, 0);
}
//approach 1
private float FindAngleToShoot(float vel, float distance, float height) {
float g = Physics.gravity.y;
return Mathf.Atan(Mathf.Pow(vel, 2) + Mathf.Sqrt(Mathf.Pow(vel, 4) - g * (g * Mathf.Pow(distance, 2) + 2 * height * Mathf.Pow(vel, 2))) / g * distance);
}
//approach 2
private float FindAngleToShoot2(float v, float s) {
float g = Physics.gravity.y;
return Mathf.Sin(2 * (Mathf.Pow(v, 2) * g) / Mathf.Pow(s, 2)) * Mathf.Rad2Deg;
}
Sorry for the oversized post :/
I really appreciate any kind of help!
Felix
If this is a particle system, why are you doing the maths manually? Why not just let the physics engine sort out the trajectory for you based on initial velocity, angle, and effect of gravity?
Yeah I was going to say the same thing. Can't you just switch Simulation Space to World?
tanoshimi and $$anonymous$$rSteve1, the issue isn't the physical representation of the trajectory from what I understand, it's rather a problem of ai$$anonymous$$g. I'll write something up. As a side note, the formula is most likely not working because you did not write it properly in code (you forgot at least one pair of brackets so the entire term that's supposed to be the numerator will be divided by the deno$$anonymous$$ator).
Answer by christoph_r · Aug 13, 2014 at 07:08 PM
This is actually more of a physics problem than a programming problem. Using a fixed velocity makes this much easier, though. The formula you picked out makes sense for these conditions. As you already wrote, the way to solve the problem is indeed to gather the information needed (so height and distance) and then calculate the angle with your formula. Then have the cannon shoot the water at the angle (and the fixed velocity). But there are a few other things to consider:
In code, the formula should look like this:
a = Mathf.Atan((vel^2 - Mathf.Sqrt(vel^4 - g*(g * distance^2 + 2 *y * vel^2))/(g*distance))
Now as you see in the formula, there are two possible solutions, because you'll have to process a quadratic equation somewhere on the way to get to that formula. As you already noted, the two solutions are a high and a low angle (usually). The thing is, the further we get, the further the angle will converge towards 45° until it can't reach the target anymore with the set velocity. So one thing you should do is set a maximum distance for your cannon, or else the maths will start to get a bit messy. The other thing is: Does it ever make sense to use a greater angle? The disadvantage is that it takes a relatively long time for the water to reach the target, so if the target moves, that's not what you want. The advantage, though, is, that you can avoid hitting things you might otherwise hit with a low angle. If that's not something you need to consider though, the low angle is what you want to go with. The formula I wrote up should return the low angle (not 100% sure but pretty sure). To recap: Get the distance and height difference, throw them into the formula and adjust the water firing angle accordingly. Do you want your cannon to have limits for its rotation? If so, you need to set those, too. In that case, if the target is below the cannon and very close it might make sense to fire at a high angle. But if you need help with that, write a comment.
I appreciate your answer, unfortunately I am not able to test your method because I found a simple method by simply changing the localVelocity of the particle System, it goes like this:
private Vector3 CalculateBestThrowSpeed(Vector3 origin, Vector3 target, float timeToTarget) {
// calculate vectors
Vector3 toTarget = target - origin;
Vector3 toTargetXZ = toTarget;
toTargetXZ.y = 0;
// calculate xz and y
float y = toTarget.y;
float xz = toTargetXZ.magnitude;
// calculate starting speeds for xz and y. Physics forumulase deltaX = v0 * t + 1/2 * a * t * t
// where a is "-gravity" but only on the y plane, and a is 0 in xz plane.
// so xz = v0xz * t => v0xz = xz / t
// and y = v0y * t - 1/2 * gravity * t * t => v0y * t = y + 1/2 * gravity * t * t => v0y = y / t + 1/2 * gravity * t
float t = timeToTarget;
float v0y = y / t + 0.5f * Physics.gravity.magnitude * t;
float v0xz = xz / t;
// create result vector for calculated starting speeds
Vector3 result = toTargetXZ.normalized; // get direction of xz but with magnitude 1
result *= v0xz; // set magnitude of xz to v0xz (starting speed in xz plane)
result.y = v0y; // set y to v0y (starting speed of y plane)
return result;
}
It works so perfectly that I am begrudge to change it again. But again, thank you very much!