I saw a few folks’ code to detach the ball using a distance or distance squared check. There is one issue you are likely to encounter with such an approach and it increases in likelihood as you make your scene more complex (or if you’re playing on a lower end device).
With a more complex scene (and/or with a lower end device) you’ll have lower frame rates, which means more time will pass between frames, which means your ball may overshoot your distance check and thus the ball may never release!
But there’s a rather simple fix. The technique I’ll show you is very useful for many many other applications in Unity. Two approaches:
-
Check the forces on the spring and look for the moment in time when the spring wants to pull the ball in the opposite direction of the ball’s travel and/or when the current force on the spring is opposite the original force from when the ball was launched. It will work, but the drawback here is that you have to store the reference to the rigidbody so that will add a bit more complexity because we’re nulling it out in our code.
-
A simpler approach is to look at when the ball’s current position relative to the pivot is “opposite” its position at launch. By looking at logs I was able to convince myself that the ball is on the “opposite” side around the time when the forces flip so effectively these two approaches have identical results but this one is simpler to code up.
OK. But how do you know it’s "on the opposite side."
Simple. Capture the direction vector (ball to pivot) at launch and measure it again each frame. If the dot product between these vectors is <0 then its opposite.
A few important important explanations
- You only want to check the vectors if there is a ball to be detached, hence this new bool I called “checkForDetach”. Start with it false when the game starts or when ball spawns, set it to true after you launch the ball, then immediately set it to false once you’ve detached it to prevent unnecessary code from executing and/or null exceptions
- The check uses FixedUpdate instead of Update() because it runs just before the physics system runs and also before Update() is called. This way you have the highest likelihood of detaching the ball before the spring starts pulling on it in the wrong direction.
And here are the most relevant changes.
private bool checkForDetach;
private Vector2 ballToPivotVectorLaunch;
GameObject ballInstance;
private void LaunchBall()
{
ballToPivotVectorLaunch = pivot.transform.position - ballInstance.transform.position;
currentBallRigidBody.isKinematic = false;
currentBallRigidBody = null;
checkForDetach = true;
}
private void DetachBall()
{
currentBallSpringJoint.enabled = false;
currentBallSpringJoint = null;
checkForDetach = false;
}
private void FixedUpdate()
{
if (checkForDetach)
{
Vector2 ballToPivotVectorCurrent = pivot.transform.position - ballInstance.transform.position;
if (Vector2.Dot(ballToPivotVectorLaunch, ballToPivotVectorCurrent) < 0)
{
DetachBall();
}
}
}
In case anyone is interested in how to check the force on the spring here is the unity reference: