The goal of Aerodynamic Movement is to simulate aerodynamic force without losing the ability to accurately set target points as the plugin does in standard projectile motion.
You can use this feature to simulate curve effect seen in sports such as football, baseball, golf (draw/fade), etc. You can also use it to simulate wind, air drag, or achieve creative weapon behaviors.
The core concept is the local offset vector, here we name it OffsetLocal. Definition: of an aerodynamic movement, the max displacement along local space, the local space is formed by projecting "end - start" onto xz plane as forward vector.
The figure below is a top-down view of a scene, it explains the definition and shows two example values of OffsetLocal. The underscore symbol _
indicates that the y axis value can be arbitrary.
Here we describe a standardized process of using the API.
A concrete example script following these steps can be found in the folder "Demos/Scripts/05 Aerodynamic Movement", named CurvyTest.cs
. The corresponding example scene can be found in "Demos/05 Aerodynamic Movement.unity".
Preparation:
Let's prepare the script with some fields that can be adjusted in the Unity inspector panel. These are the fields used throughout the steps, but your own script can be different, for example, you may want to control the targetPoint via custom logic, so it can be a local variable instead of a private class field.
xxxxxxxxxx
[SerializeField] Rigidbody ballPrefab;
[SerializeField] Vector3 startPoint;
[SerializeField] Vector3 targetPoint = new Vector3(0f, 0f, 50f);
[SerializeField] Vector3 offsetLocal = new Vector3(25f, 0f, 10f);
Step 0:
Create a new AerodynamicMoveSolver
instance,
amSolver = new AerodynamicMoveSolver();
and optionally, add a custom callback that will be called when the projectile reaches target. Here we stop it and add a bounce to it:
xxxxxxxxxx
amSolver.OnFinished = (ball) =>
{
ball.angularVelocity = Vector3.zero;
ball.velocity = new Vector3(0, 10, 0);
};
If you also want to predict the trajectory, set up a PEBTrajectoryPredictor
instance (here we call it predictor), the instance will be used later:
x
var b = Instantiate(ballPrefab, startPoint, Quaternion.identity);
predictor.Simulatee = b;
predictor.AMSolver = amSolver;
b.gameObject.SetActive(false);
Note
Step 0 all happens in a Start()
or Awake()
method.
Step 1:
In this step, we calculate all the data needed by calling Solve(...)
. You can add the code below directly into your logic or encapsulate it to a method.
Here, we are using VelocityByHeight(...)
, but you can use any methods in Projectile
class.
x
var offY = new Vector3(0, offsetLocal.y, 0);
var vRaw = Projectile.VelocityByHeight(startPoint, targetPoint + offY, 10f);
var vReal = amSolver.Solve(startPoint, targetPoint, offsetLocal, vRaw);
If you also predict the trajectory, call the predictor
we added earlier.
xxxxxxxxxx
predictor.LaunchVelocity = vReal + Projectile.VelocityCompensation;
predictor.SimulateAndRender();
Step 2:
This is the final step, we launch the projectile, and then set up execution of Aerodynamic Movement through AerodynamicMoveRuntime
component. And now your projectile moves in the air in curve!
x
myProjectile = Instantiate(projectilePrefab, startPoint, Quaternion.identity);
myProjectile.AddForce(vReal + Projectile.VelocityCompensation, ForceMode.VelocityChange);
myProjectile.TryGetComponent<AerodynamicMoveRuntime>(out var rt);
rt ??= myProjectile.gameObject.AddComponent<AerodynamicMoveRuntime>();
rt.AcquireRuntimeParameters(amSolver, myProjectile);
rt.Run();
Aerodynamic Movement consists of 2 classes AerodynamicMoveSolver
and AerodynamicMoveRuntime
.
xxxxxxxxxx
public Action<Rigidbody> OnFinished
Custom callback that will be called when the projectile reaches target. Note that it is triggered after a pre-calculated time, not by collision.
xxxxxxxxxx
public Vector3 SolvedVelocity { get; }
The modified launch velocity that takes aerodynamic movement into account. The same value as the return value of Solve(...)
.
xxxxxxxxxx
public Vector3 Acceleration { get; }
The continuous acceleration that is used to conduct aerodynamic movement.
xxxxxxxxxx
public Vector3 OffsetVector { get; }
The world space version of offsetLocal
(here, offsetLocal
refers specifically to the value passed to Solve(...)
). It is used internally to get offset end point ( = end + OffsetVector).
xxxxxxxxxx
public float TimeOfFlight { get; }
The duration needed for the projectile to move to end point, the acceleration of aerodynamic move is applied during this period of time.
Computes the values required for aerodynamic movement.
xxxxxxxxxx
public Vector3 Solve(Vector3 start, Vector3 end, Vector3 offsetLocal, Vector3 v)
start
: The launch point.
end
: The target point.
offsetLocal
: Of a aerodynamic movement, the max displacement along local space, the local space is formed by projecting "end - start" onto xz plane as forward vector.
v
: The original launch velocity that makes the projectile move from start point to end point without taking aerodynamic move into account.
returns
: The modified launch velocity that takes aerodynamic move into account.
Executes the aerodynamic movement.
This is used internally in PEBTrajectoryPredictor
class for aerodynamic movement prediction, but for the execution applied to real projectile, we recommend using methods in AerodynamicMoveRuntime
class.
xxxxxxxxxx
public bool ApplyAcceleration(Rigidbody rBody, ref float timer)
rBody
: The Rigidbody of the object to which the acceleration is applied.
timer
: This is used as a runtime context, it records how much time has been passed since the projectile launch.
returns
: Whether or not the acceleration is applied. False means the procedure is finished.
public float TimeOfFlight => timeOfFlight;
The duration needed for the projectile to move to end point, the acceleration of aerodynamic move is applied during this period of time.
public float Timer => timer;
How much time has been passed since the projectile launch.
Acquires the values from the AerodynamicMoveSolver instance called solver. A valid instance should have been called Solve(...) beforehand.
x
public void AcquireRuntimeParameters(AerodynamicMoveSolver solver, Rigidbody rBody)
solver
: The AerodynamicMoveSolver instance to acquire values from.
rBody
: The Rigidbody of the object to which the acceleration is applied.
Starts running the AerodynamicMoveRuntime.
public void Run()
Pauses the AerodynamicMoveRuntime. Can be useful for implementing replay and handling collisions.
public void Pause()
Pauses the AerodynamicMoveRuntime and reset the internal timer, i.e., stop. Can be useful for implementing replay and handling collisions.
public void Stop()