Ballistic Path Follow

BallisticPathFollow moves a GameObject along one or more precomputed ballistic paths (instances of BallisticPath). As it “plays” through each path segment, it:
Steps the object’s position over time (using
FixedUpdate/LateUpdate).Optionally validates any planned bounce‐impacts (so you don’t hit a surface that’s since moved).
Optionally checks for unplanned collisions mid‐flight.
Fires a UnityEvent (
endOfPath) once the end of all path segments is reached (or a collision causes an early stop).Can re‐enable physics at the end (so your Rigidbody can resume dynamic movement).
Designers typically use this component when they have already generated a full flight path (via BallisticPath.Get(...)) and want an object (e.g., a projectile, grenade, or thrown item) to follow that path exactly, including triggering any collision callbacks at the right moment.
Demo Scene
You can see the Ballistic Path Follow and its 2D variant demonstrated in the (2D) and (3D) Deterministic Bounce Prediction scenes. Take a look at the projectile template in each scene and you will find the Ballistic Path Follow which is what causes the projectile to fallow the pre-planned path.
Public Fields (Designer-Facing)
BallisticsData projectileReference to the projectile’s data (mass, radius, drag, etc.). Used internally for collision checks (e.g., sphere‐casting).In the Inspector, you’ll see a slot for a
BallisticsDataScriptableObject or component.
List<BallisticPath> pathA list of precomputed ballistic trajectories (each aBallisticPath).Assign as many segments as you like—for example, initial launch arc, then a bounce arc, etc.
Designers typically populate this list at runtime (e.g., right after calling
BallisticPath.Get(...)).If this list is empty or
null, the component does nothing.
bool resumeDynamicOnEnd(default:true) If checked, once the entirepathis complete (or stopped early by collision), the component finds the nearest parentRigidbody, sets it to non-kinematic, and applies the final velocity so that Unity’s physics takes over naturally.
Planned Collision Settings
bool validateBounce(default:true) When following aBallisticPath, if a segment’s final sample contains animpact(i.e., where the physics cast expected to hit something), enabling this flag will re-test with a short, instantaneousSphereCastjust before invoking the collision.If the surface has moved or is gone, the component abandons the planned bounce and ends the path early.
If disabled, it will always invoke the planned collision regardless of whether the collider has shifted.
bool invokeOnCollisionEnter(default:true) If checked, at the moment the component reaches a segment’s planned impact, it enqueues an “artificial”OnCollisionEntercall on the target GameObject so that any scripts on that object with anOnCollisionEnter(Collision)callback will run.Designers can disable this if they want to handle bounce logic entirely within custom scripts rather than rely on a simulated Unity collision.
Unplanned Collision Settings
LayerMask collisionMask(default:1= “Default” layer) Specifies which layers to test for unexpected collisions while following the path. If the projectile hits something outside of its planned path (e.g., you throw it near another object), the component can stop early.Only used if
endOnCollisionis enabled.
bool endOnCollision(default:true) When checked, everyFixedUpdatethe component does a quickPhysics.SphereCastfrom the object’s current position in the direction of its instantaneous velocity.If it detects a collider on any of the layers in
collisionMaskthat isn’t part of a planned impact, it stops the path immediately and triggers the same “end of path” logic (and event).
Vector3Event endOfPathA UnityEvent that fires when the path is finished or forcibly stopped by a collision.The event’s payload is the final velocity (
float3) of the object as it ends.Designers can hook any method (e.g., spawn an explosion, play a sound, enable a ragdoll) by assigning to this event in the Inspector or in code:
ballisticPathFollow.endOfPath.AddListener(finalVelocity => { // Your custom logic here });
Typical Designer Usage
Scene Setup
Attach
BallisticPathFollowto the projectile prefab (or parent GameObject).Ensure that the prefab either has a
Rigidbody(set to “Kinematic” initially) or that a parent in the hierarchy has one.
Compute & Assign Path
In your firing script (e.g., when the player clicks “shoot”), call
BallisticPath.Get(...)to get aBallisticPathfor the initial launch.If you expect bounces, compute subsequent segments by using the previous impact point/velocity as the new
startand recomputing anotherBallisticPath.Assign all segments to the
pathlist inBallisticPathFollow. Example:// Suppose we already have a BallisticPath initialPath: var projectileFollow = projectileInstance.GetComponent<BallisticPathFollow>(); projectileFollow.projectile = myBallisticsData; projectileFollow.path = new List<BallisticPath> { initialPath, bouncePath, finalPath };
Configure Collision Behavior
If you want to enforce “surface‐moved” detection on planned bounces, leave
validateBounce=true.If you want to skip simulating
OnCollisionEnter(maybe you handle impacts in a different system), uncheckinvokeOnCollisionEnter.If you don’t need to stop for anything unexpected mid-flight (e.g., your scene is empty), uncheck
endOnCollisionto save needless checks.To only collide with specific layers (e.g., “Environment” but not “Player Characters”), adjust
collisionMaskin the Inspector.
Hooking the End-of-Path Event
projectileFollow.endOfPath.AddListener(finalVelocity => { // Example: switch on physics and let it fall naturally var rb = projectileInstance.GetComponent<Rigidbody>(); rb.isKinematic = false; rb.velocity = finalVelocity; // (Or spawn an explosion, play sound, etc.) });Runtime Behavior
As soon as the GameObject is enabled (or when
pathis non-empty),BallisticPathFollowbegins simulating motion.Each
LateUpdate, it calculates how far along the concatenatedpathlist it should be (based onTime.time - startTime).If it reaches a planned impact point:
It optionally re-validates whether that surface still exists.
It enqueues an artificial
OnCollisionEnteron the impacted GameObject (so any scripts there respond as if hit by a real Unity collision).If the bounce is invalid (surface moved), it stops early.
If it detects an unplanned sphere‐cast hit (and
endOnCollisionis enabled), it stops immediately.When it finally cannot advance any further (all segments done or a collision forced stop):
If
resumeDynamicOnEndis true, it flips the nearest parentRigidbodyback to non-kinematic and sets its velocity so Unity physics takes over.Fires the
endOfPathUnityEvent with the ending velocity (or zero if it never moved).
Designer Tips
Preparing Multiple Segments If you expect your projectile to bounce (e.g., off a wall or the ground), compute each segment in sequence and add them to the
pathlist. For example:// 1. First segment: from muzzle to first bounce: BallisticPath first = BallisticPath.Get(startPos, startCollider, launchVelocity, radius, resolution, maxLen, layerMask); if (first.impact.HasValue) { // 2. Compute bounce velocity via reflection off the first impact’s normal: Vector3 bounceVel = Vector3.Reflect(first.steps[^1].velocity, first.impact.Value.normal); // 3. Second segment: from impact point, using bounceVel: BallisticPath second = BallisticPath.Get(first.impact.Value.point, /* same collider? */ collider, bounceVel, radius, resolution, maxLen, layerMask); projectileFollow.path = new List<BallisticPath> { first, second }; } else { projectileFollow.path = new List<BallisticPath> { first }; }Skipping Event Injection By default,
BallisticPathFollowfinds anyOnCollisionEnter(Collision)methods on the impacted GameObject and invokes them via reflection. If your gameplay already handles bounce logic entirely within a different event (e.g., you manually spawn a decal or sound when the path ends), uncheckinvokeOnCollisionEnterand listen only toendOfPath.Collision Layers
Planned impacts always use whatever layers you originally sampled when calling
BallisticPath.Get(...).Unplanned impacts use the
collisionMask. Make sure your projectile’s own collider (or parent colliders) is excluded so it doesn’t immediately collide with itself.
Pausing or Resuming
If you disable the GameObject mid-flight (e.g., pausing the level),
LateUpdatestops advancing. Upon re-OnEnable, it resetstime = Time.timeso it will continue where it left off.To reset entirely, clear or replace
pathand toggle the component off/on.
By assigning a precomputed list of BallisticPath segments and configuring the collision flags, designers can have a projectile follow exactly the path they planned—complete with bounces, bounce validation, automatic invocation of any impacted object’s OnCollisionEnter, and the option to hand control back to Unity’s physics at the end.
Last updated