Bezier curves for flight paths in Unity C#

Post navigation

For my mobile game I needed enemy space ships that would fly to random points in space within a specific area. When they reached the designated point, they would choose a new point and the process would repeat. First I tried the easiest solution: transform.LookAt(), which worked, but the space ships had a specific turn radius and under some circumstances would circle the destination point indefinitely. I could increase the distance that was required for the ship to “reach” the checkpoint but I wanted to guarantee consistency.

Bezier curves would allow me to have consistent flight patterns without having to fine tune parameters in order to avoid unintended behaviors.

using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
[SerializeField]
private float dist; //the distance of the current curve on a scale of 0 to 1
[SerializeField]
private Vector3[] points; // an array of Vector3 points to calculate the curve. You can adjust the formula to use your desired amount of points.
[SerializeField]
private float min; //the minimum distance at which to place a points[]
[SerializeField]
private float max; //the maximum distance at which to place a points[]
[SerializeField]
private float speed; //the speed modifier for moving the ship along the curve
void Awake()
{
CalcInitPoints();
}
Vector3 CalculateBezierPoint(float t,
Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) // returns the vector3 of the desired point along the curve. this vector3 will be the transform.position of the ship
{
float u = 1f - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t;
Vector3 p = uu * p0;
p += 2 * u * t * p1;
p += tt * p2;
return p;
}
void Update ()
{
MoveBezier(); //for the purpose of demonstration, the flight is continuously calculated on Update().
}
private void CalcInitPoints() //create the initial points for the curve
{
for (int i = 0; i <= 4; i++)
{
points[i] = new Vector3(Random.Range(min, max), Random.Range(min, max), Random.Range(min, max));
}
}
private void NewPoints() //NewPoints is used only after there are points already existing
{
points[0] = points[2]; //re-use the third point
points[1] = (points[2] + (-0.5f * (points[1] - points[2]))); //the second point becomes the new tangent calculated using the third and second point
points[2] = new Vector3(Random.Range(min, max), Random.Range(min, max), Random.Range(min, max));//the third point is the new random Vector3
}
private void MoveBezier() //updates every frame to move the ship along the curve using Time.deltaTime
{
Vector3 nextPoint = CalculateBezierPoint(dist, points[0], points[1], points[2], points[3]); //injects dist (0-1 which increases by deltaTime) into the bezier formula
transform.position = nextPoint; //translate the position of the ship
if (dist <= 1f) //check every frame to see if the end of the curve has been reached
{
dist += Time.deltaTime * speed;
transform.LookAt(CalculateBezierPoint(dist, points[0], points[1], points[2], points[3])); //keeps the ship facing in the direction that it is flying
}
else
{
NewPoints(); //when the end of the curve has been reached, create a new curve and then reset the dist to zero
dist = 0;
}
}
}

The length and point on the curve is measured between 0 and 1. The position of the ship is set equal to a specific point on the curve which is being increased by Time.deltaTime. Once the distance has reached 1, a new point is made, a new curve is calculated, and the distance along the curve is set back to 0. Now we have random flight paths using bezier curves.

Some caveats; the curves would sometimes be very acute, which resulted in a space ship that turned very quickly, which I found undesirable. A solution to this would be use a 3rd point between the previous and the next point. An extra point would create a softer curve, as long as it is placed between the first and last points a shape close to an equilateral triangle.