Hi all, here are my aproach to solve all deadlock.
First of all, I identify two deadlock conditions: zero speed on some axis, or changing speed on axis but a number of consecutive rebounds on unbreakable elements.
To solve this, the first step is to apply the tag “Unbreakable” in the 3 walls and in the paddle.
Then I add some attributes to the ball. One is to count how many consecutive bounces on unbreakable elements I accept, then a speed correction for those cases, and finally a speed correction for cases where an axis is “dead”.
[SerializeField] float maxUnbreakableCollisions = 15;
[SerializeField] float unbreakableDLRandom = 1f;
[SerializeField] float axisDLSpeedAdjust = 5;
Then, I accumulate the number of hits on unbreakable objects in a variable of type int:
//state variables
Vector2 paddleToBallVector;
bool hasStarted;
int deadCollisionsCount;
private void OnCollisionEnter2D(Collision2D collision)
{
float tweatPercentaje = UnityEngine.Random.Range(0.2f,0.2f);
if (hasStarted)
{
AudioClip clip = ballSounds[UnityEngine.Random.Range(0, ballSounds.Length)];
myAudioSource.PlayOneShot(clip);
if (collision.gameObject.tag == "Unbreakable")
{
deadCollisionsCount++;
}
else
{
deadCollisionsCount = 0;
}
AvoidDeadLock();
}
}
Finally, my strategy is to avoid locks, in a “smooth” way.
What is done is the following:

Obtain the magnitude of the velocity (this is the absolute value of the velocity vector, since this value I want it to remain invariant throughout the game

If the speed of one axis is less than an acceptable threshold, I correct the speed of that axis, and calculate the speed of the other axis so that the speed magnitude is maintained. Since that axis has zero speed, it won’t look unnatural if the ball bounces to one side or the other, sso the factor can be a +/ 5.0f correction, or something like that.

If I am in a condition of “many rebounds without breaking something” what I do is to “subtly” randomize the axis with the lower speed and, of course, preserve the total absolute value, adjusting the predominant axis.
private void AvoidDeadLock()
{
float xVel = myRigidBody2D.velocity.x;
float yVel = myRigidBody2D.velocity.y;
float initialMagnitude = myRigidBody2D.velocity.magnitude;
if (Mathf.Abs(xVel) <= 0.5f)
{
xVel = Random.Range(axisDLSpeedAdjust, axisDLSpeedAdjust);
yVel = Mathf.Sign(yVel) * Mathf.Sqrt(Mathf.Pow(initialMagnitude,2)Mathf.Pow(xVel,2));
Debug.Log("dead x");
}
else if (Mathf.Abs(yVel) <= 0.5f)
{
yVel = Random.Range(axisDLSpeedAdjust, axisDLSpeedAdjust);
xVel = Mathf.Sign(xVel) * Mathf.Sqrt(Mathf.Pow(initialMagnitude,2)Mathf.Pow(yVel,2));
Debug.Log("dead y");
}
else if (deadCollisionsCount >= maxUnbreakableCollisions)
{
Debug.Log("dead lock");
if (Mathf.Abs(xVel) > Mathf.Abs(yVel))
{
yVel = RandomizeAxisSpeed(yVel, initialMagnitude);
xVel = Mathf.Sign(xVel) * Mathf.Sqrt(Mathf.Pow(initialMagnitude, 2)  Mathf.Pow(yVel, 2));
}
else
{
xVel = RandomizeAxisSpeed(xVel, initialMagnitude);
yVel = Mathf.Sign(yVel) * Mathf.Sqrt(Mathf.Pow(initialMagnitude, 2)  Mathf.Pow(xVel, 2));
}
deadCollisionsCount = 0;
}
myRigidBody2D.velocity = new Vector2(xVel,yVel);
}
IMPORTANT: The method to randomize the speed on the axis that least affects the movement of the ball, takes into account the sign of that speed and preserves it. This is done so that the ball does not make unnatural bounces, which completely alter the direction it was leading before bouncing.
private float RandomizeAxisSpeed(float vel, float initialMagnitude)
{
Debug.Log(vel);
if (vel < 0)
{
vel = Mathf.Clamp(vel + Random.Range(unbreakableDLRandom, 0), initialMagnitude, 0);
}
else
{
vel = Mathf.Clamp(vel + Random.Range(0, unbreakableDLRandom), 0, initialMagnitude);
}
Debug.Log(vel);
return vel;
}
I have tested this on a blockless level and the result is as expected. The ball never goes into deadlock of any kind. When a certain bounce pattern begins to happen, the ball changes its bounce completely subtly and naturally, and this is invisible to the user.
Game for see this strategy in action: https://sharemygame.com/@guillef/blockbreakerwithmusic