[Coroutine] Smooth Trajectories: Stop Mid-Path Resets
Solution
Unity 2021.x - Unity 6.3.x
Published Tue, Mar 10
An object intended to follow a parabolic 2D trajectory frequently fails to reach its designated target or exhibits incomplete movement. This behavior is observed when your movement coroutine is repeatedly initiated inside Update(), causing the path calculation to reset prematurely every frame.
Resolve erratic object movement by initiating coroutines once within Start() instead of Update(), and ensure the AnimationCurve is correctly configured.
The primary cause of the described issue is the erroneous invocation of your coroutine within the Update() method. The Update() method executes once per frame, leading to the coroutine being started anew in every frame. This continuous re-initialization resets internal state variables, such as timePassed, preventing the coroutine from progressing through its full duration. Consequently, the object appears to move only partially or erratically as it restarts the trajectory calculation constantly.
To rectify this behavior:
- Relocate the
StartCoroutinecall from theUpdate()method to a lifecycle method that executes only once, such asStart()orAwake(). - If the movement must be triggered by a specific event or input, use a boolean flag to ensure the coroutine is not already running before calling
StartCoroutineagain. - Verify the
AnimationCurveasset evaluation to ensure the timePassed value is correctly normalized between 0 and 1.
Additional Tips
- Ensure your timePassed variable is incremented using
Time.deltaTimeto maintain frame-rate independent movement. - Check that the
AnimationCurvewrap mode is set to Clamp to prevent the projectile from looping or snapping back after completion. - Utilize
Vector2.Lerpfor the horizontal axis while using the curve to offset the vertical position for a clean parabolic arc.
public class projectileController : MonoBehaviour
{
public AnimationCurve curve;
[SerializeField] private float duration = 1.0f;
[SerializeField] private float heightY = 3.0f;
public GameObject targetObject;
void Start()
{
// Coroutine initiated once upon script enablement to prevent reset
StartCoroutine(Curve(transform.position, targetObject.transform.position));
}
public IEnumerator Curve(Vector3 start, Vector3 target)
{
float timePassed = 0f;
Vector2 end = target;
while (timePassed < duration)
{
timePassed += Time.deltaTime;
float linearT = timePassed / duration;
float heightT = curve.Evaluate(linearT);
float height = Mathf.Lerp(0f, heightY, heightT);
// Interpolate position and add the vertical offset from the curve
transform.position = Vector2.Lerp(start, end, linearT) + new Vector2(0f, height);
yield return null;
}
}
}
Related Posts Haven't quite found a solution to your problem? We think these posts might help you.
Content inspired by a Unity discussion post.